diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Interpreter')
47 files changed, 12397 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp new file mode 100644 index 000000000000..c5971b52f837 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp @@ -0,0 +1,250 @@ +//===-- CommandAlias.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 "lldb/Interpreter/CommandAlias.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/ErrorHandling.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp, + llvm::StringRef options_args, + OptionArgVectorSP &option_arg_vector_sp) { + bool success = true; + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + if (options_args.size() < 1) + return true; + + Args args(options_args); + std::string options_string(options_args); + // TODO: Find a way to propagate errors in this CommandReturnObject up the + // stack. + CommandReturnObject result(false); + // Check to see if the command being aliased can take any command options. + Options *options = cmd_obj_sp->GetOptions(); + if (options) { + // See if any options were specified as part of the alias; if so, handle + // them appropriately. + ExecutionContext exe_ctx = + cmd_obj_sp->GetCommandInterpreter().GetExecutionContext(); + options->NotifyOptionParsingStarting(&exe_ctx); + + llvm::Expected<Args> args_or = + options->ParseAlias(args, option_arg_vector, options_string); + if (!args_or) { + result.AppendError(toString(args_or.takeError())); + result.AppendError("Unable to create requested alias.\n"); + return false; + } + args = std::move(*args_or); + options->VerifyPartialOptions(result); + if (!result.Succeeded() && + result.GetStatus() != lldb::eReturnStatusStarted) { + result.AppendError("Unable to create requested alias.\n"); + return false; + } + } + + if (!options_string.empty()) { + if (cmd_obj_sp->WantsRawCommandString()) + option_arg_vector->emplace_back(CommandInterpreter::g_argument, + -1, options_string); + else { + for (auto &entry : args.entries()) { + if (!entry.ref().empty()) + option_arg_vector->emplace_back(std::string(CommandInterpreter::g_argument), -1, + std::string(entry.ref())); + } + } + } + + return success; +} + +CommandAlias::CommandAlias(CommandInterpreter &interpreter, + lldb::CommandObjectSP cmd_sp, + llvm::StringRef options_args, llvm::StringRef name, + llvm::StringRef help, llvm::StringRef syntax, + uint32_t flags) + : CommandObject(interpreter, name, help, syntax, flags), + m_option_string(std::string(options_args)), + m_option_args_sp(new OptionArgVector), + m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false), + m_did_set_help_long(false) { + if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) { + m_underlying_command_sp = cmd_sp; + for (int i = 0; + auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i); + i++) { + m_arguments.push_back(*cmd_entry); + } + if (!help.empty()) { + StreamString sstr; + StreamString translation_and_help; + GetAliasExpansion(sstr); + + translation_and_help.Printf( + "(%s) %s", sstr.GetData(), + GetUnderlyingCommand()->GetHelp().str().c_str()); + SetHelp(translation_and_help.GetString()); + } + } +} + +bool CommandAlias::WantsRawCommandString() { + if (IsValid()) + return m_underlying_command_sp->WantsRawCommandString(); + return false; +} + +bool CommandAlias::WantsCompletion() { + if (IsValid()) + return m_underlying_command_sp->WantsCompletion(); + return false; +} + +void CommandAlias::HandleCompletion(CompletionRequest &request) { + if (IsValid()) + m_underlying_command_sp->HandleCompletion(request); +} + +void CommandAlias::HandleArgumentCompletion( + CompletionRequest &request, OptionElementVector &opt_element_vector) { + if (IsValid()) + m_underlying_command_sp->HandleArgumentCompletion(request, + opt_element_vector); +} + +Options *CommandAlias::GetOptions() { + if (IsValid()) + return m_underlying_command_sp->GetOptions(); + return nullptr; +} + +void CommandAlias::Execute(const char *args_string, + CommandReturnObject &result) { + llvm_unreachable("CommandAlias::Execute is not to be called"); +} + +void CommandAlias::GetAliasExpansion(StreamString &help_string) const { + llvm::StringRef command_name = m_underlying_command_sp->GetCommandName(); + help_string.Printf("'%*s", (int)command_name.size(), command_name.data()); + + if (!m_option_args_sp) { + help_string.Printf("'"); + return; + } + + OptionArgVector *options = m_option_args_sp.get(); + std::string opt; + std::string value; + + for (const auto &opt_entry : *options) { + std::tie(opt, std::ignore, value) = opt_entry; + if (opt == CommandInterpreter::g_argument) { + help_string.Printf(" %s", value.c_str()); + } else { + help_string.Printf(" %s", opt.c_str()); + if ((value != CommandInterpreter::g_no_argument) + && (value != CommandInterpreter::g_need_argument)) { + help_string.Printf(" %s", value.c_str()); + } + } + } + + help_string.Printf("'"); +} + +bool CommandAlias::IsDashDashCommand() { + if (m_is_dashdash_alias != eLazyBoolCalculate) + return (m_is_dashdash_alias == eLazyBoolYes); + m_is_dashdash_alias = eLazyBoolNo; + if (!IsValid()) + return false; + + std::string opt; + std::string value; + + for (const auto &opt_entry : *GetOptionArguments()) { + std::tie(opt, std::ignore, value) = opt_entry; + if (opt == CommandInterpreter::g_argument && !value.empty() && + llvm::StringRef(value).ends_with("--")) { + m_is_dashdash_alias = eLazyBoolYes; + break; + } + } + + // if this is a nested alias, it may be adding arguments on top of an already + // dash-dash alias + if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias()) + m_is_dashdash_alias = + (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes + : eLazyBoolNo); + return (m_is_dashdash_alias == eLazyBoolYes); +} + +bool CommandAlias::IsNestedAlias() { + if (GetUnderlyingCommand()) + return GetUnderlyingCommand()->IsAlias(); + return false; +} + +std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() { + auto underlying = GetUnderlyingCommand(); + if (!underlying) + return {nullptr, nullptr}; + + if (underlying->IsAlias()) { + // FIXME: This doesn't work if the original alias fills a slot in the + // underlying alias, since this just appends the two lists. + auto desugared = ((CommandAlias *)underlying.get())->Desugar(); + OptionArgVectorSP options = std::make_shared<OptionArgVector>(); + llvm::append_range(*options, *desugared.second); + llvm::append_range(*options, *GetOptionArguments()); + return {desugared.first, options}; + } + + return {underlying, GetOptionArguments()}; +} + +// allow CommandAlias objects to provide their own help, but fallback to the +// info for the underlying command if no customization has been provided +void CommandAlias::SetHelp(llvm::StringRef str) { + this->CommandObject::SetHelp(str); + m_did_set_help = true; +} + +void CommandAlias::SetHelpLong(llvm::StringRef str) { + this->CommandObject::SetHelpLong(str); + m_did_set_help_long = true; +} + +llvm::StringRef CommandAlias::GetHelp() { + if (!m_cmd_help_short.empty() || m_did_set_help) + return m_cmd_help_short; + if (IsValid()) + return m_underlying_command_sp->GetHelp(); + return llvm::StringRef(); +} + +llvm::StringRef CommandAlias::GetHelpLong() { + if (!m_cmd_help_long.empty() || m_did_set_help_long) + return m_cmd_help_long; + if (IsValid()) + return m_underlying_command_sp->GetHelpLong(); + return llvm::StringRef(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandHistory.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandHistory.cpp new file mode 100644 index 000000000000..f5dbcb675030 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/CommandHistory.cpp @@ -0,0 +1,106 @@ +//===-- CommandHistory.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 <cinttypes> +#include <optional> + +#include "lldb/Interpreter/CommandHistory.h" + +using namespace lldb; +using namespace lldb_private; + +size_t CommandHistory::GetSize() const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + return m_history.size(); +} + +bool CommandHistory::IsEmpty() const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + return m_history.empty(); +} + +std::optional<llvm::StringRef> +CommandHistory::FindString(llvm::StringRef input_str) const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (input_str.size() < 2) + return std::nullopt; + + if (input_str[0] != g_repeat_char) + return std::nullopt; + + if (input_str[1] == g_repeat_char) { + if (m_history.empty()) + return std::nullopt; + return llvm::StringRef(m_history.back()); + } + + input_str = input_str.drop_front(); + + size_t idx = 0; + if (input_str.front() == '-') { + if (input_str.drop_front(1).getAsInteger(0, idx)) + return std::nullopt; + if (idx >= m_history.size()) + return std::nullopt; + idx = m_history.size() - idx; + } else { + if (input_str.getAsInteger(0, idx)) + return std::nullopt; + if (idx >= m_history.size()) + return std::nullopt; + } + + return llvm::StringRef(m_history[idx]); +} + +llvm::StringRef CommandHistory::GetStringAtIndex(size_t idx) const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (idx < m_history.size()) + return m_history[idx]; + return ""; +} + +llvm::StringRef CommandHistory::operator[](size_t idx) const { + return GetStringAtIndex(idx); +} + +llvm::StringRef CommandHistory::GetRecentmostString() const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (m_history.empty()) + return ""; + return m_history.back(); +} + +void CommandHistory::AppendString(llvm::StringRef str, bool reject_if_dupe) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (reject_if_dupe) { + if (!m_history.empty()) { + if (str == m_history.back()) + return; + } + } + m_history.push_back(std::string(str)); +} + +void CommandHistory::Clear() { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + m_history.clear(); +} + +void CommandHistory::Dump(Stream &stream, size_t start_idx, + size_t stop_idx) const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + stop_idx = std::min(stop_idx + 1, m_history.size()); + for (size_t counter = start_idx; counter < stop_idx; counter++) { + const std::string hist_item = m_history[counter]; + if (!hist_item.empty()) { + stream.Indent(); + stream.Printf("%4" PRIu64 ": %s\n", (uint64_t)counter, hist_item.c_str()); + } + } +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp new file mode 100644 index 000000000000..fc07168b6c0a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp @@ -0,0 +1,3583 @@ +//===-- CommandInterpreter.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 <chrono> +#include <cstdlib> +#include <limits> +#include <memory> +#include <optional> +#include <string> +#include <vector> + +#include "Commands/CommandObjectApropos.h" +#include "Commands/CommandObjectBreakpoint.h" +#include "Commands/CommandObjectCommands.h" +#include "Commands/CommandObjectDWIMPrint.h" +#include "Commands/CommandObjectDiagnostics.h" +#include "Commands/CommandObjectDisassemble.h" +#include "Commands/CommandObjectExpression.h" +#include "Commands/CommandObjectFrame.h" +#include "Commands/CommandObjectGUI.h" +#include "Commands/CommandObjectHelp.h" +#include "Commands/CommandObjectLanguage.h" +#include "Commands/CommandObjectLog.h" +#include "Commands/CommandObjectMemory.h" +#include "Commands/CommandObjectPlatform.h" +#include "Commands/CommandObjectPlugin.h" +#include "Commands/CommandObjectProcess.h" +#include "Commands/CommandObjectQuit.h" +#include "Commands/CommandObjectRegexCommand.h" +#include "Commands/CommandObjectRegister.h" +#include "Commands/CommandObjectScripting.h" +#include "Commands/CommandObjectSession.h" +#include "Commands/CommandObjectSettings.h" +#include "Commands/CommandObjectSource.h" +#include "Commands/CommandObjectStats.h" +#include "Commands/CommandObjectTarget.h" +#include "Commands/CommandObjectThread.h" +#include "Commands/CommandObjectTrace.h" +#include "Commands/CommandObjectType.h" +#include "Commands/CommandObjectVersion.h" +#include "Commands/CommandObjectWatchpoint.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/StreamFile.h" +#include "lldb/Utility/ErrorMessages.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/Timer.h" + +#include "lldb/Host/Config.h" +#if LLDB_ENABLE_LIBEDIT +#include "lldb/Host/Editline.h" +#endif +#include "lldb/Host/File.h" +#include "lldb/Host/FileCache.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" + +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Utility/Args.h" + +#include "lldb/Target/Language.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/UnixSignals.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/ScopedPrinter.h" + +#if defined(__APPLE__) +#include <TargetConditionals.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +static const char *k_white_space = " \t\v"; + +static constexpr const char *InitFileWarning = + "There is a .lldbinit file in the current directory which is not being " + "read.\n" + "To silence this warning without sourcing in the local .lldbinit,\n" + "add the following to the lldbinit file in your home directory:\n" + " settings set target.load-cwd-lldbinit false\n" + "To allow lldb to source .lldbinit files in the current working " + "directory,\n" + "set the value of this variable to true. Only do so if you understand " + "and\n" + "accept the security risk."; + +const char *CommandInterpreter::g_no_argument = "<no-argument>"; +const char *CommandInterpreter::g_need_argument = "<need-argument>"; +const char *CommandInterpreter::g_argument = "<argument>"; + + +#define LLDB_PROPERTIES_interpreter +#include "InterpreterProperties.inc" + +enum { +#define LLDB_PROPERTIES_interpreter +#include "InterpreterPropertiesEnum.inc" +}; + +llvm::StringRef CommandInterpreter::GetStaticBroadcasterClass() { + static constexpr llvm::StringLiteral class_name("lldb.commandInterpreter"); + return class_name; +} + +CommandInterpreter::CommandInterpreter(Debugger &debugger, + bool synchronous_execution) + : Broadcaster(debugger.GetBroadcasterManager(), + CommandInterpreter::GetStaticBroadcasterClass().str()), + Properties( + OptionValuePropertiesSP(new OptionValueProperties("interpreter"))), + IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand), + m_debugger(debugger), m_synchronous_execution(true), + m_skip_lldbinit_files(false), m_skip_app_init_files(false), + m_comment_char('#'), m_batch_command_mode(false), + m_truncation_warning(eNoOmission), m_max_depth_warning(eNoOmission), + m_command_source_depth(0) { + SetEventName(eBroadcastBitThreadShouldExit, "thread-should-exit"); + SetEventName(eBroadcastBitResetPrompt, "reset-prompt"); + SetEventName(eBroadcastBitQuitCommandReceived, "quit"); + SetSynchronous(synchronous_execution); + CheckInWithManager(); + m_collection_sp->Initialize(g_interpreter_properties); +} + +bool CommandInterpreter::GetExpandRegexAliases() const { + const uint32_t idx = ePropertyExpandRegexAliases; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +bool CommandInterpreter::GetPromptOnQuit() const { + const uint32_t idx = ePropertyPromptOnQuit; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +void CommandInterpreter::SetPromptOnQuit(bool enable) { + const uint32_t idx = ePropertyPromptOnQuit; + SetPropertyAtIndex(idx, enable); +} + +bool CommandInterpreter::GetSaveTranscript() const { + const uint32_t idx = ePropertySaveTranscript; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +void CommandInterpreter::SetSaveTranscript(bool enable) { + const uint32_t idx = ePropertySaveTranscript; + SetPropertyAtIndex(idx, enable); +} + +bool CommandInterpreter::GetSaveSessionOnQuit() const { + const uint32_t idx = ePropertySaveSessionOnQuit; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +void CommandInterpreter::SetSaveSessionOnQuit(bool enable) { + const uint32_t idx = ePropertySaveSessionOnQuit; + SetPropertyAtIndex(idx, enable); +} + +bool CommandInterpreter::GetOpenTranscriptInEditor() const { + const uint32_t idx = ePropertyOpenTranscriptInEditor; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +void CommandInterpreter::SetOpenTranscriptInEditor(bool enable) { + const uint32_t idx = ePropertyOpenTranscriptInEditor; + SetPropertyAtIndex(idx, enable); +} + +FileSpec CommandInterpreter::GetSaveSessionDirectory() const { + const uint32_t idx = ePropertySaveSessionDirectory; + return GetPropertyAtIndexAs<FileSpec>(idx, {}); +} + +void CommandInterpreter::SetSaveSessionDirectory(llvm::StringRef path) { + const uint32_t idx = ePropertySaveSessionDirectory; + SetPropertyAtIndex(idx, path); +} + +bool CommandInterpreter::GetEchoCommands() const { + const uint32_t idx = ePropertyEchoCommands; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +void CommandInterpreter::SetEchoCommands(bool enable) { + const uint32_t idx = ePropertyEchoCommands; + SetPropertyAtIndex(idx, enable); +} + +bool CommandInterpreter::GetEchoCommentCommands() const { + const uint32_t idx = ePropertyEchoCommentCommands; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +void CommandInterpreter::SetEchoCommentCommands(bool enable) { + const uint32_t idx = ePropertyEchoCommentCommands; + SetPropertyAtIndex(idx, enable); +} + +void CommandInterpreter::AllowExitCodeOnQuit(bool allow) { + m_allow_exit_code = allow; + if (!allow) + m_quit_exit_code.reset(); +} + +bool CommandInterpreter::SetQuitExitCode(int exit_code) { + if (!m_allow_exit_code) + return false; + m_quit_exit_code = exit_code; + return true; +} + +int CommandInterpreter::GetQuitExitCode(bool &exited) const { + exited = m_quit_exit_code.has_value(); + if (exited) + return *m_quit_exit_code; + return 0; +} + +void CommandInterpreter::ResolveCommand(const char *command_line, + CommandReturnObject &result) { + std::string command = command_line; + if (ResolveCommandImpl(command, result) != nullptr) { + result.AppendMessageWithFormat("%s", command.c_str()); + result.SetStatus(eReturnStatusSuccessFinishResult); + } +} + +bool CommandInterpreter::GetStopCmdSourceOnError() const { + const uint32_t idx = ePropertyStopCmdSourceOnError; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +bool CommandInterpreter::GetSpaceReplPrompts() const { + const uint32_t idx = ePropertySpaceReplPrompts; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +bool CommandInterpreter::GetRepeatPreviousCommand() const { + const uint32_t idx = ePropertyRepeatPreviousCommand; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +bool CommandInterpreter::GetRequireCommandOverwrite() const { + const uint32_t idx = ePropertyRequireCommandOverwrite; + return GetPropertyAtIndexAs<bool>( + idx, g_interpreter_properties[idx].default_uint_value != 0); +} + +void CommandInterpreter::Initialize() { + LLDB_SCOPED_TIMER(); + + CommandReturnObject result(m_debugger.GetUseColor()); + + LoadCommandDictionary(); + + // An alias arguments vector to reuse - reset it before use... + OptionArgVectorSP alias_arguments_vector_sp(new OptionArgVector); + + // Set up some initial aliases. + CommandObjectSP cmd_obj_sp = GetCommandSPExact("quit"); + if (cmd_obj_sp) { + AddAlias("q", cmd_obj_sp); + AddAlias("exit", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("_regexp-attach"); + if (cmd_obj_sp) + AddAlias("attach", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("process detach"); + if (cmd_obj_sp) { + AddAlias("detach", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("process continue"); + if (cmd_obj_sp) { + AddAlias("c", cmd_obj_sp); + AddAlias("continue", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("_regexp-break"); + if (cmd_obj_sp) + AddAlias("b", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("_regexp-tbreak"); + if (cmd_obj_sp) + AddAlias("tbreak", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("thread step-inst"); + if (cmd_obj_sp) { + AddAlias("stepi", cmd_obj_sp); + AddAlias("si", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("thread step-inst-over"); + if (cmd_obj_sp) { + AddAlias("nexti", cmd_obj_sp); + AddAlias("ni", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("thread step-in"); + if (cmd_obj_sp) { + AddAlias("s", cmd_obj_sp); + AddAlias("step", cmd_obj_sp); + CommandAlias *sif_alias = AddAlias( + "sif", cmd_obj_sp, "--end-linenumber block --step-in-target %1"); + if (sif_alias) { + sif_alias->SetHelp("Step through the current block, stopping if you step " + "directly into a function whose name matches the " + "TargetFunctionName."); + sif_alias->SetSyntax("sif <TargetFunctionName>"); + } + } + + cmd_obj_sp = GetCommandSPExact("thread step-over"); + if (cmd_obj_sp) { + AddAlias("n", cmd_obj_sp); + AddAlias("next", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("thread step-out"); + if (cmd_obj_sp) { + AddAlias("finish", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("frame select"); + if (cmd_obj_sp) { + AddAlias("f", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("thread select"); + if (cmd_obj_sp) { + AddAlias("t", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("_regexp-jump"); + if (cmd_obj_sp) { + AddAlias("j", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + AddAlias("jump", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + } + + cmd_obj_sp = GetCommandSPExact("_regexp-list"); + if (cmd_obj_sp) { + AddAlias("l", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + AddAlias("list", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + } + + cmd_obj_sp = GetCommandSPExact("_regexp-env"); + if (cmd_obj_sp) + AddAlias("env", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("memory read"); + if (cmd_obj_sp) + AddAlias("x", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact("_regexp-up"); + if (cmd_obj_sp) + AddAlias("up", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("_regexp-down"); + if (cmd_obj_sp) + AddAlias("down", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("_regexp-display"); + if (cmd_obj_sp) + AddAlias("display", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("disassemble"); + if (cmd_obj_sp) + AddAlias("dis", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact("disassemble"); + if (cmd_obj_sp) + AddAlias("di", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact("_regexp-undisplay"); + if (cmd_obj_sp) + AddAlias("undisplay", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("_regexp-bt"); + if (cmd_obj_sp) + AddAlias("bt", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); + + cmd_obj_sp = GetCommandSPExact("target create"); + if (cmd_obj_sp) + AddAlias("file", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact("target modules"); + if (cmd_obj_sp) + AddAlias("image", cmd_obj_sp); + + alias_arguments_vector_sp = std::make_shared<OptionArgVector>(); + + cmd_obj_sp = GetCommandSPExact("dwim-print"); + if (cmd_obj_sp) { + AddAlias("p", cmd_obj_sp, "--")->SetHelpLong(""); + AddAlias("print", cmd_obj_sp, "--")->SetHelpLong(""); + if (auto *po = AddAlias("po", cmd_obj_sp, "-O --")) { + po->SetHelp("Evaluate an expression on the current thread. Displays any " + "returned value with formatting " + "controlled by the type's author."); + po->SetHelpLong(""); + } + } + + cmd_obj_sp = GetCommandSPExact("expression"); + if (cmd_obj_sp) { + AddAlias("call", cmd_obj_sp, "--")->SetHelpLong(""); + CommandAlias *parray_alias = + AddAlias("parray", cmd_obj_sp, "--element-count %1 --"); + if (parray_alias) { + parray_alias->SetHelp + ("parray <COUNT> <EXPRESSION> -- lldb will evaluate EXPRESSION " + "to get a typed-pointer-to-an-array in memory, and will display " + "COUNT elements of that type from the array."); + parray_alias->SetHelpLong(""); + } + CommandAlias *poarray_alias = AddAlias("poarray", cmd_obj_sp, + "--object-description --element-count %1 --"); + if (poarray_alias) { + poarray_alias->SetHelp("poarray <COUNT> <EXPRESSION> -- lldb will " + "evaluate EXPRESSION to get the address of an array of COUNT " + "objects in memory, and will call po on them."); + poarray_alias->SetHelpLong(""); + } + } + + cmd_obj_sp = GetCommandSPExact("platform shell"); + if (cmd_obj_sp) { + CommandAlias *shell_alias = AddAlias("shell", cmd_obj_sp, " --host --"); + if (shell_alias) { + shell_alias->SetHelp("Run a shell command on the host."); + shell_alias->SetHelpLong(""); + shell_alias->SetSyntax("shell <shell-command>"); + } + } + + cmd_obj_sp = GetCommandSPExact("process kill"); + if (cmd_obj_sp) { + AddAlias("kill", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("process launch"); + if (cmd_obj_sp) { + alias_arguments_vector_sp = std::make_shared<OptionArgVector>(); +#if defined(__APPLE__) +#if TARGET_OS_IPHONE + AddAlias("r", cmd_obj_sp, "--"); + AddAlias("run", cmd_obj_sp, "--"); +#else + AddAlias("r", cmd_obj_sp, "--shell-expand-args true --"); + AddAlias("run", cmd_obj_sp, "--shell-expand-args true --"); +#endif +#else + StreamString defaultshell; + defaultshell.Printf("--shell=%s --", + HostInfo::GetDefaultShell().GetPath().c_str()); + AddAlias("r", cmd_obj_sp, defaultshell.GetString()); + AddAlias("run", cmd_obj_sp, defaultshell.GetString()); +#endif + } + + cmd_obj_sp = GetCommandSPExact("target symbols add"); + if (cmd_obj_sp) { + AddAlias("add-dsym", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("breakpoint set"); + if (cmd_obj_sp) { + AddAlias("rbreak", cmd_obj_sp, "--func-regex %1"); + } + + cmd_obj_sp = GetCommandSPExact("frame variable"); + if (cmd_obj_sp) { + AddAlias("v", cmd_obj_sp); + AddAlias("var", cmd_obj_sp); + AddAlias("vo", cmd_obj_sp, "--object-description"); + } + + cmd_obj_sp = GetCommandSPExact("register"); + if (cmd_obj_sp) { + AddAlias("re", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("scripting run"); + if (cmd_obj_sp) { + AddAlias("sc", cmd_obj_sp); + AddAlias("scr", cmd_obj_sp); + AddAlias("scri", cmd_obj_sp); + AddAlias("scrip", cmd_obj_sp); + AddAlias("script", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("session history"); + if (cmd_obj_sp) { + AddAlias("history", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact("help"); + if (cmd_obj_sp) { + AddAlias("h", cmd_obj_sp); + } +} + +void CommandInterpreter::Clear() { + m_command_io_handler_sp.reset(); +} + +const char *CommandInterpreter::ProcessEmbeddedScriptCommands(const char *arg) { + // This function has not yet been implemented. + + // Look for any embedded script command + // If found, + // get interpreter object from the command dictionary, + // call execute_one_command on it, + // get the results as a string, + // substitute that string for current stuff. + + return arg; +} + +#define REGISTER_COMMAND_OBJECT(NAME, CLASS) \ + m_command_dict[NAME] = std::make_shared<CLASS>(*this); + +void CommandInterpreter::LoadCommandDictionary() { + LLDB_SCOPED_TIMER(); + + REGISTER_COMMAND_OBJECT("apropos", CommandObjectApropos); + REGISTER_COMMAND_OBJECT("breakpoint", CommandObjectMultiwordBreakpoint); + REGISTER_COMMAND_OBJECT("command", CommandObjectMultiwordCommands); + REGISTER_COMMAND_OBJECT("diagnostics", CommandObjectDiagnostics); + REGISTER_COMMAND_OBJECT("disassemble", CommandObjectDisassemble); + REGISTER_COMMAND_OBJECT("dwim-print", CommandObjectDWIMPrint); + REGISTER_COMMAND_OBJECT("expression", CommandObjectExpression); + REGISTER_COMMAND_OBJECT("frame", CommandObjectMultiwordFrame); + REGISTER_COMMAND_OBJECT("gui", CommandObjectGUI); + REGISTER_COMMAND_OBJECT("help", CommandObjectHelp); + REGISTER_COMMAND_OBJECT("log", CommandObjectLog); + REGISTER_COMMAND_OBJECT("memory", CommandObjectMemory); + REGISTER_COMMAND_OBJECT("platform", CommandObjectPlatform); + REGISTER_COMMAND_OBJECT("plugin", CommandObjectPlugin); + REGISTER_COMMAND_OBJECT("process", CommandObjectMultiwordProcess); + REGISTER_COMMAND_OBJECT("quit", CommandObjectQuit); + REGISTER_COMMAND_OBJECT("register", CommandObjectRegister); + REGISTER_COMMAND_OBJECT("scripting", CommandObjectMultiwordScripting); + REGISTER_COMMAND_OBJECT("settings", CommandObjectMultiwordSettings); + REGISTER_COMMAND_OBJECT("session", CommandObjectSession); + REGISTER_COMMAND_OBJECT("source", CommandObjectMultiwordSource); + REGISTER_COMMAND_OBJECT("statistics", CommandObjectStats); + REGISTER_COMMAND_OBJECT("target", CommandObjectMultiwordTarget); + REGISTER_COMMAND_OBJECT("thread", CommandObjectMultiwordThread); + REGISTER_COMMAND_OBJECT("trace", CommandObjectTrace); + REGISTER_COMMAND_OBJECT("type", CommandObjectType); + REGISTER_COMMAND_OBJECT("version", CommandObjectVersion); + REGISTER_COMMAND_OBJECT("watchpoint", CommandObjectMultiwordWatchpoint); + REGISTER_COMMAND_OBJECT("language", CommandObjectLanguage); + + // clang-format off + const char *break_regexes[][2] = { + {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", + "breakpoint set --file '%1' --line %2 --column %3"}, + {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", + "breakpoint set --file '%1' --line %2"}, + {"^/([^/]+)/$", "breakpoint set --source-pattern-regexp '%1'"}, + {"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"}, + {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"}, + {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$", + "breakpoint set --name '%1'"}, + {"^(-.*)$", "breakpoint set %1"}, + {"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$", + "breakpoint set --name '%2' --shlib '%1'"}, + {"^\\&(.*[^[:space:]])[[:space:]]*$", + "breakpoint set --name '%1' --skip-prologue=0"}, + {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$", + "breakpoint set --name '%1'"}}; + // clang-format on + + size_t num_regexes = std::size(break_regexes); + + std::unique_ptr<CommandObjectRegexCommand> break_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-break", + "Set a breakpoint using one of several shorthand formats.", + "\n" + "_regexp-break <filename>:<linenum>:<colnum>\n" + " main.c:12:21 // Break at line 12 and column " + "21 of main.c\n\n" + "_regexp-break <filename>:<linenum>\n" + " main.c:12 // Break at line 12 of " + "main.c\n\n" + "_regexp-break <linenum>\n" + " 12 // Break at line 12 of current " + "file\n\n" + "_regexp-break 0x<address>\n" + " 0x1234000 // Break at address " + "0x1234000\n\n" + "_regexp-break <name>\n" + " main // Break in 'main' after the " + "prologue\n\n" + "_regexp-break &<name>\n" + " &main // Break at first instruction " + "in 'main'\n\n" + "_regexp-break <module>`<name>\n" + " libc.so`malloc // Break in 'malloc' from " + "'libc.so'\n\n" + "_regexp-break /<source-regex>/\n" + " /break here/ // Break on source lines in " + "current file\n" + " // containing text 'break " + "here'.\n", + lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false)); + + if (break_regex_cmd_up) { + bool success = true; + for (size_t i = 0; i < num_regexes; i++) { + success = break_regex_cmd_up->AddRegexCommand(break_regexes[i][0], + break_regexes[i][1]); + if (!success) + break; + } + success = + break_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full"); + + if (success) { + CommandObjectSP break_regex_cmd_sp(break_regex_cmd_up.release()); + m_command_dict[std::string(break_regex_cmd_sp->GetCommandName())] = + break_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> tbreak_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-tbreak", + "Set a one-shot breakpoint using one of several shorthand formats.", + "\n" + "_regexp-break <filename>:<linenum>:<colnum>\n" + " main.c:12:21 // Break at line 12 and column " + "21 of main.c\n\n" + "_regexp-break <filename>:<linenum>\n" + " main.c:12 // Break at line 12 of " + "main.c\n\n" + "_regexp-break <linenum>\n" + " 12 // Break at line 12 of current " + "file\n\n" + "_regexp-break 0x<address>\n" + " 0x1234000 // Break at address " + "0x1234000\n\n" + "_regexp-break <name>\n" + " main // Break in 'main' after the " + "prologue\n\n" + "_regexp-break &<name>\n" + " &main // Break at first instruction " + "in 'main'\n\n" + "_regexp-break <module>`<name>\n" + " libc.so`malloc // Break in 'malloc' from " + "'libc.so'\n\n" + "_regexp-break /<source-regex>/\n" + " /break here/ // Break on source lines in " + "current file\n" + " // containing text 'break " + "here'.\n", + lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false)); + + if (tbreak_regex_cmd_up) { + bool success = true; + for (size_t i = 0; i < num_regexes; i++) { + std::string command = break_regexes[i][1]; + command += " -o 1"; + success = + tbreak_regex_cmd_up->AddRegexCommand(break_regexes[i][0], command); + if (!success) + break; + } + success = + tbreak_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full"); + + if (success) { + CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_up.release()); + m_command_dict[std::string(tbreak_regex_cmd_sp->GetCommandName())] = + tbreak_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> attach_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-attach", "Attach to process by ID or name.", + "_regexp-attach <pid> | <process-name>", 0, false)); + if (attach_regex_cmd_up) { + if (attach_regex_cmd_up->AddRegexCommand("^([0-9]+)[[:space:]]*$", + "process attach --pid %1") && + attach_regex_cmd_up->AddRegexCommand( + "^(-.*|.* -.*)$", "process attach %1") && // Any options that are + // specified get passed to + // 'process attach' + attach_regex_cmd_up->AddRegexCommand("^(.+)$", + "process attach --name '%1'") && + attach_regex_cmd_up->AddRegexCommand("^$", "process attach")) { + CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_up.release()); + m_command_dict[std::string(attach_regex_cmd_sp->GetCommandName())] = + attach_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> down_regex_cmd_up( + new CommandObjectRegexCommand(*this, "_regexp-down", + "Select a newer stack frame. Defaults to " + "moving one frame, a numeric argument can " + "specify an arbitrary number.", + "_regexp-down [<count>]", 0, false)); + if (down_regex_cmd_up) { + if (down_regex_cmd_up->AddRegexCommand("^$", "frame select -r -1") && + down_regex_cmd_up->AddRegexCommand("^([0-9]+)$", + "frame select -r -%1")) { + CommandObjectSP down_regex_cmd_sp(down_regex_cmd_up.release()); + m_command_dict[std::string(down_regex_cmd_sp->GetCommandName())] = + down_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> up_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-up", + "Select an older stack frame. Defaults to moving one " + "frame, a numeric argument can specify an arbitrary number.", + "_regexp-up [<count>]", 0, false)); + if (up_regex_cmd_up) { + if (up_regex_cmd_up->AddRegexCommand("^$", "frame select -r 1") && + up_regex_cmd_up->AddRegexCommand("^([0-9]+)$", "frame select -r %1")) { + CommandObjectSP up_regex_cmd_sp(up_regex_cmd_up.release()); + m_command_dict[std::string(up_regex_cmd_sp->GetCommandName())] = + up_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> display_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-display", + "Evaluate an expression at every stop (see 'help target stop-hook'.)", + "_regexp-display expression", 0, false)); + if (display_regex_cmd_up) { + if (display_regex_cmd_up->AddRegexCommand( + "^(.+)$", "target stop-hook add -o \"expr -- %1\"")) { + CommandObjectSP display_regex_cmd_sp(display_regex_cmd_up.release()); + m_command_dict[std::string(display_regex_cmd_sp->GetCommandName())] = + display_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> undisplay_regex_cmd_up( + new CommandObjectRegexCommand(*this, "_regexp-undisplay", + "Stop displaying expression at every " + "stop (specified by stop-hook index.)", + "_regexp-undisplay stop-hook-number", 0, + false)); + if (undisplay_regex_cmd_up) { + if (undisplay_regex_cmd_up->AddRegexCommand("^([0-9]+)$", + "target stop-hook delete %1")) { + CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_up.release()); + m_command_dict[std::string(undisplay_regex_cmd_sp->GetCommandName())] = + undisplay_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> connect_gdb_remote_cmd_up( + new CommandObjectRegexCommand( + *this, "gdb-remote", + "Connect to a process via remote GDB server.\n" + "If no host is specifed, localhost is assumed.\n" + "gdb-remote is an abbreviation for 'process connect --plugin " + "gdb-remote connect://<hostname>:<port>'\n", + "gdb-remote [<hostname>:]<portnum>", 0, false)); + if (connect_gdb_remote_cmd_up) { + if (connect_gdb_remote_cmd_up->AddRegexCommand( + "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$", + "process connect --plugin gdb-remote connect://%1:%2") && + connect_gdb_remote_cmd_up->AddRegexCommand( + "^([[:digit:]]+)$", + "process connect --plugin gdb-remote connect://localhost:%1")) { + CommandObjectSP command_sp(connect_gdb_remote_cmd_up.release()); + m_command_dict[std::string(command_sp->GetCommandName())] = command_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> connect_kdp_remote_cmd_up( + new CommandObjectRegexCommand( + *this, "kdp-remote", + "Connect to a process via remote KDP server.\n" + "If no UDP port is specified, port 41139 is assumed.\n" + "kdp-remote is an abbreviation for 'process connect --plugin " + "kdp-remote udp://<hostname>:<port>'\n", + "kdp-remote <hostname>[:<portnum>]", 0, false)); + if (connect_kdp_remote_cmd_up) { + if (connect_kdp_remote_cmd_up->AddRegexCommand( + "^([^:]+:[[:digit:]]+)$", + "process connect --plugin kdp-remote udp://%1") && + connect_kdp_remote_cmd_up->AddRegexCommand( + "^(.+)$", "process connect --plugin kdp-remote udp://%1:41139")) { + CommandObjectSP command_sp(connect_kdp_remote_cmd_up.release()); + m_command_dict[std::string(command_sp->GetCommandName())] = command_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> bt_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-bt", + "Show backtrace of the current thread's call stack. Any numeric " + "argument displays at most that many frames. The argument 'all' " + "displays all threads. Use 'settings set frame-format' to customize " + "the printing of individual frames and 'settings set thread-format' " + "to customize the thread header.", + "bt [<digit> | all]", 0, false)); + if (bt_regex_cmd_up) { + // accept but don't document "bt -c <number>" -- before bt was a regex + // command if you wanted to backtrace three frames you would do "bt -c 3" + // but the intention is to have this emulate the gdb "bt" command and so + // now "bt 3" is the preferred form, in line with gdb. + if (bt_regex_cmd_up->AddRegexCommand("^([[:digit:]]+)[[:space:]]*$", + "thread backtrace -c %1") && + bt_regex_cmd_up->AddRegexCommand("^-c ([[:digit:]]+)[[:space:]]*$", + "thread backtrace -c %1") && + bt_regex_cmd_up->AddRegexCommand("^all[[:space:]]*$", "thread backtrace all") && + bt_regex_cmd_up->AddRegexCommand("^[[:space:]]*$", "thread backtrace")) { + CommandObjectSP command_sp(bt_regex_cmd_up.release()); + m_command_dict[std::string(command_sp->GetCommandName())] = command_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> list_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-list", + "List relevant source code using one of several shorthand formats.", + "\n" + "_regexp-list <file>:<line> // List around specific file/line\n" + "_regexp-list <line> // List current file around specified " + "line\n" + "_regexp-list <function-name> // List specified function\n" + "_regexp-list 0x<address> // List around specified address\n" + "_regexp-list -[<count>] // List previous <count> lines\n" + "_regexp-list // List subsequent lines", + lldb::eSourceFileCompletion, false)); + if (list_regex_cmd_up) { + if (list_regex_cmd_up->AddRegexCommand("^([0-9]+)[[:space:]]*$", + "source list --line %1") && + list_regex_cmd_up->AddRegexCommand( + "^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]" + "]*$", + "source list --file '%1' --line %2") && + list_regex_cmd_up->AddRegexCommand( + "^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", + "source list --address %1") && + list_regex_cmd_up->AddRegexCommand("^-[[:space:]]*$", + "source list --reverse") && + list_regex_cmd_up->AddRegexCommand( + "^-([[:digit:]]+)[[:space:]]*$", + "source list --reverse --count %1") && + list_regex_cmd_up->AddRegexCommand("^(.+)$", + "source list --name \"%1\"") && + list_regex_cmd_up->AddRegexCommand("^$", "source list")) { + CommandObjectSP list_regex_cmd_sp(list_regex_cmd_up.release()); + m_command_dict[std::string(list_regex_cmd_sp->GetCommandName())] = + list_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> env_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-env", + "Shorthand for viewing and setting environment variables.", + "\n" + "_regexp-env // Show environment\n" + "_regexp-env <name>=<value> // Set an environment variable", + 0, false)); + if (env_regex_cmd_up) { + if (env_regex_cmd_up->AddRegexCommand("^$", + "settings show target.env-vars") && + env_regex_cmd_up->AddRegexCommand("^([A-Za-z_][A-Za-z_0-9]*=.*)$", + "settings set target.env-vars %1")) { + CommandObjectSP env_regex_cmd_sp(env_regex_cmd_up.release()); + m_command_dict[std::string(env_regex_cmd_sp->GetCommandName())] = + env_regex_cmd_sp; + } + } + + std::unique_ptr<CommandObjectRegexCommand> jump_regex_cmd_up( + new CommandObjectRegexCommand( + *this, "_regexp-jump", "Set the program counter to a new address.", + "\n" + "_regexp-jump <line>\n" + "_regexp-jump +<line-offset> | -<line-offset>\n" + "_regexp-jump <file>:<line>\n" + "_regexp-jump *<addr>\n", + 0, false)); + if (jump_regex_cmd_up) { + if (jump_regex_cmd_up->AddRegexCommand("^\\*(.*)$", + "thread jump --addr %1") && + jump_regex_cmd_up->AddRegexCommand("^([0-9]+)$", + "thread jump --line %1") && + jump_regex_cmd_up->AddRegexCommand("^([^:]+):([0-9]+)$", + "thread jump --file %1 --line %2") && + jump_regex_cmd_up->AddRegexCommand("^([+\\-][0-9]+)$", + "thread jump --by %1")) { + CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_up.release()); + m_command_dict[std::string(jump_regex_cmd_sp->GetCommandName())] = + jump_regex_cmd_sp; + } + } +} + +int CommandInterpreter::GetCommandNamesMatchingPartialString( + const char *cmd_str, bool include_aliases, StringList &matches, + StringList &descriptions) { + AddNamesMatchingPartialString(m_command_dict, cmd_str, matches, + &descriptions); + + if (include_aliases) { + AddNamesMatchingPartialString(m_alias_dict, cmd_str, matches, + &descriptions); + } + + return matches.GetSize(); +} + +CommandObjectMultiword *CommandInterpreter::VerifyUserMultiwordCmdPath( + Args &path, bool leaf_is_command, Status &result) { + result.Clear(); + + auto get_multi_or_report_error = + [&result](CommandObjectSP cmd_sp, + const char *name) -> CommandObjectMultiword * { + if (!cmd_sp) { + result.SetErrorStringWithFormat("Path component: '%s' not found", name); + return nullptr; + } + if (!cmd_sp->IsUserCommand()) { + result.SetErrorStringWithFormat("Path component: '%s' is not a user " + "command", + name); + return nullptr; + } + CommandObjectMultiword *cmd_as_multi = cmd_sp->GetAsMultiwordCommand(); + if (!cmd_as_multi) { + result.SetErrorStringWithFormat("Path component: '%s' is not a container " + "command", + name); + return nullptr; + } + return cmd_as_multi; + }; + + size_t num_args = path.GetArgumentCount(); + if (num_args == 0) { + result.SetErrorString("empty command path"); + return nullptr; + } + + if (num_args == 1 && leaf_is_command) { + // We just got a leaf command to be added to the root. That's not an error, + // just return null for the container. + return nullptr; + } + + // Start by getting the root command from the interpreter. + const char *cur_name = path.GetArgumentAtIndex(0); + CommandObjectSP cur_cmd_sp = GetCommandSPExact(cur_name); + CommandObjectMultiword *cur_as_multi = + get_multi_or_report_error(cur_cmd_sp, cur_name); + if (cur_as_multi == nullptr) + return nullptr; + + size_t num_path_elements = num_args - (leaf_is_command ? 1 : 0); + for (size_t cursor = 1; cursor < num_path_elements && cur_as_multi != nullptr; + cursor++) { + cur_name = path.GetArgumentAtIndex(cursor); + cur_cmd_sp = cur_as_multi->GetSubcommandSPExact(cur_name); + cur_as_multi = get_multi_or_report_error(cur_cmd_sp, cur_name); + } + return cur_as_multi; +} + +CommandObjectSP +CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, + bool exact, StringList *matches, + StringList *descriptions) const { + CommandObjectSP command_sp; + + std::string cmd = std::string(cmd_str); + + if (HasCommands()) { + auto pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + command_sp = pos->second; + } + + if (include_aliases && HasAliases()) { + auto alias_pos = m_alias_dict.find(cmd); + if (alias_pos != m_alias_dict.end()) + command_sp = alias_pos->second; + } + + if (HasUserCommands()) { + auto pos = m_user_dict.find(cmd); + if (pos != m_user_dict.end()) + command_sp = pos->second; + } + + if (HasUserMultiwordCommands()) { + auto pos = m_user_mw_dict.find(cmd); + if (pos != m_user_mw_dict.end()) + command_sp = pos->second; + } + + if (!exact && !command_sp) { + // We will only get into here if we didn't find any exact matches. + + CommandObjectSP user_match_sp, user_mw_match_sp, alias_match_sp, + real_match_sp; + + StringList local_matches; + if (matches == nullptr) + matches = &local_matches; + + unsigned int num_cmd_matches = 0; + unsigned int num_alias_matches = 0; + unsigned int num_user_matches = 0; + unsigned int num_user_mw_matches = 0; + + // Look through the command dictionaries one by one, and if we get only one + // match from any of them in toto, then return that, otherwise return an + // empty CommandObjectSP and the list of matches. + + if (HasCommands()) { + num_cmd_matches = AddNamesMatchingPartialString(m_command_dict, cmd_str, + *matches, descriptions); + } + + if (num_cmd_matches == 1) { + cmd.assign(matches->GetStringAtIndex(0)); + auto pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + real_match_sp = pos->second; + } + + if (include_aliases && HasAliases()) { + num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd_str, + *matches, descriptions); + } + + if (num_alias_matches == 1) { + cmd.assign(matches->GetStringAtIndex(num_cmd_matches)); + auto alias_pos = m_alias_dict.find(cmd); + if (alias_pos != m_alias_dict.end()) + alias_match_sp = alias_pos->second; + } + + if (HasUserCommands()) { + num_user_matches = AddNamesMatchingPartialString(m_user_dict, cmd_str, + *matches, descriptions); + } + + if (num_user_matches == 1) { + cmd.assign( + matches->GetStringAtIndex(num_cmd_matches + num_alias_matches)); + + auto pos = m_user_dict.find(cmd); + if (pos != m_user_dict.end()) + user_match_sp = pos->second; + } + + if (HasUserMultiwordCommands()) { + num_user_mw_matches = AddNamesMatchingPartialString( + m_user_mw_dict, cmd_str, *matches, descriptions); + } + + if (num_user_mw_matches == 1) { + cmd.assign(matches->GetStringAtIndex(num_cmd_matches + num_alias_matches + + num_user_matches)); + + auto pos = m_user_mw_dict.find(cmd); + if (pos != m_user_mw_dict.end()) + user_mw_match_sp = pos->second; + } + + // If we got exactly one match, return that, otherwise return the match + // list. + + if (num_user_matches + num_user_mw_matches + num_cmd_matches + + num_alias_matches == + 1) { + if (num_cmd_matches) + return real_match_sp; + else if (num_alias_matches) + return alias_match_sp; + else if (num_user_mw_matches) + return user_mw_match_sp; + else + return user_match_sp; + } + } else if (matches && command_sp) { + matches->AppendString(cmd_str); + if (descriptions) + descriptions->AppendString(command_sp->GetHelp()); + } + + return command_sp; +} + +bool CommandInterpreter::AddCommand(llvm::StringRef name, + const lldb::CommandObjectSP &cmd_sp, + bool can_replace) { + if (cmd_sp.get()) + lldbassert((this == &cmd_sp->GetCommandInterpreter()) && + "tried to add a CommandObject from a different interpreter"); + + if (name.empty()) + return false; + + cmd_sp->SetIsUserCommand(false); + + std::string name_sstr(name); + auto name_iter = m_command_dict.find(name_sstr); + if (name_iter != m_command_dict.end()) { + if (!can_replace || !name_iter->second->IsRemovable()) + return false; + name_iter->second = cmd_sp; + } else { + m_command_dict[name_sstr] = cmd_sp; + } + return true; +} + +Status CommandInterpreter::AddUserCommand(llvm::StringRef name, + const lldb::CommandObjectSP &cmd_sp, + bool can_replace) { + Status result; + if (cmd_sp.get()) + lldbassert((this == &cmd_sp->GetCommandInterpreter()) && + "tried to add a CommandObject from a different interpreter"); + if (name.empty()) { + result.SetErrorString("can't use the empty string for a command name"); + return result; + } + // do not allow replacement of internal commands + if (CommandExists(name)) { + result.SetErrorString("can't replace builtin command"); + return result; + } + + if (UserCommandExists(name)) { + if (!can_replace) { + result.SetErrorStringWithFormatv( + "user command \"{0}\" already exists and force replace was not set " + "by --overwrite or 'settings set interpreter.require-overwrite " + "false'", + name); + return result; + } + if (cmd_sp->IsMultiwordObject()) { + if (!m_user_mw_dict[std::string(name)]->IsRemovable()) { + result.SetErrorString( + "can't replace explicitly non-removable multi-word command"); + return result; + } + } else { + if (!m_user_dict[std::string(name)]->IsRemovable()) { + result.SetErrorString("can't replace explicitly non-removable command"); + return result; + } + } + } + + cmd_sp->SetIsUserCommand(true); + + if (cmd_sp->IsMultiwordObject()) + m_user_mw_dict[std::string(name)] = cmd_sp; + else + m_user_dict[std::string(name)] = cmd_sp; + return result; +} + +CommandObjectSP +CommandInterpreter::GetCommandSPExact(llvm::StringRef cmd_str, + bool include_aliases) const { + // Break up the command string into words, in case it's a multi-word command. + Args cmd_words(cmd_str); + + if (cmd_str.empty()) + return {}; + + if (cmd_words.GetArgumentCount() == 1) + return GetCommandSP(cmd_str, include_aliases, true); + + // We have a multi-word command (seemingly), so we need to do more work. + // First, get the cmd_obj_sp for the first word in the command. + CommandObjectSP cmd_obj_sp = + GetCommandSP(cmd_words.GetArgumentAtIndex(0), include_aliases, true); + if (!cmd_obj_sp) + return {}; + + // Loop through the rest of the words in the command (everything passed in + // was supposed to be part of a command name), and find the appropriate + // sub-command SP for each command word.... + size_t end = cmd_words.GetArgumentCount(); + for (size_t i = 1; i < end; ++i) { + if (!cmd_obj_sp->IsMultiwordObject()) { + // We have more words in the command name, but we don't have a + // multiword object. Fail and return. + return {}; + } + + cmd_obj_sp = cmd_obj_sp->GetSubcommandSP(cmd_words.GetArgumentAtIndex(i)); + if (!cmd_obj_sp) { + // The sub-command name was invalid. Fail and return. + return {}; + } + } + + // We successfully looped through all the command words and got valid + // command objects for them. + return cmd_obj_sp; +} + +CommandObject * +CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str, + StringList *matches, + StringList *descriptions) const { + // Try to find a match among commands and aliases. Allowing inexact matches, + // but perferring exact matches. + return GetCommandSP(cmd_str, /*include_aliases=*/true, /*exact=*/false, + matches, descriptions) + .get(); +} + +CommandObject *CommandInterpreter::GetUserCommandObject( + llvm::StringRef cmd, StringList *matches, StringList *descriptions) const { + std::string cmd_str(cmd); + auto find_exact = [&](const CommandObject::CommandMap &map) { + auto found_elem = map.find(std::string(cmd)); + if (found_elem == map.end()) + return (CommandObject *)nullptr; + CommandObject *exact_cmd = found_elem->second.get(); + if (exact_cmd) { + if (matches) + matches->AppendString(exact_cmd->GetCommandName()); + if (descriptions) + descriptions->AppendString(exact_cmd->GetHelp()); + return exact_cmd; + } + return (CommandObject *)nullptr; + }; + + CommandObject *exact_cmd = find_exact(GetUserCommands()); + if (exact_cmd) + return exact_cmd; + + exact_cmd = find_exact(GetUserMultiwordCommands()); + if (exact_cmd) + return exact_cmd; + + // We didn't have an exact command, so now look for partial matches. + StringList tmp_list; + StringList *matches_ptr = matches ? matches : &tmp_list; + AddNamesMatchingPartialString(GetUserCommands(), cmd_str, *matches_ptr); + AddNamesMatchingPartialString(GetUserMultiwordCommands(), + cmd_str, *matches_ptr); + + return {}; +} + +bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const { + return m_command_dict.find(std::string(cmd)) != m_command_dict.end(); +} + +bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd, + std::string &full_name) const { + bool exact_match = + (m_alias_dict.find(std::string(cmd)) != m_alias_dict.end()); + if (exact_match) { + full_name.assign(std::string(cmd)); + return exact_match; + } else { + StringList matches; + size_t num_alias_matches; + num_alias_matches = + AddNamesMatchingPartialString(m_alias_dict, cmd, matches); + if (num_alias_matches == 1) { + // Make sure this isn't shadowing a command in the regular command space: + StringList regular_matches; + const bool include_aliases = false; + const bool exact = false; + CommandObjectSP cmd_obj_sp( + GetCommandSP(cmd, include_aliases, exact, ®ular_matches)); + if (cmd_obj_sp || regular_matches.GetSize() > 0) + return false; + else { + full_name.assign(matches.GetStringAtIndex(0)); + return true; + } + } else + return false; + } +} + +bool CommandInterpreter::AliasExists(llvm::StringRef cmd) const { + return m_alias_dict.find(std::string(cmd)) != m_alias_dict.end(); +} + +bool CommandInterpreter::UserCommandExists(llvm::StringRef cmd) const { + return m_user_dict.find(std::string(cmd)) != m_user_dict.end(); +} + +bool CommandInterpreter::UserMultiwordCommandExists(llvm::StringRef cmd) const { + return m_user_mw_dict.find(std::string(cmd)) != m_user_mw_dict.end(); +} + +CommandAlias * +CommandInterpreter::AddAlias(llvm::StringRef alias_name, + lldb::CommandObjectSP &command_obj_sp, + llvm::StringRef args_string) { + if (command_obj_sp.get()) + lldbassert((this == &command_obj_sp->GetCommandInterpreter()) && + "tried to add a CommandObject from a different interpreter"); + + std::unique_ptr<CommandAlias> command_alias_up( + new CommandAlias(*this, command_obj_sp, args_string, alias_name)); + + if (command_alias_up && command_alias_up->IsValid()) { + m_alias_dict[std::string(alias_name)] = + CommandObjectSP(command_alias_up.get()); + return command_alias_up.release(); + } + + return nullptr; +} + +bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) { + auto pos = m_alias_dict.find(std::string(alias_name)); + if (pos != m_alias_dict.end()) { + m_alias_dict.erase(pos); + return true; + } + return false; +} + +bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd, bool force) { + auto pos = m_command_dict.find(std::string(cmd)); + if (pos != m_command_dict.end()) { + if (force || pos->second->IsRemovable()) { + // Only regular expression objects or python commands are removable under + // normal circumstances. + m_command_dict.erase(pos); + return true; + } + } + return false; +} + +bool CommandInterpreter::RemoveUser(llvm::StringRef user_name) { + CommandObject::CommandMap::iterator pos = + m_user_dict.find(std::string(user_name)); + if (pos != m_user_dict.end()) { + m_user_dict.erase(pos); + return true; + } + return false; +} + +bool CommandInterpreter::RemoveUserMultiword(llvm::StringRef multi_name) { + CommandObject::CommandMap::iterator pos = + m_user_mw_dict.find(std::string(multi_name)); + if (pos != m_user_mw_dict.end()) { + m_user_mw_dict.erase(pos); + return true; + } + return false; +} + +void CommandInterpreter::GetHelp(CommandReturnObject &result, + uint32_t cmd_types) { + llvm::StringRef help_prologue(GetDebugger().GetIOHandlerHelpPrologue()); + if (!help_prologue.empty()) { + OutputFormattedHelpText(result.GetOutputStream(), llvm::StringRef(), + help_prologue); + } + + CommandObject::CommandMap::const_iterator pos; + size_t max_len = FindLongestCommandWord(m_command_dict); + + if ((cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin) { + result.AppendMessage("Debugger commands:"); + result.AppendMessage(""); + + for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) { + if (!(cmd_types & eCommandTypesHidden) && + (pos->first.compare(0, 1, "_") == 0)) + continue; + + OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--", + pos->second->GetHelp(), max_len); + } + result.AppendMessage(""); + } + + if (!m_alias_dict.empty() && + ((cmd_types & eCommandTypesAliases) == eCommandTypesAliases)) { + result.AppendMessageWithFormat( + "Current command abbreviations " + "(type '%shelp command alias' for more info):\n", + GetCommandPrefix()); + result.AppendMessage(""); + max_len = FindLongestCommandWord(m_alias_dict); + + for (auto alias_pos = m_alias_dict.begin(); alias_pos != m_alias_dict.end(); + ++alias_pos) { + OutputFormattedHelpText(result.GetOutputStream(), alias_pos->first, "--", + alias_pos->second->GetHelp(), max_len); + } + result.AppendMessage(""); + } + + if (!m_user_dict.empty() && + ((cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef)) { + result.AppendMessage("Current user-defined commands:"); + result.AppendMessage(""); + max_len = FindLongestCommandWord(m_user_dict); + for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) { + OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--", + pos->second->GetHelp(), max_len); + } + result.AppendMessage(""); + } + + if (!m_user_mw_dict.empty() && + ((cmd_types & eCommandTypesUserMW) == eCommandTypesUserMW)) { + result.AppendMessage("Current user-defined container commands:"); + result.AppendMessage(""); + max_len = FindLongestCommandWord(m_user_mw_dict); + for (pos = m_user_mw_dict.begin(); pos != m_user_mw_dict.end(); ++pos) { + OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--", + pos->second->GetHelp(), max_len); + } + result.AppendMessage(""); + } + + result.AppendMessageWithFormat( + "For more information on any command, type '%shelp <command-name>'.\n", + GetCommandPrefix()); +} + +CommandObject *CommandInterpreter::GetCommandObjectForCommand( + llvm::StringRef &command_string) { + // This function finds the final, lowest-level, alias-resolved command object + // whose 'Execute' function will eventually be invoked by the given command + // line. + + CommandObject *cmd_obj = nullptr; + size_t start = command_string.find_first_not_of(k_white_space); + size_t end = 0; + bool done = false; + while (!done) { + if (start != std::string::npos) { + // Get the next word from command_string. + end = command_string.find_first_of(k_white_space, start); + if (end == std::string::npos) + end = command_string.size(); + std::string cmd_word = + std::string(command_string.substr(start, end - start)); + + if (cmd_obj == nullptr) + // Since cmd_obj is NULL we are on our first time through this loop. + // Check to see if cmd_word is a valid command or alias. + cmd_obj = GetCommandObject(cmd_word); + else if (cmd_obj->IsMultiwordObject()) { + // Our current object is a multi-word object; see if the cmd_word is a + // valid sub-command for our object. + CommandObject *sub_cmd_obj = + cmd_obj->GetSubcommandObject(cmd_word.c_str()); + if (sub_cmd_obj) + cmd_obj = sub_cmd_obj; + else // cmd_word was not a valid sub-command word, so we are done + done = true; + } else + // We have a cmd_obj and it is not a multi-word object, so we are done. + done = true; + + // If we didn't find a valid command object, or our command object is not + // a multi-word object, or we are at the end of the command_string, then + // we are done. Otherwise, find the start of the next word. + + if (!cmd_obj || !cmd_obj->IsMultiwordObject() || + end >= command_string.size()) + done = true; + else + start = command_string.find_first_not_of(k_white_space, end); + } else + // Unable to find any more words. + done = true; + } + + command_string = command_string.substr(end); + return cmd_obj; +} + +static const char *k_valid_command_chars = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; +static void StripLeadingSpaces(std::string &s) { + if (!s.empty()) { + size_t pos = s.find_first_not_of(k_white_space); + if (pos == std::string::npos) + s.clear(); + else if (pos == 0) + return; + s.erase(0, pos); + } +} + +static size_t FindArgumentTerminator(const std::string &s) { + const size_t s_len = s.size(); + size_t offset = 0; + while (offset < s_len) { + size_t pos = s.find("--", offset); + if (pos == std::string::npos) + break; + if (pos > 0) { + if (llvm::isSpace(s[pos - 1])) { + // Check if the string ends "\s--" (where \s is a space character) or + // if we have "\s--\s". + if ((pos + 2 >= s_len) || llvm::isSpace(s[pos + 2])) { + return pos; + } + } + } + offset = pos + 2; + } + return std::string::npos; +} + +static bool ExtractCommand(std::string &command_string, std::string &command, + std::string &suffix, char "e_char) { + command.clear(); + suffix.clear(); + StripLeadingSpaces(command_string); + + bool result = false; + quote_char = '\0'; + + if (!command_string.empty()) { + const char first_char = command_string[0]; + if (first_char == '\'' || first_char == '"') { + quote_char = first_char; + const size_t end_quote_pos = command_string.find(quote_char, 1); + if (end_quote_pos == std::string::npos) { + command.swap(command_string); + command_string.erase(); + } else { + command.assign(command_string, 1, end_quote_pos - 1); + if (end_quote_pos + 1 < command_string.size()) + command_string.erase(0, command_string.find_first_not_of( + k_white_space, end_quote_pos + 1)); + else + command_string.erase(); + } + } else { + const size_t first_space_pos = + command_string.find_first_of(k_white_space); + if (first_space_pos == std::string::npos) { + command.swap(command_string); + command_string.erase(); + } else { + command.assign(command_string, 0, first_space_pos); + command_string.erase(0, command_string.find_first_not_of( + k_white_space, first_space_pos)); + } + } + result = true; + } + + if (!command.empty()) { + // actual commands can't start with '-' or '_' + if (command[0] != '-' && command[0] != '_') { + size_t pos = command.find_first_not_of(k_valid_command_chars); + if (pos > 0 && pos != std::string::npos) { + suffix.assign(command.begin() + pos, command.end()); + command.erase(pos); + } + } + } + + return result; +} + +CommandObject *CommandInterpreter::BuildAliasResult( + llvm::StringRef alias_name, std::string &raw_input_string, + std::string &alias_result, CommandReturnObject &result) { + CommandObject *alias_cmd_obj = nullptr; + Args cmd_args(raw_input_string); + alias_cmd_obj = GetCommandObject(alias_name); + StreamString result_str; + + if (!alias_cmd_obj || !alias_cmd_obj->IsAlias()) { + alias_result.clear(); + return alias_cmd_obj; + } + std::pair<CommandObjectSP, OptionArgVectorSP> desugared = + ((CommandAlias *)alias_cmd_obj)->Desugar(); + OptionArgVectorSP option_arg_vector_sp = desugared.second; + alias_cmd_obj = desugared.first.get(); + std::string alias_name_str = std::string(alias_name); + if ((cmd_args.GetArgumentCount() == 0) || + (alias_name_str != cmd_args.GetArgumentAtIndex(0))) + cmd_args.Unshift(alias_name_str); + + result_str.Printf("%s", alias_cmd_obj->GetCommandName().str().c_str()); + + if (!option_arg_vector_sp.get()) { + alias_result = std::string(result_str.GetString()); + return alias_cmd_obj; + } + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + int value_type; + std::string option; + std::string value; + for (const auto &entry : *option_arg_vector) { + std::tie(option, value_type, value) = entry; + if (option == g_argument) { + result_str.Printf(" %s", value.c_str()); + continue; + } + + result_str.Printf(" %s", option.c_str()); + if (value_type == OptionParser::eNoArgument) + continue; + + if (value_type != OptionParser::eOptionalArgument) + result_str.Printf(" "); + int index = GetOptionArgumentPosition(value.c_str()); + if (index == 0) + result_str.Printf("%s", value.c_str()); + else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) { + + result.AppendErrorWithFormat("Not enough arguments provided; you " + "need at least %d arguments to use " + "this alias.\n", + index); + return nullptr; + } else { + const Args::ArgEntry &entry = cmd_args[index]; + size_t strpos = raw_input_string.find(entry.c_str()); + const char quote_char = entry.GetQuoteChar(); + if (strpos != std::string::npos) { + const size_t start_fudge = quote_char == '\0' ? 0 : 1; + const size_t len_fudge = quote_char == '\0' ? 0 : 2; + + // Make sure we aren't going outside the bounds of the cmd string: + if (strpos < start_fudge) { + result.AppendError("Unmatched quote at command beginning."); + return nullptr; + } + llvm::StringRef arg_text = entry.ref(); + if (strpos - start_fudge + arg_text.size() + len_fudge > + raw_input_string.size()) { + result.AppendError("Unmatched quote at command end."); + return nullptr; + } + raw_input_string = raw_input_string.erase( + strpos - start_fudge, + strlen(cmd_args.GetArgumentAtIndex(index)) + len_fudge); + } + if (quote_char == '\0') + result_str.Printf("%s", cmd_args.GetArgumentAtIndex(index)); + else + result_str.Printf("%c%s%c", quote_char, entry.c_str(), quote_char); + } + } + + alias_result = std::string(result_str.GetString()); + return alias_cmd_obj; +} + +Status CommandInterpreter::PreprocessCommand(std::string &command) { + // The command preprocessor needs to do things to the command line before any + // parsing of arguments or anything else is done. The only current stuff that + // gets preprocessed is anything enclosed in backtick ('`') characters is + // evaluated as an expression and the result of the expression must be a + // scalar that can be substituted into the command. An example would be: + // (lldb) memory read `$rsp + 20` + Status error; // Status for any expressions that might not evaluate + size_t start_backtick; + size_t pos = 0; + while ((start_backtick = command.find('`', pos)) != std::string::npos) { + // Stop if an error was encountered during the previous iteration. + if (error.Fail()) + break; + + if (start_backtick > 0 && command[start_backtick - 1] == '\\') { + // The backtick was preceded by a '\' character, remove the slash and + // don't treat the backtick as the start of an expression. + command.erase(start_backtick - 1, 1); + // No need to add one to start_backtick since we just deleted a char. + pos = start_backtick; + continue; + } + + const size_t expr_content_start = start_backtick + 1; + const size_t end_backtick = command.find('`', expr_content_start); + + if (end_backtick == std::string::npos) { + // Stop if there's no end backtick. + break; + } + + if (end_backtick == expr_content_start) { + // Skip over empty expression. (two backticks in a row) + command.erase(start_backtick, 2); + continue; + } + + std::string expr_str(command, expr_content_start, + end_backtick - expr_content_start); + error = PreprocessToken(expr_str); + // We always stop at the first error: + if (error.Fail()) + break; + + command.erase(start_backtick, end_backtick - start_backtick + 1); + command.insert(start_backtick, std::string(expr_str)); + pos = start_backtick + expr_str.size(); + } + return error; +} + +Status +CommandInterpreter::PreprocessToken(std::string &expr_str) { + Status error; + ExecutionContext exe_ctx(GetExecutionContext()); + + // Get a dummy target to allow for calculator mode while processing + // backticks. This also helps break the infinite loop caused when target is + // null. + Target *exe_target = exe_ctx.GetTargetPtr(); + Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget(); + + ValueObjectSP expr_result_valobj_sp; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false); + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + options.SetKeepInMemory(false); + options.SetTryAllThreads(true); + options.SetTimeout(std::nullopt); + + ExpressionResults expr_result = + target.EvaluateExpression(expr_str.c_str(), exe_ctx.GetFramePtr(), + expr_result_valobj_sp, options); + + if (expr_result == eExpressionCompleted) { + Scalar scalar; + if (expr_result_valobj_sp) + expr_result_valobj_sp = + expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable( + expr_result_valobj_sp->GetDynamicValueType(), true); + if (expr_result_valobj_sp->ResolveValue(scalar)) { + + StreamString value_strm; + const bool show_type = false; + scalar.GetValue(value_strm, show_type); + size_t value_string_size = value_strm.GetSize(); + if (value_string_size) { + expr_str = value_strm.GetData(); + } else { + error.SetErrorStringWithFormat("expression value didn't result " + "in a scalar value for the " + "expression '%s'", + expr_str.c_str()); + } + } else { + error.SetErrorStringWithFormat("expression value didn't result " + "in a scalar value for the " + "expression '%s'", + expr_str.c_str()); + } + return error; + } + + // If we have an error from the expression evaluation it will be in the + // ValueObject error, which won't be success and we will just report it. + // But if for some reason we didn't get a value object at all, then we will + // make up some helpful errors from the expression result. + if (expr_result_valobj_sp) + error = expr_result_valobj_sp->GetError(); + + if (error.Success()) { + std::string result = lldb_private::toString(expr_result); + error.SetErrorString(result + "for the expression '" + expr_str + "'"); + } + return error; +} + +bool CommandInterpreter::HandleCommand(const char *command_line, + LazyBool lazy_add_to_history, + const ExecutionContext &override_context, + CommandReturnObject &result) { + + OverrideExecutionContext(override_context); + bool status = HandleCommand(command_line, lazy_add_to_history, result); + RestoreExecutionContext(); + return status; +} + +bool CommandInterpreter::HandleCommand(const char *command_line, + LazyBool lazy_add_to_history, + CommandReturnObject &result, + bool force_repeat_command) { + std::string command_string(command_line); + std::string original_command_string(command_line); + + Log *log = GetLog(LLDBLog::Commands); + llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")", + command_line); + + LLDB_LOGF(log, "Processing command: %s", command_line); + LLDB_SCOPED_TIMERF("Processing command: %s.", command_line); + + if (INTERRUPT_REQUESTED(GetDebugger(), "Interrupted initiating command")) { + result.AppendError("... Interrupted"); + return false; + } + + bool add_to_history; + if (lazy_add_to_history == eLazyBoolCalculate) + add_to_history = (m_command_source_depth == 0); + else + add_to_history = (lazy_add_to_history == eLazyBoolYes); + + // The same `transcript_item` will be used below to add output and error of + // the command. + StructuredData::DictionarySP transcript_item; + if (GetSaveTranscript()) { + m_transcript_stream << "(lldb) " << command_line << '\n'; + + transcript_item = std::make_shared<StructuredData::Dictionary>(); + transcript_item->AddStringItem("command", command_line); + transcript_item->AddIntegerItem( + "timestampInEpochSeconds", + std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count()); + m_transcript.AddItem(transcript_item); + } + + bool empty_command = false; + bool comment_command = false; + if (command_string.empty()) + empty_command = true; + else { + const char *k_space_characters = "\t\n\v\f\r "; + + size_t non_space = command_string.find_first_not_of(k_space_characters); + // Check for empty line or comment line (lines whose first non-space + // character is the comment character for this interpreter) + if (non_space == std::string::npos) + empty_command = true; + else if (command_string[non_space] == m_comment_char) + comment_command = true; + else if (command_string[non_space] == CommandHistory::g_repeat_char) { + llvm::StringRef search_str(command_string); + search_str = search_str.drop_front(non_space); + if (auto hist_str = m_command_history.FindString(search_str)) { + add_to_history = false; + command_string = std::string(*hist_str); + original_command_string = std::string(*hist_str); + } else { + result.AppendErrorWithFormat("Could not find entry: %s in history", + command_string.c_str()); + return false; + } + } + } + + if (empty_command) { + if (!GetRepeatPreviousCommand()) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } + + if (m_command_history.IsEmpty()) { + result.AppendError("empty command"); + return false; + } + + command_line = m_repeat_command.c_str(); + command_string = command_line; + original_command_string = command_line; + if (m_repeat_command.empty()) { + result.AppendError("No auto repeat."); + return false; + } + + add_to_history = false; + } else if (comment_command) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } + + // Phase 1. + + // Before we do ANY kind of argument processing, we need to figure out what + // the real/final command object is for the specified command. This gets + // complicated by the fact that the user could have specified an alias, and, + // in translating the alias, there may also be command options and/or even + // data (including raw text strings) that need to be found and inserted into + // the command line as part of the translation. So this first step is plain + // look-up and replacement, resulting in: + // 1. the command object whose Execute method will actually be called + // 2. a revised command string, with all substitutions and replacements + // taken care of + // From 1 above, we can determine whether the Execute function wants raw + // input or not. + + CommandObject *cmd_obj = ResolveCommandImpl(command_string, result); + + // We have to preprocess the whole command string for Raw commands, since we + // don't know the structure of the command. For parsed commands, we only + // treat backticks as quote characters specially. + // FIXME: We probably want to have raw commands do their own preprocessing. + // For instance, I don't think people expect substitution in expr expressions. + if (cmd_obj && cmd_obj->WantsRawCommandString()) { + Status error(PreprocessCommand(command_string)); + + if (error.Fail()) { + result.AppendError(error.AsCString()); + return false; + } + } + + // Although the user may have abbreviated the command, the command_string now + // has the command expanded to the full name. For example, if the input was + // "br s -n main", command_string is now "breakpoint set -n main". + if (log) { + llvm::StringRef command_name = cmd_obj ? cmd_obj->GetCommandName() : "<not found>"; + LLDB_LOGF(log, "HandleCommand, cmd_obj : '%s'", command_name.str().c_str()); + LLDB_LOGF(log, "HandleCommand, (revised) command_string: '%s'", + command_string.c_str()); + const bool wants_raw_input = + (cmd_obj != nullptr) ? cmd_obj->WantsRawCommandString() : false; + LLDB_LOGF(log, "HandleCommand, wants_raw_input:'%s'", + wants_raw_input ? "True" : "False"); + } + + // Phase 2. + // Take care of things like setting up the history command & calling the + // appropriate Execute method on the CommandObject, with the appropriate + // arguments. + StatsDuration execute_time; + if (cmd_obj != nullptr) { + bool generate_repeat_command = add_to_history; + // If we got here when empty_command was true, then this command is a + // stored "repeat command" which we should give a chance to produce it's + // repeat command, even though we don't add repeat commands to the history. + generate_repeat_command |= empty_command; + // For `command regex`, the regex command (ex `bt`) is added to history, but + // the resolved command (ex `thread backtrace`) is _not_ added to history. + // However, the resolved command must be given the opportunity to provide a + // repeat command. `force_repeat_command` supports this case. + generate_repeat_command |= force_repeat_command; + if (generate_repeat_command) { + Args command_args(command_string); + std::optional<std::string> repeat_command = + cmd_obj->GetRepeatCommand(command_args, 0); + if (repeat_command) { + LLDB_LOGF(log, "Repeat command: %s", repeat_command->data()); + m_repeat_command.assign(*repeat_command); + } else { + m_repeat_command.assign(original_command_string); + } + } + + if (add_to_history) + m_command_history.AppendString(original_command_string); + + std::string remainder; + const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size(); + if (actual_cmd_name_len < command_string.length()) + remainder = command_string.substr(actual_cmd_name_len); + + // Remove any initial spaces + size_t pos = remainder.find_first_not_of(k_white_space); + if (pos != 0 && pos != std::string::npos) + remainder.erase(0, pos); + + LLDB_LOGF( + log, "HandleCommand, command line after removing command name(s): '%s'", + remainder.c_str()); + + // To test whether or not transcript should be saved, `transcript_item` is + // used instead of `GetSaveTrasncript()`. This is because the latter will + // fail when the command is "settings set interpreter.save-transcript true". + if (transcript_item) { + transcript_item->AddStringItem("commandName", cmd_obj->GetCommandName()); + transcript_item->AddStringItem("commandArguments", remainder); + } + + ElapsedTime elapsed(execute_time); + cmd_obj->Execute(remainder.c_str(), result); + } + + LLDB_LOGF(log, "HandleCommand, command %s", + (result.Succeeded() ? "succeeded" : "did not succeed")); + + // To test whether or not transcript should be saved, `transcript_item` is + // used instead of `GetSaveTrasncript()`. This is because the latter will + // fail when the command is "settings set interpreter.save-transcript true". + if (transcript_item) { + m_transcript_stream << result.GetOutputData(); + m_transcript_stream << result.GetErrorData(); + + transcript_item->AddStringItem("output", result.GetOutputData()); + transcript_item->AddStringItem("error", result.GetErrorData()); + transcript_item->AddFloatItem("durationInSeconds", + execute_time.get().count()); + } + + return result.Succeeded(); +} + +void CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) { + bool look_for_subcommand = false; + + // For any of the command completions a unique match will be a complete word. + + if (request.GetParsedLine().GetArgumentCount() == 0) { + // We got nothing on the command line, so return the list of commands + bool include_aliases = true; + StringList new_matches, descriptions; + GetCommandNamesMatchingPartialString("", include_aliases, new_matches, + descriptions); + request.AddCompletions(new_matches, descriptions); + } else if (request.GetCursorIndex() == 0) { + // The cursor is in the first argument, so just do a lookup in the + // dictionary. + StringList new_matches, new_descriptions; + CommandObject *cmd_obj = + GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0), + &new_matches, &new_descriptions); + + if (new_matches.GetSize() && cmd_obj && cmd_obj->IsMultiwordObject() && + new_matches.GetStringAtIndex(0) != nullptr && + strcmp(request.GetParsedLine().GetArgumentAtIndex(0), + new_matches.GetStringAtIndex(0)) == 0) { + if (request.GetParsedLine().GetArgumentCount() != 1) { + look_for_subcommand = true; + new_matches.DeleteStringAtIndex(0); + new_descriptions.DeleteStringAtIndex(0); + request.AppendEmptyArgument(); + } + } + request.AddCompletions(new_matches, new_descriptions); + } + + if (request.GetCursorIndex() > 0 || look_for_subcommand) { + // We are completing further on into a commands arguments, so find the + // command and tell it to complete the command. First see if there is a + // matching initial command: + CommandObject *command_object = + GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0)); + if (command_object) { + request.ShiftArguments(); + command_object->HandleCompletion(request); + } + } +} + +void CommandInterpreter::HandleCompletion(CompletionRequest &request) { + + // Don't complete comments, and if the line we are completing is just the + // history repeat character, substitute the appropriate history line. + llvm::StringRef first_arg = request.GetParsedLine().GetArgumentAtIndex(0); + + if (!first_arg.empty()) { + if (first_arg.front() == m_comment_char) + return; + if (first_arg.front() == CommandHistory::g_repeat_char) { + if (auto hist_str = m_command_history.FindString(first_arg)) + request.AddCompletion(*hist_str, "Previous command history event", + CompletionMode::RewriteLine); + return; + } + } + + HandleCompletionMatches(request); +} + +std::optional<std::string> +CommandInterpreter::GetAutoSuggestionForCommand(llvm::StringRef line) { + if (line.empty()) + return std::nullopt; + const size_t s = m_command_history.GetSize(); + for (int i = s - 1; i >= 0; --i) { + llvm::StringRef entry = m_command_history.GetStringAtIndex(i); + if (entry.consume_front(line)) + return entry.str(); + } + return std::nullopt; +} + +void CommandInterpreter::UpdatePrompt(llvm::StringRef new_prompt) { + EventSP prompt_change_event_sp( + new Event(eBroadcastBitResetPrompt, new EventDataBytes(new_prompt))); + ; + BroadcastEvent(prompt_change_event_sp); + if (m_command_io_handler_sp) + m_command_io_handler_sp->SetPrompt(new_prompt); +} + +bool CommandInterpreter::Confirm(llvm::StringRef message, bool default_answer) { + // Check AutoConfirm first: + if (m_debugger.GetAutoConfirm()) + return default_answer; + + IOHandlerConfirm *confirm = + new IOHandlerConfirm(m_debugger, message, default_answer); + IOHandlerSP io_handler_sp(confirm); + m_debugger.RunIOHandlerSync(io_handler_sp); + return confirm->GetResponse(); +} + +const CommandAlias * +CommandInterpreter::GetAlias(llvm::StringRef alias_name) const { + OptionArgVectorSP ret_val; + + auto pos = m_alias_dict.find(std::string(alias_name)); + if (pos != m_alias_dict.end()) + return (CommandAlias *)pos->second.get(); + + return nullptr; +} + +bool CommandInterpreter::HasCommands() const { return (!m_command_dict.empty()); } + +bool CommandInterpreter::HasAliases() const { return (!m_alias_dict.empty()); } + +bool CommandInterpreter::HasUserCommands() const { return (!m_user_dict.empty()); } + +bool CommandInterpreter::HasUserMultiwordCommands() const { + return (!m_user_mw_dict.empty()); +} + +bool CommandInterpreter::HasAliasOptions() const { return HasAliases(); } + +void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj, + const char *alias_name, + Args &cmd_args, + std::string &raw_input_string, + CommandReturnObject &result) { + OptionArgVectorSP option_arg_vector_sp = + GetAlias(alias_name)->GetOptionArguments(); + + bool wants_raw_input = alias_cmd_obj->WantsRawCommandString(); + + // Make sure that the alias name is the 0th element in cmd_args + std::string alias_name_str = alias_name; + if (alias_name_str != cmd_args.GetArgumentAtIndex(0)) + cmd_args.Unshift(alias_name_str); + + Args new_args(alias_cmd_obj->GetCommandName()); + if (new_args.GetArgumentCount() == 2) + new_args.Shift(); + + if (option_arg_vector_sp.get()) { + if (wants_raw_input) { + // We have a command that both has command options and takes raw input. + // Make *sure* it has a " -- " in the right place in the + // raw_input_string. + size_t pos = raw_input_string.find(" -- "); + if (pos == std::string::npos) { + // None found; assume it goes at the beginning of the raw input string + raw_input_string.insert(0, " -- "); + } + } + + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + const size_t old_size = cmd_args.GetArgumentCount(); + std::vector<bool> used(old_size + 1, false); + + used[0] = true; + + int value_type; + std::string option; + std::string value; + for (const auto &option_entry : *option_arg_vector) { + std::tie(option, value_type, value) = option_entry; + if (option == g_argument) { + if (!wants_raw_input || (value != "--")) { + // Since we inserted this above, make sure we don't insert it twice + new_args.AppendArgument(value); + } + continue; + } + + if (value_type != OptionParser::eOptionalArgument) + new_args.AppendArgument(option); + + if (value == g_no_argument) + continue; + + int index = GetOptionArgumentPosition(value.c_str()); + if (index == 0) { + // value was NOT a positional argument; must be a real value + if (value_type != OptionParser::eOptionalArgument) + new_args.AppendArgument(value); + else { + new_args.AppendArgument(option + value); + } + + } else if (static_cast<size_t>(index) >= cmd_args.GetArgumentCount()) { + result.AppendErrorWithFormat("Not enough arguments provided; you " + "need at least %d arguments to use " + "this alias.\n", + index); + return; + } else { + // Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string + size_t strpos = + raw_input_string.find(cmd_args.GetArgumentAtIndex(index)); + if (strpos != std::string::npos) { + raw_input_string = raw_input_string.erase( + strpos, strlen(cmd_args.GetArgumentAtIndex(index))); + } + + if (value_type != OptionParser::eOptionalArgument) + new_args.AppendArgument(cmd_args.GetArgumentAtIndex(index)); + else { + new_args.AppendArgument(option + cmd_args.GetArgumentAtIndex(index)); + } + used[index] = true; + } + } + + for (auto entry : llvm::enumerate(cmd_args.entries())) { + if (!used[entry.index()] && !wants_raw_input) + new_args.AppendArgument(entry.value().ref()); + } + + cmd_args.Clear(); + cmd_args.SetArguments(new_args.GetArgumentCount(), + new_args.GetConstArgumentVector()); + } else { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + // This alias was not created with any options; nothing further needs to be + // done, unless it is a command that wants raw input, in which case we need + // to clear the rest of the data from cmd_args, since its in the raw input + // string. + if (wants_raw_input) { + cmd_args.Clear(); + cmd_args.SetArguments(new_args.GetArgumentCount(), + new_args.GetConstArgumentVector()); + } + return; + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); +} + +int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) { + int position = 0; // Any string that isn't an argument position, i.e. '%' + // followed by an integer, gets a position + // of zero. + + const char *cptr = in_string; + + // Does it start with '%' + if (cptr[0] == '%') { + ++cptr; + + // Is the rest of it entirely digits? + if (isdigit(cptr[0])) { + const char *start = cptr; + while (isdigit(cptr[0])) + ++cptr; + + // We've gotten to the end of the digits; are we at the end of the + // string? + if (cptr[0] == '\0') + position = atoi(start); + } + } + + return position; +} + +static void GetHomeInitFile(llvm::SmallVectorImpl<char> &init_file, + llvm::StringRef suffix = {}) { + std::string init_file_name = ".lldbinit"; + if (!suffix.empty()) { + init_file_name.append("-"); + init_file_name.append(suffix.str()); + } + + FileSystem::Instance().GetHomeDirectory(init_file); + llvm::sys::path::append(init_file, init_file_name); + + FileSystem::Instance().Resolve(init_file); +} + +static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file, + LanguageType language) { + if (language == eLanguageTypeUnknown) { + LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs(); + if (auto main_repl_language = repl_languages.GetSingularLanguage()) + language = *main_repl_language; + else + return; + } + + std::string init_file_name = + (llvm::Twine(".lldbinit-") + + llvm::Twine(Language::GetNameForLanguageType(language)) + + llvm::Twine("-repl")) + .str(); + FileSystem::Instance().GetHomeDirectory(init_file); + llvm::sys::path::append(init_file, init_file_name); + FileSystem::Instance().Resolve(init_file); +} + +static void GetCwdInitFile(llvm::SmallVectorImpl<char> &init_file) { + llvm::StringRef s = ".lldbinit"; + init_file.assign(s.begin(), s.end()); + FileSystem::Instance().Resolve(init_file); +} + +void CommandInterpreter::SourceInitFile(FileSpec file, + CommandReturnObject &result) { + assert(!m_skip_lldbinit_files); + + if (!FileSystem::Instance().Exists(file)) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return; + } + + // Use HandleCommand to 'source' the given file; this will do the actual + // broadcasting of the commands back to any appropriate listener (see + // CommandObjectSource::Execute for more details). + const bool saved_batch = SetBatchCommandMode(true); + CommandInterpreterRunOptions options; + options.SetSilent(true); + options.SetPrintErrors(true); + options.SetStopOnError(false); + options.SetStopOnContinue(true); + HandleCommandsFromFile(file, options, result); + SetBatchCommandMode(saved_batch); +} + +void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) { + if (m_skip_lldbinit_files) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return; + } + + llvm::SmallString<128> init_file; + GetCwdInitFile(init_file); + if (!FileSystem::Instance().Exists(init_file)) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return; + } + + LoadCWDlldbinitFile should_load = + Target::GetGlobalProperties().GetLoadCWDlldbinitFile(); + + switch (should_load) { + case eLoadCWDlldbinitFalse: + result.SetStatus(eReturnStatusSuccessFinishNoResult); + break; + case eLoadCWDlldbinitTrue: + SourceInitFile(FileSpec(init_file.str()), result); + break; + case eLoadCWDlldbinitWarn: { + llvm::SmallString<128> home_init_file; + GetHomeInitFile(home_init_file); + if (llvm::sys::path::parent_path(init_file) == + llvm::sys::path::parent_path(home_init_file)) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } else { + result.AppendError(InitFileWarning); + } + } + } +} + +/// We will first see if there is an application specific ".lldbinit" file +/// whose name is "~/.lldbinit" followed by a "-" and the name of the program. +/// If this file doesn't exist, we fall back to the REPL init file or the +/// default home init file in "~/.lldbinit". +void CommandInterpreter::SourceInitFileHome(CommandReturnObject &result, + bool is_repl) { + if (m_skip_lldbinit_files) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return; + } + + llvm::SmallString<128> init_file; + + if (is_repl) + GetHomeREPLInitFile(init_file, GetDebugger().GetREPLLanguage()); + + if (init_file.empty()) + GetHomeInitFile(init_file); + + if (!m_skip_app_init_files) { + llvm::StringRef program_name = + HostInfo::GetProgramFileSpec().GetFilename().GetStringRef(); + llvm::SmallString<128> program_init_file; + GetHomeInitFile(program_init_file, program_name); + if (FileSystem::Instance().Exists(program_init_file)) + init_file = program_init_file; + } + + SourceInitFile(FileSpec(init_file.str()), result); +} + +void CommandInterpreter::SourceInitFileGlobal(CommandReturnObject &result) { +#ifdef LLDB_GLOBAL_INIT_DIRECTORY + if (!m_skip_lldbinit_files) { + FileSpec init_file(LLDB_GLOBAL_INIT_DIRECTORY); + if (init_file) + init_file.MakeAbsolute(HostInfo::GetShlibDir()); + + init_file.AppendPathComponent("lldbinit"); + SourceInitFile(init_file, result); + return; + } +#endif + result.SetStatus(eReturnStatusSuccessFinishNoResult); +} + +const char *CommandInterpreter::GetCommandPrefix() { + const char *prefix = GetDebugger().GetIOHandlerCommandPrefix(); + return prefix == nullptr ? "" : prefix; +} + +PlatformSP CommandInterpreter::GetPlatform(bool prefer_target_platform) { + PlatformSP platform_sp; + if (prefer_target_platform) { + ExecutionContext exe_ctx(GetExecutionContext()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + platform_sp = target->GetPlatform(); + } + + if (!platform_sp) + platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform(); + return platform_sp; +} + +bool CommandInterpreter::DidProcessStopAbnormally() const { + auto exe_ctx = GetExecutionContext(); + TargetSP target_sp = exe_ctx.GetTargetSP(); + if (!target_sp) + return false; + + ProcessSP process_sp(target_sp->GetProcessSP()); + if (!process_sp) + return false; + + if (eStateStopped != process_sp->GetState()) + return false; + + for (const auto &thread_sp : process_sp->GetThreadList().Threads()) { + StopInfoSP stop_info = thread_sp->GetStopInfo(); + if (!stop_info) { + // If there's no stop_info, keep iterating through the other threads; + // it's enough that any thread has got a stop_info that indicates + // an abnormal stop, to consider the process to be stopped abnormally. + continue; + } + + const StopReason reason = stop_info->GetStopReason(); + if (reason == eStopReasonException || + reason == eStopReasonInstrumentation || + reason == eStopReasonProcessorTrace) + return true; + + if (reason == eStopReasonSignal) { + const auto stop_signal = static_cast<int32_t>(stop_info->GetValue()); + UnixSignalsSP signals_sp = process_sp->GetUnixSignals(); + if (!signals_sp || !signals_sp->SignalIsValid(stop_signal)) + // The signal is unknown, treat it as abnormal. + return true; + + const auto sigint_num = signals_sp->GetSignalNumberFromName("SIGINT"); + const auto sigstop_num = signals_sp->GetSignalNumberFromName("SIGSTOP"); + if ((stop_signal != sigint_num) && (stop_signal != sigstop_num)) + // The signal very likely implies a crash. + return true; + } + } + + return false; +} + +void +CommandInterpreter::HandleCommands(const StringList &commands, + const ExecutionContext &override_context, + const CommandInterpreterRunOptions &options, + CommandReturnObject &result) { + + OverrideExecutionContext(override_context); + HandleCommands(commands, options, result); + RestoreExecutionContext(); +} + +void CommandInterpreter::HandleCommands(const StringList &commands, + const CommandInterpreterRunOptions &options, + CommandReturnObject &result) { + size_t num_lines = commands.GetSize(); + + // If we are going to continue past a "continue" then we need to run the + // commands synchronously. Make sure you reset this value anywhere you return + // from the function. + + bool old_async_execution = m_debugger.GetAsyncExecution(); + + if (!options.GetStopOnContinue()) { + m_debugger.SetAsyncExecution(false); + } + + for (size_t idx = 0; idx < num_lines; idx++) { + const char *cmd = commands.GetStringAtIndex(idx); + if (cmd[0] == '\0') + continue; + + if (options.GetEchoCommands()) { + // TODO: Add Stream support. + result.AppendMessageWithFormat("%s %s\n", + m_debugger.GetPrompt().str().c_str(), cmd); + } + + CommandReturnObject tmp_result(m_debugger.GetUseColor()); + tmp_result.SetInteractive(result.GetInteractive()); + tmp_result.SetSuppressImmediateOutput(true); + + // We might call into a regex or alias command, in which case the + // add_to_history will get lost. This m_command_source_depth dingus is the + // way we turn off adding to the history in that case, so set it up here. + if (!options.GetAddToHistory()) + m_command_source_depth++; + bool success = HandleCommand(cmd, options.m_add_to_history, tmp_result); + if (!options.GetAddToHistory()) + m_command_source_depth--; + + if (options.GetPrintResults()) { + if (tmp_result.Succeeded()) + result.AppendMessage(tmp_result.GetOutputData()); + } + + if (!success || !tmp_result.Succeeded()) { + llvm::StringRef error_msg = tmp_result.GetErrorData(); + if (error_msg.empty()) + error_msg = "<unknown error>.\n"; + if (options.GetStopOnError()) { + result.AppendErrorWithFormat( + "Aborting reading of commands after command #%" PRIu64 + ": '%s' failed with %s", + (uint64_t)idx, cmd, error_msg.str().c_str()); + m_debugger.SetAsyncExecution(old_async_execution); + return; + } else if (options.GetPrintResults()) { + result.AppendMessageWithFormat( + "Command #%" PRIu64 " '%s' failed with %s", (uint64_t)idx + 1, cmd, + error_msg.str().c_str()); + } + } + + if (result.GetImmediateOutputStream()) + result.GetImmediateOutputStream()->Flush(); + + if (result.GetImmediateErrorStream()) + result.GetImmediateErrorStream()->Flush(); + + // N.B. Can't depend on DidChangeProcessState, because the state coming + // into the command execution could be running (for instance in Breakpoint + // Commands. So we check the return value to see if it is has running in + // it. + if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) || + (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) { + if (options.GetStopOnContinue()) { + // If we caused the target to proceed, and we're going to stop in that + // case, set the status in our real result before returning. This is + // an error if the continue was not the last command in the set of + // commands to be run. + if (idx != num_lines - 1) + result.AppendErrorWithFormat( + "Aborting reading of commands after command #%" PRIu64 + ": '%s' continued the target.\n", + (uint64_t)idx + 1, cmd); + else + result.AppendMessageWithFormat("Command #%" PRIu64 + " '%s' continued the target.\n", + (uint64_t)idx + 1, cmd); + + result.SetStatus(tmp_result.GetStatus()); + m_debugger.SetAsyncExecution(old_async_execution); + + return; + } + } + + // Also check for "stop on crash here: + if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash() && + DidProcessStopAbnormally()) { + if (idx != num_lines - 1) + result.AppendErrorWithFormat( + "Aborting reading of commands after command #%" PRIu64 + ": '%s' stopped with a signal or exception.\n", + (uint64_t)idx + 1, cmd); + else + result.AppendMessageWithFormat( + "Command #%" PRIu64 " '%s' stopped with a signal or exception.\n", + (uint64_t)idx + 1, cmd); + + result.SetStatus(tmp_result.GetStatus()); + m_debugger.SetAsyncExecution(old_async_execution); + + return; + } + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + m_debugger.SetAsyncExecution(old_async_execution); +} + +// Make flags that we can pass into the IOHandler so our delegates can do the +// right thing +enum { + eHandleCommandFlagStopOnContinue = (1u << 0), + eHandleCommandFlagStopOnError = (1u << 1), + eHandleCommandFlagEchoCommand = (1u << 2), + eHandleCommandFlagEchoCommentCommand = (1u << 3), + eHandleCommandFlagPrintResult = (1u << 4), + eHandleCommandFlagPrintErrors = (1u << 5), + eHandleCommandFlagStopOnCrash = (1u << 6), + eHandleCommandFlagAllowRepeats = (1u << 7) +}; + +void CommandInterpreter::HandleCommandsFromFile( + FileSpec &cmd_file, const ExecutionContext &context, + const CommandInterpreterRunOptions &options, CommandReturnObject &result) { + OverrideExecutionContext(context); + HandleCommandsFromFile(cmd_file, options, result); + RestoreExecutionContext(); +} + +void CommandInterpreter::HandleCommandsFromFile(FileSpec &cmd_file, + const CommandInterpreterRunOptions &options, CommandReturnObject &result) { + if (!FileSystem::Instance().Exists(cmd_file)) { + result.AppendErrorWithFormat( + "Error reading commands from file %s - file not found.\n", + cmd_file.GetFilename().AsCString("<Unknown>")); + return; + } + + std::string cmd_file_path = cmd_file.GetPath(); + auto input_file_up = + FileSystem::Instance().Open(cmd_file, File::eOpenOptionReadOnly); + if (!input_file_up) { + std::string error = llvm::toString(input_file_up.takeError()); + result.AppendErrorWithFormatv( + "error: an error occurred read file '{0}': {1}\n", cmd_file_path, + llvm::fmt_consume(input_file_up.takeError())); + return; + } + FileSP input_file_sp = FileSP(std::move(input_file_up.get())); + + Debugger &debugger = GetDebugger(); + + uint32_t flags = 0; + + if (options.m_stop_on_continue == eLazyBoolCalculate) { + if (m_command_source_flags.empty()) { + // Stop on continue by default + flags |= eHandleCommandFlagStopOnContinue; + } else if (m_command_source_flags.back() & + eHandleCommandFlagStopOnContinue) { + flags |= eHandleCommandFlagStopOnContinue; + } + } else if (options.m_stop_on_continue == eLazyBoolYes) { + flags |= eHandleCommandFlagStopOnContinue; + } + + if (options.m_stop_on_error == eLazyBoolCalculate) { + if (m_command_source_flags.empty()) { + if (GetStopCmdSourceOnError()) + flags |= eHandleCommandFlagStopOnError; + } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnError) { + flags |= eHandleCommandFlagStopOnError; + } + } else if (options.m_stop_on_error == eLazyBoolYes) { + flags |= eHandleCommandFlagStopOnError; + } + + // stop-on-crash can only be set, if it is present in all levels of + // pushed flag sets. + if (options.GetStopOnCrash()) { + if (m_command_source_flags.empty()) { + flags |= eHandleCommandFlagStopOnCrash; + } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash) { + flags |= eHandleCommandFlagStopOnCrash; + } + } + + if (options.m_echo_commands == eLazyBoolCalculate) { + if (m_command_source_flags.empty()) { + // Echo command by default + flags |= eHandleCommandFlagEchoCommand; + } else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand) { + flags |= eHandleCommandFlagEchoCommand; + } + } else if (options.m_echo_commands == eLazyBoolYes) { + flags |= eHandleCommandFlagEchoCommand; + } + + // We will only ever ask for this flag, if we echo commands in general. + if (options.m_echo_comment_commands == eLazyBoolCalculate) { + if (m_command_source_flags.empty()) { + // Echo comments by default + flags |= eHandleCommandFlagEchoCommentCommand; + } else if (m_command_source_flags.back() & + eHandleCommandFlagEchoCommentCommand) { + flags |= eHandleCommandFlagEchoCommentCommand; + } + } else if (options.m_echo_comment_commands == eLazyBoolYes) { + flags |= eHandleCommandFlagEchoCommentCommand; + } + + if (options.m_print_results == eLazyBoolCalculate) { + if (m_command_source_flags.empty()) { + // Print output by default + flags |= eHandleCommandFlagPrintResult; + } else if (m_command_source_flags.back() & eHandleCommandFlagPrintResult) { + flags |= eHandleCommandFlagPrintResult; + } + } else if (options.m_print_results == eLazyBoolYes) { + flags |= eHandleCommandFlagPrintResult; + } + + if (options.m_print_errors == eLazyBoolCalculate) { + if (m_command_source_flags.empty()) { + // Print output by default + flags |= eHandleCommandFlagPrintErrors; + } else if (m_command_source_flags.back() & eHandleCommandFlagPrintErrors) { + flags |= eHandleCommandFlagPrintErrors; + } + } else if (options.m_print_errors == eLazyBoolYes) { + flags |= eHandleCommandFlagPrintErrors; + } + + if (flags & eHandleCommandFlagPrintResult) { + debugger.GetOutputFile().Printf("Executing commands in '%s'.\n", + cmd_file_path.c_str()); + } + + // Used for inheriting the right settings when "command source" might + // have nested "command source" commands + lldb::StreamFileSP empty_stream_sp; + m_command_source_flags.push_back(flags); + IOHandlerSP io_handler_sp(new IOHandlerEditline( + debugger, IOHandler::Type::CommandInterpreter, input_file_sp, + empty_stream_sp, // Pass in an empty stream so we inherit the top + // input reader output stream + empty_stream_sp, // Pass in an empty stream so we inherit the top + // input reader error stream + flags, + nullptr, // Pass in NULL for "editline_name" so no history is saved, + // or written + debugger.GetPrompt(), llvm::StringRef(), + false, // Not multi-line + debugger.GetUseColor(), 0, *this)); + const bool old_async_execution = debugger.GetAsyncExecution(); + + // Set synchronous execution if we are not stopping on continue + if ((flags & eHandleCommandFlagStopOnContinue) == 0) + debugger.SetAsyncExecution(false); + + m_command_source_depth++; + m_command_source_dirs.push_back(cmd_file.CopyByRemovingLastPathComponent()); + + debugger.RunIOHandlerSync(io_handler_sp); + if (!m_command_source_flags.empty()) + m_command_source_flags.pop_back(); + + m_command_source_dirs.pop_back(); + m_command_source_depth--; + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + debugger.SetAsyncExecution(old_async_execution); +} + +bool CommandInterpreter::GetSynchronous() { return m_synchronous_execution; } + +void CommandInterpreter::SetSynchronous(bool value) { + m_synchronous_execution = value; +} + +void CommandInterpreter::OutputFormattedHelpText(Stream &strm, + llvm::StringRef prefix, + llvm::StringRef help_text) { + const uint32_t max_columns = m_debugger.GetTerminalWidth(); + + size_t line_width_max = max_columns - prefix.size(); + if (line_width_max < 16) + line_width_max = help_text.size() + prefix.size(); + + strm.IndentMore(prefix.size()); + bool prefixed_yet = false; + // Even if we have no help text we still want to emit the command name. + if (help_text.empty()) + help_text = "No help text"; + while (!help_text.empty()) { + // Prefix the first line, indent subsequent lines to line up + if (!prefixed_yet) { + strm << prefix; + prefixed_yet = true; + } else + strm.Indent(); + + // Never print more than the maximum on one line. + llvm::StringRef this_line = help_text.substr(0, line_width_max); + + // Always break on an explicit newline. + std::size_t first_newline = this_line.find_first_of("\n"); + + // Don't break on space/tab unless the text is too long to fit on one line. + std::size_t last_space = llvm::StringRef::npos; + if (this_line.size() != help_text.size()) + last_space = this_line.find_last_of(" \t"); + + // Break at whichever condition triggered first. + this_line = this_line.substr(0, std::min(first_newline, last_space)); + strm.PutCString(this_line); + strm.EOL(); + + // Remove whitespace / newlines after breaking. + help_text = help_text.drop_front(this_line.size()).ltrim(); + } + strm.IndentLess(prefix.size()); +} + +void CommandInterpreter::OutputFormattedHelpText(Stream &strm, + llvm::StringRef word_text, + llvm::StringRef separator, + llvm::StringRef help_text, + size_t max_word_len) { + StreamString prefix_stream; + prefix_stream.Printf(" %-*s %*s ", (int)max_word_len, word_text.data(), + (int)separator.size(), separator.data()); + OutputFormattedHelpText(strm, prefix_stream.GetString(), help_text); +} + +void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text, + llvm::StringRef separator, + llvm::StringRef help_text, + uint32_t max_word_len) { + int indent_size = max_word_len + separator.size() + 2; + + strm.IndentMore(indent_size); + + StreamString text_strm; + text_strm.Printf("%-*s ", (int)max_word_len, word_text.data()); + text_strm << separator << " " << help_text; + + const uint32_t max_columns = m_debugger.GetTerminalWidth(); + + llvm::StringRef text = text_strm.GetString(); + + uint32_t chars_left = max_columns; + + auto nextWordLength = [](llvm::StringRef S) { + size_t pos = S.find(' '); + return pos == llvm::StringRef::npos ? S.size() : pos; + }; + + while (!text.empty()) { + if (text.front() == '\n' || + (text.front() == ' ' && nextWordLength(text.ltrim(' ')) > chars_left)) { + strm.EOL(); + strm.Indent(); + chars_left = max_columns - indent_size; + if (text.front() == '\n') + text = text.drop_front(); + else + text = text.ltrim(' '); + } else { + strm.PutChar(text.front()); + --chars_left; + text = text.drop_front(); + } + } + + strm.EOL(); + strm.IndentLess(indent_size); +} + +void CommandInterpreter::FindCommandsForApropos( + llvm::StringRef search_word, StringList &commands_found, + StringList &commands_help, const CommandObject::CommandMap &command_map) { + for (const auto &pair : command_map) { + llvm::StringRef command_name = pair.first; + CommandObject *cmd_obj = pair.second.get(); + + const bool search_short_help = true; + const bool search_long_help = false; + const bool search_syntax = false; + const bool search_options = false; + if (command_name.contains_insensitive(search_word) || + cmd_obj->HelpTextContainsWord(search_word, search_short_help, + search_long_help, search_syntax, + search_options)) { + commands_found.AppendString(command_name); + commands_help.AppendString(cmd_obj->GetHelp()); + } + + if (auto *multiword_cmd = cmd_obj->GetAsMultiwordCommand()) { + StringList subcommands_found; + FindCommandsForApropos(search_word, subcommands_found, commands_help, + multiword_cmd->GetSubcommandDictionary()); + for (const auto &subcommand_name : subcommands_found) { + std::string qualified_name = + (command_name + " " + subcommand_name).str(); + commands_found.AppendString(qualified_name); + } + } + } +} + +void CommandInterpreter::FindCommandsForApropos(llvm::StringRef search_word, + StringList &commands_found, + StringList &commands_help, + bool search_builtin_commands, + bool search_user_commands, + bool search_alias_commands, + bool search_user_mw_commands) { + CommandObject::CommandMap::const_iterator pos; + + if (search_builtin_commands) + FindCommandsForApropos(search_word, commands_found, commands_help, + m_command_dict); + + if (search_user_commands) + FindCommandsForApropos(search_word, commands_found, commands_help, + m_user_dict); + + if (search_user_mw_commands) + FindCommandsForApropos(search_word, commands_found, commands_help, + m_user_mw_dict); + + if (search_alias_commands) + FindCommandsForApropos(search_word, commands_found, commands_help, + m_alias_dict); +} + +ExecutionContext CommandInterpreter::GetExecutionContext() const { + return !m_overriden_exe_contexts.empty() + ? m_overriden_exe_contexts.top() + : m_debugger.GetSelectedExecutionContext(); +} + +void CommandInterpreter::OverrideExecutionContext( + const ExecutionContext &override_context) { + m_overriden_exe_contexts.push(override_context); +} + +void CommandInterpreter::RestoreExecutionContext() { + if (!m_overriden_exe_contexts.empty()) + m_overriden_exe_contexts.pop(); +} + +void CommandInterpreter::GetProcessOutput() { + if (ProcessSP process_sp = GetExecutionContext().GetProcessSP()) + m_debugger.FlushProcessOutput(*process_sp, /*flush_stdout*/ true, + /*flush_stderr*/ true); +} + +void CommandInterpreter::StartHandlingCommand() { + auto idle_state = CommandHandlingState::eIdle; + if (m_command_state.compare_exchange_strong( + idle_state, CommandHandlingState::eInProgress)) + lldbassert(m_iohandler_nesting_level == 0); + else + lldbassert(m_iohandler_nesting_level > 0); + ++m_iohandler_nesting_level; +} + +void CommandInterpreter::FinishHandlingCommand() { + lldbassert(m_iohandler_nesting_level > 0); + if (--m_iohandler_nesting_level == 0) { + auto prev_state = m_command_state.exchange(CommandHandlingState::eIdle); + lldbassert(prev_state != CommandHandlingState::eIdle); + } +} + +bool CommandInterpreter::InterruptCommand() { + auto in_progress = CommandHandlingState::eInProgress; + return m_command_state.compare_exchange_strong( + in_progress, CommandHandlingState::eInterrupted); +} + +bool CommandInterpreter::WasInterrupted() const { + if (!m_debugger.IsIOHandlerThreadCurrentThread()) + return false; + + bool was_interrupted = + (m_command_state == CommandHandlingState::eInterrupted); + lldbassert(!was_interrupted || m_iohandler_nesting_level > 0); + return was_interrupted; +} + +void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler, + llvm::StringRef str, + bool is_stdout) { + + lldb::StreamFileSP stream = is_stdout ? io_handler.GetOutputStreamFileSP() + : io_handler.GetErrorStreamFileSP(); + // Split the output into lines and poll for interrupt requests + bool had_output = !str.empty(); + while (!str.empty()) { + llvm::StringRef line; + std::tie(line, str) = str.split('\n'); + { + std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex()); + stream->Write(line.data(), line.size()); + stream->Write("\n", 1); + } + } + + std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex()); + if (had_output && + INTERRUPT_REQUESTED(GetDebugger(), "Interrupted dumping command output")) + stream->Printf("\n... Interrupted.\n"); + stream->Flush(); +} + +bool CommandInterpreter::EchoCommandNonInteractive( + llvm::StringRef line, const Flags &io_handler_flags) const { + if (!io_handler_flags.Test(eHandleCommandFlagEchoCommand)) + return false; + + llvm::StringRef command = line.trim(); + if (command.empty()) + return true; + + if (command.front() == m_comment_char) + return io_handler_flags.Test(eHandleCommandFlagEchoCommentCommand); + + return true; +} + +void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler, + std::string &line) { + // If we were interrupted, bail out... + if (WasInterrupted()) + return; + + const bool is_interactive = io_handler.GetIsInteractive(); + const bool allow_repeats = + io_handler.GetFlags().Test(eHandleCommandFlagAllowRepeats); + + if (!is_interactive && !allow_repeats) { + // When we are not interactive, don't execute blank lines. This will happen + // sourcing a commands file. We don't want blank lines to repeat the + // previous command and cause any errors to occur (like redefining an + // alias, get an error and stop parsing the commands file). + // But obey the AllowRepeats flag if the user has set it. + if (line.empty()) + return; + } + if (!is_interactive) { + // When using a non-interactive file handle (like when sourcing commands + // from a file) we need to echo the command out so we don't just see the + // command output and no command... + if (EchoCommandNonInteractive(line, io_handler.GetFlags())) { + std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex()); + io_handler.GetOutputStreamFileSP()->Printf( + "%s%s\n", io_handler.GetPrompt(), line.c_str()); + } + } + + StartHandlingCommand(); + + ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext(); + bool pushed_exe_ctx = false; + if (exe_ctx.HasTargetScope()) { + OverrideExecutionContext(exe_ctx); + pushed_exe_ctx = true; + } + auto finalize = llvm::make_scope_exit([this, pushed_exe_ctx]() { + if (pushed_exe_ctx) + RestoreExecutionContext(); + }); + + lldb_private::CommandReturnObject result(m_debugger.GetUseColor()); + HandleCommand(line.c_str(), eLazyBoolCalculate, result); + + // Now emit the command output text from the command we just executed + if ((result.Succeeded() && + io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) || + io_handler.GetFlags().Test(eHandleCommandFlagPrintErrors)) { + // Display any STDOUT/STDERR _prior_ to emitting the command result text + GetProcessOutput(); + + if (!result.GetImmediateOutputStream()) { + llvm::StringRef output = result.GetOutputData(); + PrintCommandOutput(io_handler, output, true); + } + + // Now emit the command error text from the command we just executed + if (!result.GetImmediateErrorStream()) { + llvm::StringRef error = result.GetErrorData(); + PrintCommandOutput(io_handler, error, false); + } + } + + FinishHandlingCommand(); + + switch (result.GetStatus()) { + case eReturnStatusInvalid: + case eReturnStatusSuccessFinishNoResult: + case eReturnStatusSuccessFinishResult: + case eReturnStatusStarted: + break; + + case eReturnStatusSuccessContinuingNoResult: + case eReturnStatusSuccessContinuingResult: + if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnContinue)) + io_handler.SetIsDone(true); + break; + + case eReturnStatusFailed: + m_result.IncrementNumberOfErrors(); + if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnError)) { + m_result.SetResult(lldb::eCommandInterpreterResultCommandError); + io_handler.SetIsDone(true); + } + break; + + case eReturnStatusQuit: + m_result.SetResult(lldb::eCommandInterpreterResultQuitRequested); + io_handler.SetIsDone(true); + break; + } + + // Finally, if we're going to stop on crash, check that here: + if (m_result.IsResult(lldb::eCommandInterpreterResultSuccess) && + result.GetDidChangeProcessState() && + io_handler.GetFlags().Test(eHandleCommandFlagStopOnCrash) && + DidProcessStopAbnormally()) { + io_handler.SetIsDone(true); + m_result.SetResult(lldb::eCommandInterpreterResultInferiorCrash); + } +} + +bool CommandInterpreter::IOHandlerInterrupt(IOHandler &io_handler) { + ExecutionContext exe_ctx(GetExecutionContext()); + Process *process = exe_ctx.GetProcessPtr(); + + if (InterruptCommand()) + return true; + + if (process) { + StateType state = process->GetState(); + if (StateIsRunningState(state)) { + process->Halt(); + return true; // Don't do any updating when we are running + } + } + + ScriptInterpreter *script_interpreter = + m_debugger.GetScriptInterpreter(false); + if (script_interpreter) { + if (script_interpreter->Interrupt()) + return true; + } + return false; +} + +bool CommandInterpreter::SaveTranscript( + CommandReturnObject &result, std::optional<std::string> output_file) { + if (output_file == std::nullopt || output_file->empty()) { + std::string now = llvm::to_string(std::chrono::system_clock::now()); + std::replace(now.begin(), now.end(), ' ', '_'); + // Can't have file name with colons on Windows + std::replace(now.begin(), now.end(), ':', '-'); + const std::string file_name = "lldb_session_" + now + ".log"; + + FileSpec save_location = GetSaveSessionDirectory(); + + if (!save_location) + save_location = HostInfo::GetGlobalTempDir(); + + FileSystem::Instance().Resolve(save_location); + save_location.AppendPathComponent(file_name); + output_file = save_location.GetPath(); + } + + auto error_out = [&](llvm::StringRef error_message, std::string description) { + LLDB_LOG(GetLog(LLDBLog::Commands), "{0} ({1}:{2})", error_message, + output_file, description); + result.AppendErrorWithFormatv( + "Failed to save session's transcripts to {0}!", *output_file); + return false; + }; + + File::OpenOptions flags = File::eOpenOptionWriteOnly | + File::eOpenOptionCanCreate | + File::eOpenOptionTruncate; + + auto opened_file = FileSystem::Instance().Open(FileSpec(*output_file), flags); + + if (!opened_file) + return error_out("Unable to create file", + llvm::toString(opened_file.takeError())); + + FileUP file = std::move(opened_file.get()); + + size_t byte_size = m_transcript_stream.GetSize(); + + Status error = file->Write(m_transcript_stream.GetData(), byte_size); + + if (error.Fail() || byte_size != m_transcript_stream.GetSize()) + return error_out("Unable to write to destination file", + "Bytes written do not match transcript size."); + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + result.AppendMessageWithFormat("Session's transcripts saved to %s\n", + output_file->c_str()); + + if (GetOpenTranscriptInEditor() && Host::IsInteractiveGraphicSession()) { + const FileSpec file_spec; + error = file->GetFileSpec(const_cast<FileSpec &>(file_spec)); + if (error.Success()) { + if (llvm::Error e = Host::OpenFileInExternalEditor( + m_debugger.GetExternalEditor(), file_spec, 1)) + result.AppendError(llvm::toString(std::move(e))); + } + } + + return true; +} + +bool CommandInterpreter::IsInteractive() { + return (GetIOHandler() ? GetIOHandler()->GetIsInteractive() : false); +} + +FileSpec CommandInterpreter::GetCurrentSourceDir() { + if (m_command_source_dirs.empty()) + return {}; + return m_command_source_dirs.back(); +} + +void CommandInterpreter::GetLLDBCommandsFromIOHandler( + const char *prompt, IOHandlerDelegate &delegate, void *baton) { + Debugger &debugger = GetDebugger(); + IOHandlerSP io_handler_sp( + new IOHandlerEditline(debugger, IOHandler::Type::CommandList, + "lldb", // Name of input reader for history + llvm::StringRef(prompt), // Prompt + llvm::StringRef(), // Continuation prompt + true, // Get multiple lines + debugger.GetUseColor(), + 0, // Don't show line numbers + delegate)); // IOHandlerDelegate + + if (io_handler_sp) { + io_handler_sp->SetUserData(baton); + debugger.RunIOHandlerAsync(io_handler_sp); + } +} + +void CommandInterpreter::GetPythonCommandsFromIOHandler( + const char *prompt, IOHandlerDelegate &delegate, void *baton) { + Debugger &debugger = GetDebugger(); + IOHandlerSP io_handler_sp( + new IOHandlerEditline(debugger, IOHandler::Type::PythonCode, + "lldb-python", // Name of input reader for history + llvm::StringRef(prompt), // Prompt + llvm::StringRef(), // Continuation prompt + true, // Get multiple lines + debugger.GetUseColor(), + 0, // Don't show line numbers + delegate)); // IOHandlerDelegate + + if (io_handler_sp) { + io_handler_sp->SetUserData(baton); + debugger.RunIOHandlerAsync(io_handler_sp); + } +} + +bool CommandInterpreter::IsActive() { + return m_debugger.IsTopIOHandler(m_command_io_handler_sp); +} + +lldb::IOHandlerSP +CommandInterpreter::GetIOHandler(bool force_create, + CommandInterpreterRunOptions *options) { + // Always re-create the IOHandlerEditline in case the input changed. The old + // instance might have had a non-interactive input and now it does or vice + // versa. + if (force_create || !m_command_io_handler_sp) { + // Always re-create the IOHandlerEditline in case the input changed. The + // old instance might have had a non-interactive input and now it does or + // vice versa. + uint32_t flags = 0; + + if (options) { + if (options->m_stop_on_continue == eLazyBoolYes) + flags |= eHandleCommandFlagStopOnContinue; + if (options->m_stop_on_error == eLazyBoolYes) + flags |= eHandleCommandFlagStopOnError; + if (options->m_stop_on_crash == eLazyBoolYes) + flags |= eHandleCommandFlagStopOnCrash; + if (options->m_echo_commands != eLazyBoolNo) + flags |= eHandleCommandFlagEchoCommand; + if (options->m_echo_comment_commands != eLazyBoolNo) + flags |= eHandleCommandFlagEchoCommentCommand; + if (options->m_print_results != eLazyBoolNo) + flags |= eHandleCommandFlagPrintResult; + if (options->m_print_errors != eLazyBoolNo) + flags |= eHandleCommandFlagPrintErrors; + if (options->m_allow_repeats == eLazyBoolYes) + flags |= eHandleCommandFlagAllowRepeats; + } else { + flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult | + eHandleCommandFlagPrintErrors; + } + + m_command_io_handler_sp = std::make_shared<IOHandlerEditline>( + m_debugger, IOHandler::Type::CommandInterpreter, + m_debugger.GetInputFileSP(), m_debugger.GetOutputStreamSP(), + m_debugger.GetErrorStreamSP(), flags, "lldb", m_debugger.GetPrompt(), + llvm::StringRef(), // Continuation prompt + false, // Don't enable multiple line input, just single line commands + m_debugger.GetUseColor(), + 0, // Don't show line numbers + *this); // IOHandlerDelegate + } + return m_command_io_handler_sp; +} + +CommandInterpreterRunResult CommandInterpreter::RunCommandInterpreter( + CommandInterpreterRunOptions &options) { + // Always re-create the command interpreter when we run it in case any file + // handles have changed. + bool force_create = true; + m_debugger.RunIOHandlerAsync(GetIOHandler(force_create, &options)); + m_result = CommandInterpreterRunResult(); + + if (options.GetAutoHandleEvents()) + m_debugger.StartEventHandlerThread(); + + if (options.GetSpawnThread()) { + m_debugger.StartIOHandlerThread(); + } else { + // If the current thread is not managed by a host thread, we won't detect + // that this IS the CommandInterpreter IOHandler thread, so make it so: + HostThread new_io_handler_thread(Host::GetCurrentThread()); + HostThread old_io_handler_thread = + m_debugger.SetIOHandlerThread(new_io_handler_thread); + m_debugger.RunIOHandlers(); + m_debugger.SetIOHandlerThread(old_io_handler_thread); + + if (options.GetAutoHandleEvents()) + m_debugger.StopEventHandlerThread(); + } + + return m_result; +} + +CommandObject * +CommandInterpreter::ResolveCommandImpl(std::string &command_line, + CommandReturnObject &result) { + std::string scratch_command(command_line); // working copy so we don't modify + // command_line unless we succeed + CommandObject *cmd_obj = nullptr; + StreamString revised_command_line; + bool wants_raw_input = false; + std::string next_word; + StringList matches; + bool done = false; + while (!done) { + char quote_char = '\0'; + std::string suffix; + ExtractCommand(scratch_command, next_word, suffix, quote_char); + if (cmd_obj == nullptr) { + std::string full_name; + bool is_alias = GetAliasFullName(next_word, full_name); + cmd_obj = GetCommandObject(next_word, &matches); + bool is_real_command = + (!is_alias) || (cmd_obj != nullptr && !cmd_obj->IsAlias()); + if (!is_real_command) { + matches.Clear(); + std::string alias_result; + cmd_obj = + BuildAliasResult(full_name, scratch_command, alias_result, result); + revised_command_line.Printf("%s", alias_result.c_str()); + if (cmd_obj) { + wants_raw_input = cmd_obj->WantsRawCommandString(); + } + } else { + if (cmd_obj) { + llvm::StringRef cmd_name = cmd_obj->GetCommandName(); + revised_command_line.Printf("%s", cmd_name.str().c_str()); + wants_raw_input = cmd_obj->WantsRawCommandString(); + } else { + revised_command_line.Printf("%s", next_word.c_str()); + } + } + } else { + if (cmd_obj->IsMultiwordObject()) { + CommandObject *sub_cmd_obj = + cmd_obj->GetSubcommandObject(next_word.c_str()); + if (sub_cmd_obj) { + // The subcommand's name includes the parent command's name, so + // restart rather than append to the revised_command_line. + llvm::StringRef sub_cmd_name = sub_cmd_obj->GetCommandName(); + revised_command_line.Clear(); + revised_command_line.Printf("%s", sub_cmd_name.str().c_str()); + cmd_obj = sub_cmd_obj; + wants_raw_input = cmd_obj->WantsRawCommandString(); + } else { + if (quote_char) + revised_command_line.Printf(" %c%s%s%c", quote_char, + next_word.c_str(), suffix.c_str(), + quote_char); + else + revised_command_line.Printf(" %s%s", next_word.c_str(), + suffix.c_str()); + done = true; + } + } else { + if (quote_char) + revised_command_line.Printf(" %c%s%s%c", quote_char, + next_word.c_str(), suffix.c_str(), + quote_char); + else + revised_command_line.Printf(" %s%s", next_word.c_str(), + suffix.c_str()); + done = true; + } + } + + if (cmd_obj == nullptr) { + const size_t num_matches = matches.GetSize(); + if (matches.GetSize() > 1) { + StreamString error_msg; + error_msg.Printf("Ambiguous command '%s'. Possible matches:\n", + next_word.c_str()); + + for (uint32_t i = 0; i < num_matches; ++i) { + error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i)); + } + result.AppendRawError(error_msg.GetString()); + } else { + // We didn't have only one match, otherwise we wouldn't get here. + lldbassert(num_matches == 0); + result.AppendErrorWithFormat("'%s' is not a valid command.\n", + next_word.c_str()); + } + return nullptr; + } + + if (cmd_obj->IsMultiwordObject()) { + if (!suffix.empty()) { + result.AppendErrorWithFormat( + "command '%s' did not recognize '%s%s%s' as valid (subcommand " + "might be invalid).\n", + cmd_obj->GetCommandName().str().c_str(), + next_word.empty() ? "" : next_word.c_str(), + next_word.empty() ? " -- " : " ", suffix.c_str()); + return nullptr; + } + } else { + // If we found a normal command, we are done + done = true; + if (!suffix.empty()) { + switch (suffix[0]) { + case '/': + // GDB format suffixes + { + Options *command_options = cmd_obj->GetOptions(); + if (command_options && + command_options->SupportsLongOption("gdb-format")) { + std::string gdb_format_option("--gdb-format="); + gdb_format_option += (suffix.c_str() + 1); + + std::string cmd = std::string(revised_command_line.GetString()); + size_t arg_terminator_idx = FindArgumentTerminator(cmd); + if (arg_terminator_idx != std::string::npos) { + // Insert the gdb format option before the "--" that terminates + // options + gdb_format_option.append(1, ' '); + cmd.insert(arg_terminator_idx, gdb_format_option); + revised_command_line.Clear(); + revised_command_line.PutCString(cmd); + } else + revised_command_line.Printf(" %s", gdb_format_option.c_str()); + + if (wants_raw_input && + FindArgumentTerminator(cmd) == std::string::npos) + revised_command_line.PutCString(" --"); + } else { + result.AppendErrorWithFormat( + "the '%s' command doesn't support the --gdb-format option\n", + cmd_obj->GetCommandName().str().c_str()); + return nullptr; + } + } + break; + + default: + result.AppendErrorWithFormat( + "unknown command shorthand suffix: '%s'\n", suffix.c_str()); + return nullptr; + } + } + } + if (scratch_command.empty()) + done = true; + } + + if (!scratch_command.empty()) + revised_command_line.Printf(" %s", scratch_command.c_str()); + + if (cmd_obj != nullptr) + command_line = std::string(revised_command_line.GetString()); + + return cmd_obj; +} + +llvm::json::Value CommandInterpreter::GetStatistics() { + llvm::json::Object stats; + for (const auto &command_usage : m_command_usages) + stats.try_emplace(command_usage.getKey(), command_usage.getValue()); + return stats; +} + +const StructuredData::Array &CommandInterpreter::GetTranscript() const { + return m_transcript; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandObject.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandObject.cpp new file mode 100644 index 000000000000..4634b75c6a33 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/CommandObject.cpp @@ -0,0 +1,854 @@ +//===-- CommandObject.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 "lldb/Interpreter/CommandObject.h" + +#include <map> +#include <sstream> +#include <string> + +#include <cctype> +#include <cstdlib> + +#include "lldb/Core/Address.h" +#include "lldb/Interpreter/CommandOptionArgumentTable.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Utility/ArchSpec.h" +#include "llvm/ADT/ScopeExit.h" + +// These are for the Sourcename completers. +// FIXME: Make a separate file for the completers. +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/FileSpecList.h" + +#include "lldb/Target/Language.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandOptionArgumentTable.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +// CommandObject + +CommandObject::CommandObject(CommandInterpreter &interpreter, + llvm::StringRef name, llvm::StringRef help, + llvm::StringRef syntax, uint32_t flags) + : m_interpreter(interpreter), m_cmd_name(std::string(name)), + m_flags(flags), m_deprecated_command_override_callback(nullptr), + m_command_override_callback(nullptr), m_command_override_baton(nullptr) { + m_cmd_help_short = std::string(help); + m_cmd_syntax = std::string(syntax); +} + +Debugger &CommandObject::GetDebugger() { return m_interpreter.GetDebugger(); } + +llvm::StringRef CommandObject::GetHelp() { return m_cmd_help_short; } + +llvm::StringRef CommandObject::GetHelpLong() { return m_cmd_help_long; } + +llvm::StringRef CommandObject::GetSyntax() { + if (!m_cmd_syntax.empty()) + return m_cmd_syntax; + + StreamString syntax_str; + syntax_str.PutCString(GetCommandName()); + + if (!IsDashDashCommand() && GetOptions() != nullptr) + syntax_str.PutCString(" <cmd-options>"); + + if (!m_arguments.empty()) { + syntax_str.PutCString(" "); + + if (!IsDashDashCommand() && WantsRawCommandString() && GetOptions() && + GetOptions()->NumCommandOptions()) + syntax_str.PutCString("-- "); + GetFormattedCommandArguments(syntax_str); + } + m_cmd_syntax = std::string(syntax_str.GetString()); + + return m_cmd_syntax; +} + +llvm::StringRef CommandObject::GetCommandName() const { return m_cmd_name; } + +void CommandObject::SetCommandName(llvm::StringRef name) { + m_cmd_name = std::string(name); +} + +void CommandObject::SetHelp(llvm::StringRef str) { + m_cmd_help_short = std::string(str); +} + +void CommandObject::SetHelpLong(llvm::StringRef str) { + m_cmd_help_long = std::string(str); +} + +void CommandObject::SetSyntax(llvm::StringRef str) { + m_cmd_syntax = std::string(str); +} + +Options *CommandObject::GetOptions() { + // By default commands don't have options unless this virtual function is + // overridden by base classes. + return nullptr; +} + +bool CommandObject::ParseOptions(Args &args, CommandReturnObject &result) { + // See if the subclass has options? + Options *options = GetOptions(); + if (options != nullptr) { + Status error; + + auto exe_ctx = GetCommandInterpreter().GetExecutionContext(); + options->NotifyOptionParsingStarting(&exe_ctx); + + const bool require_validation = true; + llvm::Expected<Args> args_or = options->Parse( + args, &exe_ctx, GetCommandInterpreter().GetPlatform(true), + require_validation); + + if (args_or) { + args = std::move(*args_or); + error = options->NotifyOptionParsingFinished(&exe_ctx); + } else + error = args_or.takeError(); + + if (error.Success()) { + if (options->VerifyOptions(result)) + return true; + } else { + const char *error_cstr = error.AsCString(); + if (error_cstr) { + // We got an error string, lets use that + result.AppendError(error_cstr); + } else { + // No error string, output the usage information into result + options->GenerateOptionUsage( + result.GetErrorStream(), *this, + GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + } + } + result.SetStatus(eReturnStatusFailed); + return false; + } + return true; +} + +bool CommandObject::CheckRequirements(CommandReturnObject &result) { + // Nothing should be stored in m_exe_ctx between running commands as + // m_exe_ctx has shared pointers to the target, process, thread and frame and + // we don't want any CommandObject instances to keep any of these objects + // around longer than for a single command. Every command should call + // CommandObject::Cleanup() after it has completed. + assert(!m_exe_ctx.GetTargetPtr()); + assert(!m_exe_ctx.GetProcessPtr()); + assert(!m_exe_ctx.GetThreadPtr()); + assert(!m_exe_ctx.GetFramePtr()); + + // Lock down the interpreter's execution context prior to running the command + // so we guarantee the selected target, process, thread and frame can't go + // away during the execution + m_exe_ctx = m_interpreter.GetExecutionContext(); + + const uint32_t flags = GetFlags().Get(); + if (flags & (eCommandRequiresTarget | eCommandRequiresProcess | + eCommandRequiresThread | eCommandRequiresFrame | + eCommandTryTargetAPILock)) { + + if ((flags & eCommandRequiresTarget) && !m_exe_ctx.HasTargetScope()) { + result.AppendError(GetInvalidTargetDescription()); + return false; + } + + if ((flags & eCommandRequiresProcess) && !m_exe_ctx.HasProcessScope()) { + if (!m_exe_ctx.HasTargetScope()) + result.AppendError(GetInvalidTargetDescription()); + else + result.AppendError(GetInvalidProcessDescription()); + return false; + } + + if ((flags & eCommandRequiresThread) && !m_exe_ctx.HasThreadScope()) { + if (!m_exe_ctx.HasTargetScope()) + result.AppendError(GetInvalidTargetDescription()); + else if (!m_exe_ctx.HasProcessScope()) + result.AppendError(GetInvalidProcessDescription()); + else + result.AppendError(GetInvalidThreadDescription()); + return false; + } + + if ((flags & eCommandRequiresFrame) && !m_exe_ctx.HasFrameScope()) { + if (!m_exe_ctx.HasTargetScope()) + result.AppendError(GetInvalidTargetDescription()); + else if (!m_exe_ctx.HasProcessScope()) + result.AppendError(GetInvalidProcessDescription()); + else if (!m_exe_ctx.HasThreadScope()) + result.AppendError(GetInvalidThreadDescription()); + else + result.AppendError(GetInvalidFrameDescription()); + return false; + } + + if ((flags & eCommandRequiresRegContext) && + (m_exe_ctx.GetRegisterContext() == nullptr)) { + result.AppendError(GetInvalidRegContextDescription()); + return false; + } + + if (flags & eCommandTryTargetAPILock) { + Target *target = m_exe_ctx.GetTargetPtr(); + if (target) + m_api_locker = + std::unique_lock<std::recursive_mutex>(target->GetAPIMutex()); + } + } + + if (GetFlags().AnySet(eCommandProcessMustBeLaunched | + eCommandProcessMustBePaused)) { + Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process == nullptr) { + // A process that is not running is considered paused. + if (GetFlags().Test(eCommandProcessMustBeLaunched)) { + result.AppendError("Process must exist."); + return false; + } + } else { + StateType state = process->GetState(); + switch (state) { + case eStateInvalid: + case eStateSuspended: + case eStateCrashed: + case eStateStopped: + break; + + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateDetached: + case eStateExited: + case eStateUnloaded: + if (GetFlags().Test(eCommandProcessMustBeLaunched)) { + result.AppendError("Process must be launched."); + return false; + } + break; + + case eStateRunning: + case eStateStepping: + if (GetFlags().Test(eCommandProcessMustBePaused)) { + result.AppendError("Process is running. Use 'process interrupt' to " + "pause execution."); + return false; + } + } + } + } + + if (GetFlags().Test(eCommandProcessMustBeTraced)) { + Target *target = m_exe_ctx.GetTargetPtr(); + if (target && !target->GetTrace()) { + result.AppendError("Process is not being traced."); + return false; + } + } + + return true; +} + +void CommandObject::Cleanup() { + m_exe_ctx.Clear(); + if (m_api_locker.owns_lock()) + m_api_locker.unlock(); +} + +void CommandObject::HandleCompletion(CompletionRequest &request) { + + m_exe_ctx = m_interpreter.GetExecutionContext(); + auto reset_ctx = llvm::make_scope_exit([this]() { Cleanup(); }); + + // Default implementation of WantsCompletion() is !WantsRawCommandString(). + // Subclasses who want raw command string but desire, for example, argument + // completion should override WantsCompletion() to return true, instead. + if (WantsRawCommandString() && !WantsCompletion()) { + // FIXME: Abstract telling the completion to insert the completion + // character. + return; + } else { + // Can we do anything generic with the options? + Options *cur_options = GetOptions(); + CommandReturnObject result(m_interpreter.GetDebugger().GetUseColor()); + OptionElementVector opt_element_vector; + + if (cur_options != nullptr) { + opt_element_vector = cur_options->ParseForCompletion( + request.GetParsedLine(), request.GetCursorIndex()); + + bool handled_by_options = cur_options->HandleOptionCompletion( + request, opt_element_vector, GetCommandInterpreter()); + if (handled_by_options) + return; + } + + // If we got here, the last word is not an option or an option argument. + HandleArgumentCompletion(request, opt_element_vector); + } +} + +void CommandObject::HandleArgumentCompletion( + CompletionRequest &request, OptionElementVector &opt_element_vector) { + size_t num_arg_entries = GetNumArgumentEntries(); + if (num_arg_entries != 1) + return; + + CommandArgumentEntry *entry_ptr = GetArgumentEntryAtIndex(0); + if (!entry_ptr) { + assert(entry_ptr && "We said there was one entry, but there wasn't."); + return; // Not worth crashing if asserts are off... + } + + CommandArgumentEntry &entry = *entry_ptr; + // For now, we only handle the simple case of one homogenous argument type. + if (entry.size() != 1) + return; + + // Look up the completion type, and if it has one, invoke it: + const CommandObject::ArgumentTableEntry *arg_entry = + FindArgumentDataByType(entry[0].arg_type); + const ArgumentRepetitionType repeat = entry[0].arg_repetition; + + if (arg_entry == nullptr || arg_entry->completion_type == lldb::eNoCompletion) + return; + + // FIXME: This should be handled higher in the Command Parser. + // Check the case where this command only takes one argument, and don't do + // the completion if we aren't on the first entry: + if (repeat == eArgRepeatPlain && request.GetCursorIndex() != 0) + return; + + lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), arg_entry->completion_type, request, nullptr); + +} + + +bool CommandObject::HelpTextContainsWord(llvm::StringRef search_word, + bool search_short_help, + bool search_long_help, + bool search_syntax, + bool search_options) { + std::string options_usage_help; + + bool found_word = false; + + llvm::StringRef short_help = GetHelp(); + llvm::StringRef long_help = GetHelpLong(); + llvm::StringRef syntax_help = GetSyntax(); + + if (search_short_help && short_help.contains_insensitive(search_word)) + found_word = true; + else if (search_long_help && long_help.contains_insensitive(search_word)) + found_word = true; + else if (search_syntax && syntax_help.contains_insensitive(search_word)) + found_word = true; + + if (!found_word && search_options && GetOptions() != nullptr) { + StreamString usage_help; + GetOptions()->GenerateOptionUsage( + usage_help, *this, + GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + if (!usage_help.Empty()) { + llvm::StringRef usage_text = usage_help.GetString(); + if (usage_text.contains_insensitive(search_word)) + found_word = true; + } + } + + return found_word; +} + +bool CommandObject::ParseOptionsAndNotify(Args &args, + CommandReturnObject &result, + OptionGroupOptions &group_options, + ExecutionContext &exe_ctx) { + if (!ParseOptions(args, result)) + return false; + + Status error(group_options.NotifyOptionParsingFinished(&exe_ctx)); + if (error.Fail()) { + result.AppendError(error.AsCString()); + return false; + } + return true; +} + +void CommandObject::AddSimpleArgumentList( + CommandArgumentType arg_type, ArgumentRepetitionType repetition_type) { + + CommandArgumentEntry arg_entry; + CommandArgumentData simple_arg; + + // Define the first (and only) variant of this arg. + simple_arg.arg_type = arg_type; + simple_arg.arg_repetition = repetition_type; + + // There is only one variant this argument could be; put it into the argument + // entry. + arg_entry.push_back(simple_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back(arg_entry); +} + +int CommandObject::GetNumArgumentEntries() { return m_arguments.size(); } + +CommandObject::CommandArgumentEntry * +CommandObject::GetArgumentEntryAtIndex(int idx) { + if (static_cast<size_t>(idx) < m_arguments.size()) + return &(m_arguments[idx]); + + return nullptr; +} + +const CommandObject::ArgumentTableEntry * +CommandObject::FindArgumentDataByType(CommandArgumentType arg_type) { + for (int i = 0; i < eArgTypeLastArg; ++i) + if (g_argument_table[i].arg_type == arg_type) + return &(g_argument_table[i]); + + return nullptr; +} + +void CommandObject::GetArgumentHelp(Stream &str, CommandArgumentType arg_type, + CommandInterpreter &interpreter) { + const ArgumentTableEntry *entry = &(g_argument_table[arg_type]); + + // The table is *supposed* to be kept in arg_type order, but someone *could* + // have messed it up... + + if (entry->arg_type != arg_type) + entry = CommandObject::FindArgumentDataByType(arg_type); + + if (!entry) + return; + + StreamString name_str; + name_str.Printf("<%s>", entry->arg_name); + + if (entry->help_function) { + llvm::StringRef help_text = entry->help_function(); + if (!entry->help_function.self_formatting) { + interpreter.OutputFormattedHelpText(str, name_str.GetString(), "--", + help_text, name_str.GetSize()); + } else { + interpreter.OutputHelpText(str, name_str.GetString(), "--", help_text, + name_str.GetSize()); + } + } else { + interpreter.OutputFormattedHelpText(str, name_str.GetString(), "--", + entry->help_text, name_str.GetSize()); + + // Print enum values and their description if any. + OptionEnumValues enum_values = g_argument_table[arg_type].enum_values; + if (!enum_values.empty()) { + str.EOL(); + size_t longest = 0; + for (const OptionEnumValueElement &element : enum_values) + longest = + std::max(longest, llvm::StringRef(element.string_value).size()); + str.IndentMore(5); + for (const OptionEnumValueElement &element : enum_values) { + str.Indent(); + interpreter.OutputHelpText(str, element.string_value, ":", + element.usage, longest); + } + str.IndentLess(5); + str.EOL(); + } + } +} + +const char *CommandObject::GetArgumentName(CommandArgumentType arg_type) { + const ArgumentTableEntry *entry = &(g_argument_table[arg_type]); + + // The table is *supposed* to be kept in arg_type order, but someone *could* + // have messed it up... + + if (entry->arg_type != arg_type) + entry = CommandObject::FindArgumentDataByType(arg_type); + + if (entry) + return entry->arg_name; + + return nullptr; +} + +bool CommandObject::IsPairType(ArgumentRepetitionType arg_repeat_type) { + return (arg_repeat_type == eArgRepeatPairPlain) || + (arg_repeat_type == eArgRepeatPairOptional) || + (arg_repeat_type == eArgRepeatPairPlus) || + (arg_repeat_type == eArgRepeatPairStar) || + (arg_repeat_type == eArgRepeatPairRange) || + (arg_repeat_type == eArgRepeatPairRangeOptional); +} + +std::optional<ArgumentRepetitionType> +CommandObject::ArgRepetitionFromString(llvm::StringRef string) { + return llvm::StringSwitch<ArgumentRepetitionType>(string) + .Case("plain", eArgRepeatPlain) + .Case("optional", eArgRepeatOptional) + .Case("plus", eArgRepeatPlus) + .Case("star", eArgRepeatStar) + .Case("range", eArgRepeatRange) + .Case("pair-plain", eArgRepeatPairPlain) + .Case("pair-optional", eArgRepeatPairOptional) + .Case("pair-plus", eArgRepeatPairPlus) + .Case("pair-star", eArgRepeatPairStar) + .Case("pair-range", eArgRepeatPairRange) + .Case("pair-range-optional", eArgRepeatPairRangeOptional) + .Default({}); +} + +static CommandObject::CommandArgumentEntry +OptSetFiltered(uint32_t opt_set_mask, + CommandObject::CommandArgumentEntry &cmd_arg_entry) { + CommandObject::CommandArgumentEntry ret_val; + for (unsigned i = 0; i < cmd_arg_entry.size(); ++i) + if (opt_set_mask & cmd_arg_entry[i].arg_opt_set_association) + ret_val.push_back(cmd_arg_entry[i]); + return ret_val; +} + +// Default parameter value of opt_set_mask is LLDB_OPT_SET_ALL, which means +// take all the argument data into account. On rare cases where some argument +// sticks with certain option sets, this function returns the option set +// filtered args. +void CommandObject::GetFormattedCommandArguments(Stream &str, + uint32_t opt_set_mask) { + int num_args = m_arguments.size(); + for (int i = 0; i < num_args; ++i) { + if (i > 0) + str.Printf(" "); + CommandArgumentEntry arg_entry = + opt_set_mask == LLDB_OPT_SET_ALL + ? m_arguments[i] + : OptSetFiltered(opt_set_mask, m_arguments[i]); + // This argument is not associated with the current option set, so skip it. + if (arg_entry.empty()) + continue; + int num_alternatives = arg_entry.size(); + + if ((num_alternatives == 2) && IsPairType(arg_entry[0].arg_repetition)) { + const char *first_name = GetArgumentName(arg_entry[0].arg_type); + const char *second_name = GetArgumentName(arg_entry[1].arg_type); + switch (arg_entry[0].arg_repetition) { + case eArgRepeatPairPlain: + str.Printf("<%s> <%s>", first_name, second_name); + break; + case eArgRepeatPairOptional: + str.Printf("[<%s> <%s>]", first_name, second_name); + break; + case eArgRepeatPairPlus: + str.Printf("<%s> <%s> [<%s> <%s> [...]]", first_name, second_name, + first_name, second_name); + break; + case eArgRepeatPairStar: + str.Printf("[<%s> <%s> [<%s> <%s> [...]]]", first_name, second_name, + first_name, second_name); + break; + case eArgRepeatPairRange: + str.Printf("<%s_1> <%s_1> ... <%s_n> <%s_n>", first_name, second_name, + first_name, second_name); + break; + case eArgRepeatPairRangeOptional: + str.Printf("[<%s_1> <%s_1> ... <%s_n> <%s_n>]", first_name, second_name, + first_name, second_name); + break; + // Explicitly test for all the rest of the cases, so if new types get + // added we will notice the missing case statement(s). + case eArgRepeatPlain: + case eArgRepeatOptional: + case eArgRepeatPlus: + case eArgRepeatStar: + case eArgRepeatRange: + // These should not be reached, as they should fail the IsPairType test + // above. + break; + } + } else { + StreamString names; + for (int j = 0; j < num_alternatives; ++j) { + if (j > 0) + names.Printf(" | "); + names.Printf("%s", GetArgumentName(arg_entry[j].arg_type)); + } + + std::string name_str = std::string(names.GetString()); + switch (arg_entry[0].arg_repetition) { + case eArgRepeatPlain: + str.Printf("<%s>", name_str.c_str()); + break; + case eArgRepeatPlus: + str.Printf("<%s> [<%s> [...]]", name_str.c_str(), name_str.c_str()); + break; + case eArgRepeatStar: + str.Printf("[<%s> [<%s> [...]]]", name_str.c_str(), name_str.c_str()); + break; + case eArgRepeatOptional: + str.Printf("[<%s>]", name_str.c_str()); + break; + case eArgRepeatRange: + str.Printf("<%s_1> .. <%s_n>", name_str.c_str(), name_str.c_str()); + break; + // Explicitly test for all the rest of the cases, so if new types get + // added we will notice the missing case statement(s). + case eArgRepeatPairPlain: + case eArgRepeatPairOptional: + case eArgRepeatPairPlus: + case eArgRepeatPairStar: + case eArgRepeatPairRange: + case eArgRepeatPairRangeOptional: + // These should not be hit, as they should pass the IsPairType test + // above, and control should have gone into the other branch of the if + // statement. + break; + } + } + } +} + +CommandArgumentType +CommandObject::LookupArgumentName(llvm::StringRef arg_name) { + CommandArgumentType return_type = eArgTypeLastArg; + + arg_name = arg_name.ltrim('<').rtrim('>'); + + for (int i = 0; i < eArgTypeLastArg; ++i) + if (arg_name == g_argument_table[i].arg_name) + return_type = g_argument_table[i].arg_type; + + return return_type; +} + +void CommandObject::FormatLongHelpText(Stream &output_strm, + llvm::StringRef long_help) { + CommandInterpreter &interpreter = GetCommandInterpreter(); + std::stringstream lineStream{std::string(long_help)}; + std::string line; + while (std::getline(lineStream, line)) { + if (line.empty()) { + output_strm << "\n"; + continue; + } + size_t result = line.find_first_not_of(" \t"); + if (result == std::string::npos) { + result = 0; + } + std::string whitespace_prefix = line.substr(0, result); + std::string remainder = line.substr(result); + interpreter.OutputFormattedHelpText(output_strm, whitespace_prefix, + remainder); + } +} + +void CommandObject::GenerateHelpText(CommandReturnObject &result) { + GenerateHelpText(result.GetOutputStream()); + + result.SetStatus(eReturnStatusSuccessFinishNoResult); +} + +void CommandObject::GenerateHelpText(Stream &output_strm) { + CommandInterpreter &interpreter = GetCommandInterpreter(); + std::string help_text(GetHelp()); + if (WantsRawCommandString()) { + help_text.append(" Expects 'raw' input (see 'help raw-input'.)"); + } + interpreter.OutputFormattedHelpText(output_strm, "", help_text); + output_strm << "\nSyntax: " << GetSyntax() << "\n"; + Options *options = GetOptions(); + if (options != nullptr) { + options->GenerateOptionUsage( + output_strm, *this, + GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + } + llvm::StringRef long_help = GetHelpLong(); + if (!long_help.empty()) { + FormatLongHelpText(output_strm, long_help); + } + if (!IsDashDashCommand() && options && options->NumCommandOptions() > 0) { + if (WantsRawCommandString() && !WantsCompletion()) { + // Emit the message about using ' -- ' between the end of the command + // options and the raw input conditionally, i.e., only if the command + // object does not want completion. + interpreter.OutputFormattedHelpText( + output_strm, "", "", + "\nImportant Note: Because this command takes 'raw' input, if you " + "use any command options" + " you must use ' -- ' between the end of the command options and the " + "beginning of the raw input.", + 1); + } else if (GetNumArgumentEntries() > 0) { + // Also emit a warning about using "--" in case you are using a command + // that takes options and arguments. + interpreter.OutputFormattedHelpText( + output_strm, "", "", + "\nThis command takes options and free-form arguments. If your " + "arguments resemble" + " option specifiers (i.e., they start with a - or --), you must use " + "' -- ' between" + " the end of the command options and the beginning of the arguments.", + 1); + } + } +} + +void CommandObject::AddIDsArgumentData(CommandObject::IDType type) { + CommandArgumentEntry arg; + CommandArgumentData id_arg; + CommandArgumentData id_range_arg; + + // Create the first variant for the first (and only) argument for this + // command. + switch (type) { + case eBreakpointArgs: + id_arg.arg_type = eArgTypeBreakpointID; + id_range_arg.arg_type = eArgTypeBreakpointIDRange; + break; + case eWatchpointArgs: + id_arg.arg_type = eArgTypeWatchpointID; + id_range_arg.arg_type = eArgTypeWatchpointIDRange; + break; + } + id_arg.arg_repetition = eArgRepeatOptional; + id_range_arg.arg_repetition = eArgRepeatOptional; + + // The first (and only) argument for this command could be either an id or an + // id_range. Push both variants into the entry for the first argument for + // this command. + arg.push_back(id_arg); + arg.push_back(id_range_arg); + m_arguments.push_back(arg); +} + +const char *CommandObject::GetArgumentTypeAsCString( + const lldb::CommandArgumentType arg_type) { + assert(arg_type < eArgTypeLastArg && + "Invalid argument type passed to GetArgumentTypeAsCString"); + return g_argument_table[arg_type].arg_name; +} + +const char *CommandObject::GetArgumentDescriptionAsCString( + const lldb::CommandArgumentType arg_type) { + assert(arg_type < eArgTypeLastArg && + "Invalid argument type passed to GetArgumentDescriptionAsCString"); + return g_argument_table[arg_type].help_text; +} + +Target &CommandObject::GetDummyTarget() { + return m_interpreter.GetDebugger().GetDummyTarget(); +} + +Target &CommandObject::GetSelectedOrDummyTarget(bool prefer_dummy) { + return m_interpreter.GetDebugger().GetSelectedOrDummyTarget(prefer_dummy); +} + +Target &CommandObject::GetSelectedTarget() { + assert(m_flags.AnySet(eCommandRequiresTarget | eCommandProcessMustBePaused | + eCommandProcessMustBeLaunched | eCommandRequiresFrame | + eCommandRequiresThread | eCommandRequiresProcess | + eCommandRequiresRegContext) && + "GetSelectedTarget called from object that may have no target"); + return *m_interpreter.GetDebugger().GetSelectedTarget(); +} + +Thread *CommandObject::GetDefaultThread() { + Thread *thread_to_use = m_exe_ctx.GetThreadPtr(); + if (thread_to_use) + return thread_to_use; + + Process *process = m_exe_ctx.GetProcessPtr(); + if (!process) { + Target *target = m_exe_ctx.GetTargetPtr(); + if (!target) { + target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + } + if (target) + process = target->GetProcessSP().get(); + } + + if (process) + return process->GetThreadList().GetSelectedThread().get(); + else + return nullptr; +} + +void CommandObjectParsed::Execute(const char *args_string, + CommandReturnObject &result) { + bool handled = false; + Args cmd_args(args_string); + if (HasOverrideCallback()) { + Args full_args(GetCommandName()); + full_args.AppendArguments(cmd_args); + handled = + InvokeOverrideCallback(full_args.GetConstArgumentVector(), result); + } + if (!handled) { + for (auto entry : llvm::enumerate(cmd_args.entries())) { + const Args::ArgEntry &value = entry.value(); + if (!value.ref().empty() && value.GetQuoteChar() == '`') { + // We have to put the backtick back in place for PreprocessCommand. + std::string opt_string = value.c_str(); + Status error; + error = m_interpreter.PreprocessToken(opt_string); + if (error.Success()) + cmd_args.ReplaceArgumentAtIndex(entry.index(), opt_string); + } + } + + if (CheckRequirements(result)) { + if (ParseOptions(cmd_args, result)) { + // Call the command-specific version of 'Execute', passing it the + // already processed arguments. + if (cmd_args.GetArgumentCount() != 0 && m_arguments.empty()) { + result.AppendErrorWithFormatv("'{0}' doesn't take any arguments.", + GetCommandName()); + Cleanup(); + return; + } + m_interpreter.IncreaseCommandUsage(*this); + DoExecute(cmd_args, result); + } + } + + Cleanup(); + } +} + +void CommandObjectRaw::Execute(const char *args_string, + CommandReturnObject &result) { + bool handled = false; + if (HasOverrideCallback()) { + std::string full_command(GetCommandName()); + full_command += ' '; + full_command += args_string; + const char *argv[2] = {nullptr, nullptr}; + argv[0] = full_command.c_str(); + handled = InvokeOverrideCallback(argv, result); + } + if (!handled) { + if (CheckRequirements(result)) + DoExecute(args_string, result); + + Cleanup(); + } +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandOptionValidators.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandOptionValidators.cpp new file mode 100644 index 000000000000..a4b4b57c54b1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/CommandOptionValidators.cpp @@ -0,0 +1,36 @@ +//===-- CommandOptionValidators.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 "lldb/Interpreter/CommandOptionValidators.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Platform.h" + +using namespace lldb; +using namespace lldb_private; + +bool PosixPlatformCommandOptionValidator::IsValid( + Platform &platform, const ExecutionContext &target) const { + llvm::Triple::OSType os = + platform.GetSystemArchitecture().GetTriple().getOS(); + switch (os) { + // Are there any other platforms that are not POSIX-compatible? + case llvm::Triple::Win32: + return false; + default: + return true; + } +} + +const char *PosixPlatformCommandOptionValidator::ShortConditionString() const { + return "POSIX"; +} + +const char *PosixPlatformCommandOptionValidator::LongConditionString() const { + return "Option only valid for POSIX-compliant hosts."; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/CommandReturnObject.cpp b/contrib/llvm-project/lldb/source/Interpreter/CommandReturnObject.cpp new file mode 100644 index 000000000000..0bc58124f394 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/CommandReturnObject.cpp @@ -0,0 +1,175 @@ +//===-- CommandReturnObject.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 "lldb/Interpreter/CommandReturnObject.h" + +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +static llvm::raw_ostream &error(Stream &strm) { + return llvm::WithColor(strm.AsRawOstream(), llvm::HighlightColor::Error, + llvm::ColorMode::Enable) + << "error: "; +} + +static llvm::raw_ostream &warning(Stream &strm) { + return llvm::WithColor(strm.AsRawOstream(), llvm::HighlightColor::Warning, + llvm::ColorMode::Enable) + << "warning: "; +} + +static void DumpStringToStreamWithNewline(Stream &strm, const std::string &s) { + bool add_newline = false; + if (!s.empty()) { + // We already checked for empty above, now make sure there is a newline in + // the error, and if there isn't one, add one. + strm.Write(s.c_str(), s.size()); + + const char last_char = *s.rbegin(); + add_newline = last_char != '\n' && last_char != '\r'; + } + if (add_newline) + strm.EOL(); +} + +CommandReturnObject::CommandReturnObject(bool colors) + : m_out_stream(colors), m_err_stream(colors) {} + +void CommandReturnObject::AppendErrorWithFormat(const char *format, ...) { + SetStatus(eReturnStatusFailed); + + if (!format) + return; + va_list args; + va_start(args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end(args); + + const std::string &s = std::string(sstrm.GetString()); + if (!s.empty()) { + error(GetErrorStream()); + DumpStringToStreamWithNewline(GetErrorStream(), s); + } +} + +void CommandReturnObject::AppendMessageWithFormat(const char *format, ...) { + if (!format) + return; + va_list args; + va_start(args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end(args); + + GetOutputStream() << sstrm.GetString(); +} + +void CommandReturnObject::AppendWarningWithFormat(const char *format, ...) { + if (!format) + return; + va_list args; + va_start(args, format); + StreamString sstrm; + sstrm.PrintfVarArg(format, args); + va_end(args); + + warning(GetErrorStream()) << sstrm.GetString(); +} + +void CommandReturnObject::AppendMessage(llvm::StringRef in_string) { + if (in_string.empty()) + return; + GetOutputStream() << in_string.rtrim() << '\n'; +} + +void CommandReturnObject::AppendWarning(llvm::StringRef in_string) { + if (in_string.empty()) + return; + warning(GetErrorStream()) << in_string.rtrim() << '\n'; +} + +void CommandReturnObject::AppendError(llvm::StringRef in_string) { + SetStatus(eReturnStatusFailed); + if (in_string.empty()) + return; + // Workaround to deal with already fully formatted compiler diagnostics. + llvm::StringRef msg(in_string.rtrim()); + msg.consume_front("error: "); + error(GetErrorStream()) << msg << '\n'; +} + +void CommandReturnObject::SetError(const Status &error, + const char *fallback_error_cstr) { + if (error.Fail()) + AppendError(error.AsCString(fallback_error_cstr)); +} + +void CommandReturnObject::SetError(llvm::Error error) { + if (error) + AppendError(llvm::toString(std::move(error))); +} + +// Similar to AppendError, but do not prepend 'Status: ' to message, and don't +// append "\n" to the end of it. + +void CommandReturnObject::AppendRawError(llvm::StringRef in_string) { + SetStatus(eReturnStatusFailed); + assert(!in_string.empty() && "Expected a non-empty error message"); + GetErrorStream() << in_string; +} + +void CommandReturnObject::SetStatus(ReturnStatus status) { m_status = status; } + +ReturnStatus CommandReturnObject::GetStatus() const { return m_status; } + +bool CommandReturnObject::Succeeded() const { + return m_status <= eReturnStatusSuccessContinuingResult; +} + +bool CommandReturnObject::HasResult() const { + return (m_status == eReturnStatusSuccessFinishResult || + m_status == eReturnStatusSuccessContinuingResult); +} + +void CommandReturnObject::Clear() { + lldb::StreamSP stream_sp; + stream_sp = m_out_stream.GetStreamAtIndex(eStreamStringIndex); + if (stream_sp) + static_cast<StreamString *>(stream_sp.get())->Clear(); + stream_sp = m_err_stream.GetStreamAtIndex(eStreamStringIndex); + if (stream_sp) + static_cast<StreamString *>(stream_sp.get())->Clear(); + m_status = eReturnStatusStarted; + m_did_change_process_state = false; + m_suppress_immediate_output = false; + m_interactive = true; +} + +bool CommandReturnObject::GetDidChangeProcessState() const { + return m_did_change_process_state; +} + +void CommandReturnObject::SetDidChangeProcessState(bool b) { + m_did_change_process_state = b; +} + +bool CommandReturnObject::GetInteractive() const { return m_interactive; } + +void CommandReturnObject::SetInteractive(bool b) { m_interactive = b; } + +bool CommandReturnObject::GetSuppressImmediateOutput() const { + return m_suppress_immediate_output; +} + +void CommandReturnObject::SetSuppressImmediateOutput(bool b) { + m_suppress_immediate_output = b; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/InterpreterProperties.td b/contrib/llvm-project/lldb/source/Interpreter/InterpreterProperties.td new file mode 100644 index 000000000000..a5fccbbca091 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/InterpreterProperties.td @@ -0,0 +1,51 @@ +include "../../include/lldb/Core/PropertiesBase.td" + +let Definition = "interpreter" in { + def ExpandRegexAliases: Property<"expand-regex-aliases", "Boolean">, + Global, + DefaultFalse, + Desc<"If true, regular expression alias commands will show the expanded command that will be executed. This can be used to debug new regular expression alias commands.">; + def PromptOnQuit: Property<"prompt-on-quit", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, LLDB will prompt you before quitting if there are any live processes being debugged. If false, LLDB will quit without asking in any case.">; + def SaveTranscript: Property<"save-transcript", "Boolean">, + Global, + DefaultFalse, + Desc<"If true, commands will be saved into a transcript buffer for user access.">; + def SaveSessionOnQuit: Property<"save-session-on-quit", "Boolean">, + Global, + DefaultFalse, + Desc<"If true, LLDB will save the session's transcripts before quitting.">; + def OpenTranscriptInEditor: Property<"open-transcript-in-editor", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, LLDB will open the saved session's transcripts in the external editor.">; + def SaveSessionDirectory: Property<"save-session-directory", "FileSpec">, + DefaultStringValue<"">, + Desc<"A path where LLDB will save the session's transcripts. This is particularly useful when you can't set the session file, for example when using `save-session-on-quit`.">; + def StopCmdSourceOnError: Property<"stop-command-source-on-error", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, LLDB will stop running a 'command source' script upon encountering an error.">; + def SpaceReplPrompts: Property<"space-repl-prompts", "Boolean">, + Global, + DefaultFalse, + Desc<"If true, blank lines will be printed between between REPL submissions.">; + def EchoCommands: Property<"echo-commands", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, commands will be echoed before they are evaluated.">; + def EchoCommentCommands: Property<"echo-comment-commands", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, commands will be echoed even if they are pure comment lines.">; + def RepeatPreviousCommand: Property<"repeat-previous-command", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, LLDB will repeat the previous command if no command was passed to the interpreter. If false, LLDB won't repeat the previous command but only return a new prompt.">; + def RequireCommandOverwrite: Property<"require-overwrite", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, require --overwrite in 'command script add' before overwriting existing user commands.">; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionArgParser.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionArgParser.cpp new file mode 100644 index 000000000000..105d4846da14 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionArgParser.cpp @@ -0,0 +1,320 @@ +//===-- OptionArgParser.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 "lldb/Interpreter/OptionArgParser.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb_private; +using namespace lldb; + +bool OptionArgParser::ToBoolean(llvm::StringRef ref, bool fail_value, + bool *success_ptr) { + if (success_ptr) + *success_ptr = true; + ref = ref.trim(); + if (ref.equals_insensitive("false") || ref.equals_insensitive("off") || + ref.equals_insensitive("no") || ref.equals_insensitive("0")) { + return false; + } else if (ref.equals_insensitive("true") || ref.equals_insensitive("on") || + ref.equals_insensitive("yes") || ref.equals_insensitive("1")) { + return true; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +llvm::Expected<bool> OptionArgParser::ToBoolean(llvm::StringRef option_name, + llvm::StringRef option_arg) { + bool parse_success; + const bool option_value = + ToBoolean(option_arg, false /* doesn't matter */, &parse_success); + if (parse_success) + return option_value; + else + return llvm::createStringError( + "Invalid boolean value for option '%s': '%s'", + option_name.str().c_str(), + option_arg.empty() ? "<null>" : option_arg.str().c_str()); +} + +char OptionArgParser::ToChar(llvm::StringRef s, char fail_value, + bool *success_ptr) { + if (success_ptr) + *success_ptr = false; + if (s.size() != 1) + return fail_value; + + if (success_ptr) + *success_ptr = true; + return s[0]; +} + +int64_t OptionArgParser::ToOptionEnum(llvm::StringRef s, + const OptionEnumValues &enum_values, + int32_t fail_value, Status &error) { + error.Clear(); + if (enum_values.empty()) { + error.SetErrorString("invalid enumeration argument"); + return fail_value; + } + + if (s.empty()) { + error.SetErrorString("empty enumeration string"); + return fail_value; + } + + for (const auto &enum_value : enum_values) { + llvm::StringRef this_enum(enum_value.string_value); + if (this_enum.starts_with(s)) + return enum_value.value; + } + + StreamString strm; + strm.PutCString("invalid enumeration value, valid values are: "); + bool is_first = true; + for (const auto &enum_value : enum_values) { + strm.Printf("%s\"%s\"", + is_first ? is_first = false,"" : ", ", enum_value.string_value); + } + error.SetErrorString(strm.GetString()); + return fail_value; +} + +Status OptionArgParser::ToFormat(const char *s, lldb::Format &format, + size_t *byte_size_ptr) { + format = eFormatInvalid; + Status error; + + if (s && s[0]) { + if (byte_size_ptr) { + if (isdigit(s[0])) { + char *format_char = nullptr; + unsigned long byte_size = ::strtoul(s, &format_char, 0); + if (byte_size != ULONG_MAX) + *byte_size_ptr = byte_size; + s = format_char; + } else + *byte_size_ptr = 0; + } + + if (!FormatManager::GetFormatFromCString(s, format)) { + StreamString error_strm; + error_strm.Printf( + "Invalid format character or name '%s'. Valid values are:\n", s); + for (Format f = eFormatDefault; f < kNumFormats; f = Format(f + 1)) { + char format_char = FormatManager::GetFormatAsFormatChar(f); + if (format_char) + error_strm.Printf("'%c' or ", format_char); + + error_strm.Printf("\"%s\"", FormatManager::GetFormatAsCString(f)); + error_strm.EOL(); + } + + if (byte_size_ptr) + error_strm.PutCString( + "An optional byte size can precede the format character.\n"); + error.SetErrorString(error_strm.GetString()); + } + + if (error.Fail()) + return error; + } else { + error.SetErrorStringWithFormat("%s option string", s ? "empty" : "invalid"); + } + return error; +} + +lldb::ScriptLanguage OptionArgParser::ToScriptLanguage( + llvm::StringRef s, lldb::ScriptLanguage fail_value, bool *success_ptr) { + if (success_ptr) + *success_ptr = true; + + if (s.equals_insensitive("python")) + return eScriptLanguagePython; + if (s.equals_insensitive("lua")) + return eScriptLanguageLua; + if (s.equals_insensitive("default")) + return eScriptLanguageDefault; + if (s.equals_insensitive("none")) + return eScriptLanguageNone; + + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +lldb::addr_t OptionArgParser::ToRawAddress(const ExecutionContext *exe_ctx, + llvm::StringRef s, + lldb::addr_t fail_value, + Status *error_ptr) { + std::optional<lldb::addr_t> maybe_addr = DoToAddress(exe_ctx, s, error_ptr); + return maybe_addr ? *maybe_addr : fail_value; +} + +lldb::addr_t OptionArgParser::ToAddress(const ExecutionContext *exe_ctx, + llvm::StringRef s, + lldb::addr_t fail_value, + Status *error_ptr) { + std::optional<lldb::addr_t> maybe_addr = DoToAddress(exe_ctx, s, error_ptr); + if (!maybe_addr) + return fail_value; + + lldb::addr_t addr = *maybe_addr; + + if (Process *process = exe_ctx->GetProcessPtr()) + if (ABISP abi_sp = process->GetABI()) + addr = abi_sp->FixCodeAddress(addr); + + return addr; +} + +std::optional<lldb::addr_t> +OptionArgParser::DoToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s, + Status *error_ptr) { + if (s.empty()) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"", + s.str().c_str()); + return {}; + } + + llvm::StringRef sref = s; + + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + if (!s.getAsInteger(0, addr)) { + if (error_ptr) + error_ptr->Clear(); + + return addr; + } + + // Try base 16 with no prefix... + if (!s.getAsInteger(16, addr)) { + if (error_ptr) + error_ptr->Clear(); + return addr; + } + + Target *target = nullptr; + if (!exe_ctx || !(target = exe_ctx->GetTargetPtr())) { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"", + s.str().c_str()); + return {}; + } + + lldb::ValueObjectSP valobj_sp; + EvaluateExpressionOptions options; + options.SetCoerceToId(false); + options.SetUnwindOnError(true); + options.SetKeepInMemory(false); + options.SetTryAllThreads(true); + + ExpressionResults expr_result = + target->EvaluateExpression(s, exe_ctx->GetFramePtr(), valobj_sp, options); + + bool success = false; + if (expr_result == eExpressionCompleted) { + if (valobj_sp) + valobj_sp = valobj_sp->GetQualifiedRepresentationIfAvailable( + valobj_sp->GetDynamicValueType(), true); + // Get the address to watch. + if (valobj_sp) + addr = valobj_sp->GetValueAsUnsigned(0, &success); + if (success) { + if (error_ptr) + error_ptr->Clear(); + return addr; + } + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "address expression \"%s\" resulted in a value whose type " + "can't be converted to an address: %s", + s.str().c_str(), valobj_sp->GetTypeName().GetCString()); + return {}; + } + + // Since the compiler can't handle things like "main + 12" we should try to + // do this for now. The compiler doesn't like adding offsets to function + // pointer types. + // Some languages also don't have a natural representation for register + // values (e.g. swift) so handle simple uses of them here as well. + // We use a regex to parse these forms, the regex handles: + // $reg_name + // $reg_name+offset + // symbol_name+offset + // + // The important matching elements in the regex below are: + // 1: The reg name if there's no +offset + // 3: The symbol/reg name if there is an offset + // 4: +/- + // 5: The offset value. + static RegularExpression g_symbol_plus_offset_regex( + "^(\\$[^ +-]+)|(([^ +-]+)([-\\+])[[:space:]]*(0x[0-9A-Fa-f]+|[0-9]+)[[:space:]]*)$"); + + llvm::SmallVector<llvm::StringRef, 4> matches; + if (g_symbol_plus_offset_regex.Execute(sref, &matches)) { + uint64_t offset = 0; + llvm::StringRef name; + if (!matches[1].empty()) + name = matches[1]; + else + name = matches[3]; + + llvm::StringRef sign = matches[4]; + llvm::StringRef str_offset = matches[5]; + + // Some languages don't have a natural type for register values, but it + // is still useful to look them up here: + std::optional<lldb::addr_t> register_value; + StackFrame *frame = exe_ctx->GetFramePtr(); + llvm::StringRef reg_name = name; + if (frame && reg_name.consume_front("$")) { + RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); + if (reg_ctx_sp) { + const RegisterInfo *reg_info = reg_ctx_sp->GetRegisterInfoByName(reg_name); + if (reg_info) { + RegisterValue reg_val; + bool success = reg_ctx_sp->ReadRegister(reg_info, reg_val); + if (success && reg_val.GetType() != RegisterValue::eTypeInvalid) { + register_value = reg_val.GetAsUInt64(0, &success); + if (!success) + register_value.reset(); + } + } + } + } + if (!str_offset.empty() && !str_offset.getAsInteger(0, offset)) { + Status error; + if (register_value) + addr = register_value.value(); + else + addr = ToAddress(exe_ctx, name, LLDB_INVALID_ADDRESS, &error); + if (addr != LLDB_INVALID_ADDRESS) { + if (sign[0] == '+') + return addr + offset; + return addr - offset; + } + } else if (register_value) + // In the case of register values, someone might just want to get the + // value in a language whose expression parser doesn't support registers. + return register_value.value(); + } + + if (error_ptr) + error_ptr->SetErrorStringWithFormat( + "address expression \"%s\" evaluation failed", s.str().c_str()); + return {}; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupArchitecture.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupArchitecture.cpp new file mode 100644 index 000000000000..3925f835885f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupArchitecture.cpp @@ -0,0 +1,54 @@ +//===-- OptionGroupArchitecture.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 "lldb/Interpreter/OptionGroupArchitecture.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Target/Platform.h" + +using namespace lldb; +using namespace lldb_private; + +static constexpr OptionDefinition g_option_table[] = { + {LLDB_OPT_SET_1, false, "arch", 'a', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeArchitecture, + "Specify the architecture for the target."}, +}; + +llvm::ArrayRef<OptionDefinition> OptionGroupArchitecture::GetDefinitions() { + return llvm::ArrayRef(g_option_table); +} + +bool OptionGroupArchitecture::GetArchitecture(Platform *platform, + ArchSpec &arch) { + arch = Platform::GetAugmentedArchSpec(platform, m_arch_str); + return arch.IsValid(); +} + +Status +OptionGroupArchitecture::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) { + case 'a': + m_arch_str.assign(std::string(option_arg)); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; +} + +void OptionGroupArchitecture::OptionParsingStarting( + ExecutionContext *execution_context) { + m_arch_str.clear(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupBoolean.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupBoolean.cpp new file mode 100644 index 000000000000..3f73893fca96 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupBoolean.cpp @@ -0,0 +1,54 @@ +//===-- OptionGroupBoolean.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 "lldb/Interpreter/OptionGroupBoolean.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupBoolean::OptionGroupBoolean(uint32_t usage_mask, bool required, + const char *long_option, + int short_option, const char *usage_text, + bool default_value, + bool no_argument_toggle_default) + : m_value(default_value, default_value) { + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.validator = nullptr; + m_option_definition.option_has_arg = no_argument_toggle_default + ? OptionParser::eNoArgument + : OptionParser::eRequiredArgument; + m_option_definition.enum_values = {}; + m_option_definition.completion_type = 0; + m_option_definition.argument_type = eArgTypeBoolean; + m_option_definition.usage_text = usage_text; +} + +Status OptionGroupBoolean::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_value, + ExecutionContext *execution_context) { + Status error; + if (m_option_definition.option_has_arg == OptionParser::eNoArgument) { + // Not argument, toggle the default value and mark the option as having + // been set + m_value.SetCurrentValue(!m_value.GetDefaultValue()); + m_value.SetOptionWasSet(); + } else { + error = m_value.SetValueFromString(option_value); + } + return error; +} + +void OptionGroupBoolean::OptionParsingStarting( + ExecutionContext *execution_context) { + m_value.Clear(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFile.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFile.cpp new file mode 100644 index 000000000000..12ade1855829 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFile.cpp @@ -0,0 +1,75 @@ +//===-- OptionGroupFile.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 "lldb/Interpreter/OptionGroupFile.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupFile::OptionGroupFile(uint32_t usage_mask, bool required, + const char *long_option, int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text) { + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.validator = nullptr; + m_option_definition.option_has_arg = OptionParser::eRequiredArgument; + m_option_definition.enum_values = {}; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +Status OptionGroupFile::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error(m_file.SetValueFromString(option_arg)); + return error; +} + +void OptionGroupFile::OptionParsingStarting( + ExecutionContext *execution_context) { + m_file.Clear(); +} + +OptionGroupFileList::OptionGroupFileList( + uint32_t usage_mask, bool required, const char *long_option, + int short_option, uint32_t completion_type, + lldb::CommandArgumentType argument_type, const char *usage_text) + : m_file_list() { + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.validator = nullptr; + m_option_definition.option_has_arg = OptionParser::eRequiredArgument; + m_option_definition.enum_values = {}; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupFileList::~OptionGroupFileList() = default; + +Status +OptionGroupFileList::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_value, + ExecutionContext *execution_context) { + Status error(m_file_list.SetValueFromString(option_value)); + return error; +} + +void OptionGroupFileList::OptionParsingStarting( + ExecutionContext *execution_context) { + m_file_list.Clear(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFormat.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFormat.cpp new file mode 100644 index 000000000000..6b56ad2ea819 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupFormat.cpp @@ -0,0 +1,283 @@ +//===-- OptionGroupFormat.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 "lldb/Interpreter/OptionGroupFormat.h" + +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +static constexpr OptionDefinition g_default_option_definitions[] = { + {LLDB_OPT_SET_1, false, "format", 'f', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeFormat, + "Specify a format to be used for display."}, + {LLDB_OPT_SET_2, false, "gdb-format", 'G', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeGDBFormat, + "Specify a format using a GDB format specifier string."}, + {LLDB_OPT_SET_3, false, "size", 's', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeByteSize, + "The size in bytes to use when displaying with the selected format."}, + {LLDB_OPT_SET_4, false, "count", 'c', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeCount, + "The number of total items to display."}, +}; + +OptionGroupFormat::OptionGroupFormat( + lldb::Format default_format, uint64_t default_byte_size, + uint64_t default_count, OptionGroupFormatUsageTextVector usage_text_vector) + : m_format(default_format, default_format), + m_byte_size(default_byte_size, default_byte_size), + m_count(default_count, default_count), m_prev_gdb_format('x'), + m_prev_gdb_size('w'), m_has_gdb_format(false) { + // Copy the default option definitions. + std::copy(std::begin(g_default_option_definitions), + std::end(g_default_option_definitions), + std::begin(m_option_definitions)); + + for (auto usage_text_tuple : usage_text_vector) { + switch (std::get<0>(usage_text_tuple)) { + case eArgTypeFormat: + m_option_definitions[0].usage_text = std::get<1>(usage_text_tuple); + break; + case eArgTypeByteSize: + m_option_definitions[2].usage_text = std::get<1>(usage_text_tuple); + break; + default: + llvm_unreachable("Unimplemented option"); + } + } +} + +llvm::ArrayRef<OptionDefinition> OptionGroupFormat::GetDefinitions() { + auto result = llvm::ArrayRef(m_option_definitions); + if (m_byte_size.GetDefaultValue() < UINT64_MAX) { + if (m_count.GetDefaultValue() < UINT64_MAX) + return result; + else + return result.take_front(3); + } + return result.take_front(2); +} + +Status OptionGroupFormat::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = m_option_definitions[option_idx].short_option; + + switch (short_option) { + case 'f': + error = m_format.SetValueFromString(option_arg); + break; + + case 'c': + if (m_count.GetDefaultValue() == 0) { + error.SetErrorString("--count option is disabled"); + } else { + error = m_count.SetValueFromString(option_arg); + if (m_count.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid --count option value '%s'", + option_arg.str().c_str()); + } + break; + + case 's': + if (m_byte_size.GetDefaultValue() == 0) { + error.SetErrorString("--size option is disabled"); + } else { + error = m_byte_size.SetValueFromString(option_arg); + if (m_byte_size.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid --size option value '%s'", + option_arg.str().c_str()); + } + break; + + case 'G': { + uint64_t count = 0; + llvm::StringRef gdb_format_str = option_arg; + gdb_format_str.consumeInteger(0, count); + + Format format = eFormatDefault; + uint32_t byte_size = 0; + + while (!gdb_format_str.empty() && + ParserGDBFormatLetter(execution_context, gdb_format_str[0], format, + byte_size)) { + gdb_format_str = gdb_format_str.drop_front(); + } + + // We the first character of the "gdb_format_str" is not the + // NULL terminator, we didn't consume the entire string and + // something is wrong. Also, if none of the format, size or count was + // specified correctly, then abort. + if (!gdb_format_str.empty() || + (format == eFormatInvalid && byte_size == 0 && count == 0)) { + // Nothing got set correctly + error.SetErrorStringWithFormat("invalid gdb format string '%s'", + option_arg.str().c_str()); + return error; + } + + // At least one of the format, size or count was set correctly. Anything + // that wasn't set correctly should be set to the previous default + if (format == eFormatInvalid) + ParserGDBFormatLetter(execution_context, m_prev_gdb_format, format, + byte_size); + + const bool byte_size_enabled = m_byte_size.GetDefaultValue() < UINT64_MAX; + const bool count_enabled = m_count.GetDefaultValue() < UINT64_MAX; + if (byte_size_enabled) { + // Byte size is enabled + if (byte_size == 0) + ParserGDBFormatLetter(execution_context, m_prev_gdb_size, format, + byte_size); + } else { + // Byte size is disabled, make sure it wasn't specified but if this is an + // address, it's actually necessary to specify one so don't error out + if (byte_size > 0 && format != lldb::eFormatAddressInfo) { + error.SetErrorString( + "this command doesn't support specifying a byte size"); + return error; + } + } + + if (count_enabled) { + // Count is enabled and was not set, set it to the default for gdb format + // statements (which is 1). + if (count == 0) + count = 1; + } else { + // Count is disabled, make sure it wasn't specified + if (count > 0) { + error.SetErrorString("this command doesn't support specifying a count"); + return error; + } + } + + m_format.SetCurrentValue(format); + m_format.SetOptionWasSet(); + if (byte_size_enabled) { + m_byte_size.SetCurrentValue(byte_size); + m_byte_size.SetOptionWasSet(); + } + if (count_enabled) { + m_count.SetCurrentValue(count); + m_count.SetOptionWasSet(); + } + } break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; +} + +bool OptionGroupFormat::ParserGDBFormatLetter( + ExecutionContext *execution_context, char format_letter, Format &format, + uint32_t &byte_size) { + m_has_gdb_format = true; + switch (format_letter) { + case 'o': + format = eFormatOctal; + m_prev_gdb_format = format_letter; + return true; + case 'x': + format = eFormatHex; + m_prev_gdb_format = format_letter; + return true; + case 'd': + format = eFormatDecimal; + m_prev_gdb_format = format_letter; + return true; + case 'u': + format = eFormatUnsigned; + m_prev_gdb_format = format_letter; + return true; + case 't': + format = eFormatBinary; + m_prev_gdb_format = format_letter; + return true; + case 'f': + format = eFormatFloat; + m_prev_gdb_format = format_letter; + return true; + case 'a': + format = eFormatAddressInfo; + { + TargetSP target_sp = + execution_context ? execution_context->GetTargetSP() : TargetSP(); + if (target_sp) + byte_size = target_sp->GetArchitecture().GetAddressByteSize(); + m_prev_gdb_format = format_letter; + return true; + } + case 'i': + format = eFormatInstruction; + m_prev_gdb_format = format_letter; + return true; + case 'c': + format = eFormatChar; + m_prev_gdb_format = format_letter; + return true; + case 's': + format = eFormatCString; + m_prev_gdb_format = format_letter; + return true; + case 'T': + format = eFormatOSType; + m_prev_gdb_format = format_letter; + return true; + case 'A': + format = eFormatHexFloat; + m_prev_gdb_format = format_letter; + return true; + + case 'b': + case 'h': + case 'w': + case 'g': + { + // Size isn't used for printing instructions, so if a size is specified, + // and the previous format was 'i', then we should reset it to the + // default ('x'). Otherwise we'll continue to print as instructions, + // which isn't expected. + if (format_letter == 'b') + byte_size = 1; + else if (format_letter == 'h') + byte_size = 2; + else if (format_letter == 'w') + byte_size = 4; + else if (format_letter == 'g') + byte_size = 8; + + m_prev_gdb_size = format_letter; + if (m_prev_gdb_format == 'i') + m_prev_gdb_format = 'x'; + return true; + } + break; + default: + break; + } + + + return false; +} + +void OptionGroupFormat::OptionParsingStarting( + ExecutionContext *execution_context) { + m_format.Clear(); + m_byte_size.Clear(); + m_count.Clear(); + m_has_gdb_format = false; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupMemoryTag.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupMemoryTag.cpp new file mode 100644 index 000000000000..c6efab7c3871 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupMemoryTag.cpp @@ -0,0 +1,60 @@ +//===-- OptionGroupMemoryTag.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 "lldb/Interpreter/OptionGroupMemoryTag.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; + +static const uint32_t SHORT_OPTION_SHOW_TAGS = 0x54414753; // 'tags' + +OptionGroupMemoryTag::OptionGroupMemoryTag(bool note_binary /*=false*/) + : m_show_tags(false, false), m_option_definition{ + LLDB_OPT_SET_1, + false, + "show-tags", + SHORT_OPTION_SHOW_TAGS, + OptionParser::eNoArgument, + nullptr, + {}, + 0, + eArgTypeNone, + note_binary + ? "Include memory tags in output " + "(does not apply to binary output)." + : "Include memory tags in output."} {} + +llvm::ArrayRef<OptionDefinition> OptionGroupMemoryTag::GetDefinitions() { + return llvm::ArrayRef(m_option_definition); +} + +Status +OptionGroupMemoryTag::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + assert(option_idx == 0 && "Only one option in memory tag group!"); + + switch (m_option_definition.short_option) { + case SHORT_OPTION_SHOW_TAGS: + m_show_tags.SetCurrentValue(true); + m_show_tags.SetOptionWasSet(); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return {}; +} + +void OptionGroupMemoryTag::OptionParsingStarting( + ExecutionContext *execution_context) { + m_show_tags.Clear(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupOutputFile.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupOutputFile.cpp new file mode 100644 index 000000000000..f0044606e1de --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupOutputFile.cpp @@ -0,0 +1,60 @@ +//===-- OptionGroupOutputFile.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 "lldb/Interpreter/OptionGroupOutputFile.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupOutputFile::OptionGroupOutputFile() : m_append(false, false) {} + +static const uint32_t SHORT_OPTION_APND = 0x61706e64; // 'apnd' + +static constexpr OptionDefinition g_option_table[] = { + {LLDB_OPT_SET_1, false, "outfile", 'o', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeFilename, + "Specify a path for capturing command output."}, + {LLDB_OPT_SET_1, false, "append-outfile", SHORT_OPTION_APND, + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Append to the file specified with '--outfile <path>'."}, +}; + +llvm::ArrayRef<OptionDefinition> OptionGroupOutputFile::GetDefinitions() { + return llvm::ArrayRef(g_option_table); +} + +Status +OptionGroupOutputFile::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) { + case 'o': + error = m_file.SetValueFromString(option_arg); + break; + + case SHORT_OPTION_APND: + m_append.SetCurrentValue(true); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; +} + +void OptionGroupOutputFile::OptionParsingStarting( + ExecutionContext *execution_context) { + m_file.Clear(); + m_append.Clear(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPlatform.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPlatform.cpp new file mode 100644 index 000000000000..9928a5cda03e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPlatform.cpp @@ -0,0 +1,148 @@ +//===-- OptionGroupPlatform.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 "lldb/Interpreter/OptionGroupPlatform.h" + +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Platform.h" + +using namespace lldb; +using namespace lldb_private; + +PlatformSP OptionGroupPlatform::CreatePlatformWithOptions( + CommandInterpreter &interpreter, const ArchSpec &arch, bool make_selected, + Status &error, ArchSpec &platform_arch) const { + PlatformList &platforms = interpreter.GetDebugger().GetPlatformList(); + + PlatformSP platform_sp; + + if (!m_platform_name.empty()) { + platform_sp = platforms.Create(m_platform_name); + if (!platform_sp) { + error.SetErrorStringWithFormatv( + "unable to find a plug-in for the platform named \"{0}\"", + m_platform_name); + } + if (platform_sp) { + if (platform_arch.IsValid() && + !platform_sp->IsCompatibleArchitecture( + arch, {}, ArchSpec::CompatibleMatch, &platform_arch)) { + error.SetErrorStringWithFormatv("platform '{0}' doesn't support '{1}'", + platform_sp->GetPluginName(), + arch.GetTriple().getTriple()); + platform_sp.reset(); + return platform_sp; + } + } + } else if (arch.IsValid()) { + platform_sp = platforms.GetOrCreate(arch, {}, &platform_arch, error); + } + + if (platform_sp) { + if (make_selected) + platforms.SetSelectedPlatform(platform_sp); + if (!m_os_version.empty()) + platform_sp->SetOSVersion(m_os_version); + + if (!m_sdk_sysroot.empty()) + platform_sp->SetSDKRootDirectory(m_sdk_sysroot); + + if (!m_sdk_build.empty()) + platform_sp->SetSDKBuild(m_sdk_build); + } + + return platform_sp; +} + +void OptionGroupPlatform::OptionParsingStarting( + ExecutionContext *execution_context) { + m_platform_name.clear(); + m_sdk_sysroot.clear(); + m_sdk_build.clear(); + m_os_version = llvm::VersionTuple(); +} + +static constexpr OptionDefinition g_option_table[] = { + {LLDB_OPT_SET_ALL, false, "platform", 'p', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypePlatform, "Specify name of the platform to " + "use for this target, creating the " + "platform if necessary."}, + {LLDB_OPT_SET_ALL, false, "version", 'v', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeNone, + "Specify the initial SDK version to use prior to connecting."}, + {LLDB_OPT_SET_ALL, false, "build", 'b', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeNone, + "Specify the initial SDK build number."}, + {LLDB_OPT_SET_ALL, false, "sysroot", 'S', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeFilename, "Specify the SDK root directory " + "that contains a root of all " + "remote system files."}}; + +llvm::ArrayRef<OptionDefinition> OptionGroupPlatform::GetDefinitions() { + llvm::ArrayRef<OptionDefinition> result(g_option_table); + if (m_include_platform_option) + return result; + return result.drop_front(); +} + +Status +OptionGroupPlatform::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + if (!m_include_platform_option) + ++option_idx; + + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) { + case 'p': + m_platform_name.assign(option_arg.str()); + break; + + case 'v': + if (m_os_version.tryParse(option_arg)) + error.SetErrorStringWithFormatv("invalid version string '{0}'", + option_arg); + break; + + case 'b': + m_sdk_build.assign(option_arg.str()); + break; + + case 'S': + m_sdk_sysroot.assign(option_arg.str()); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + return error; +} + +bool OptionGroupPlatform::PlatformMatches( + const lldb::PlatformSP &platform_sp) const { + if (!platform_sp) + return false; + + if (!m_platform_name.empty() && platform_sp->GetName() != m_platform_name) + return false; + + if (!m_sdk_build.empty() && platform_sp->GetSDKBuild() != m_sdk_build) + return false; + + if (!m_sdk_sysroot.empty() && + platform_sp->GetSDKRootDirectory() != m_sdk_sysroot) + return false; + + if (!m_os_version.empty() && platform_sp->GetOSVersion() != m_os_version) + return false; + + return true; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp new file mode 100644 index 000000000000..8f507828518b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupPythonClassWithDict.cpp @@ -0,0 +1,156 @@ +//===-- OptionGroupPythonClassWithDict.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 "lldb/Interpreter/OptionGroupPythonClassWithDict.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupPythonClassWithDict::OptionGroupPythonClassWithDict( + const char *class_use, bool is_class, int class_option, int key_option, + int value_option, uint16_t required_options) + : m_is_class(is_class), m_required_options(required_options) { + m_key_usage_text.assign("The key for a key/value pair passed to the " + "implementation of a "); + m_key_usage_text.append(class_use); + m_key_usage_text.append(". Pairs can be specified more than once."); + + m_value_usage_text.assign("The value for the previous key in the pair passed " + "to the implementation of a "); + m_value_usage_text.append(class_use); + m_value_usage_text.append(". Pairs can be specified more than once."); + + m_class_usage_text.assign("The name of the "); + m_class_usage_text.append(m_is_class ? "class" : "function"); + m_class_usage_text.append(" that will manage a "); + m_class_usage_text.append(class_use); + m_class_usage_text.append("."); + + m_option_definition[0].usage_mask = LLDB_OPT_SET_1; + m_option_definition[0].required = m_required_options.Test(eScriptClass); + m_option_definition[0].long_option = "script-class"; + m_option_definition[0].short_option = class_option; + m_option_definition[0].validator = nullptr; + m_option_definition[0].option_has_arg = OptionParser::eRequiredArgument; + m_option_definition[0].enum_values = {}; + m_option_definition[0].completion_type = 0; + m_option_definition[0].argument_type = eArgTypePythonClass; + m_option_definition[0].usage_text = m_class_usage_text.data(); + + m_option_definition[1].usage_mask = LLDB_OPT_SET_2; + m_option_definition[1].required = m_required_options.Test(eDictKey); + m_option_definition[1].long_option = "structured-data-key"; + m_option_definition[1].short_option = key_option; + m_option_definition[1].validator = nullptr; + m_option_definition[1].option_has_arg = OptionParser::eRequiredArgument; + m_option_definition[1].enum_values = {}; + m_option_definition[1].completion_type = 0; + m_option_definition[1].argument_type = eArgTypeNone; + m_option_definition[1].usage_text = m_key_usage_text.data(); + + m_option_definition[2].usage_mask = LLDB_OPT_SET_2; + m_option_definition[2].required = m_required_options.Test(eDictValue); + m_option_definition[2].long_option = "structured-data-value"; + m_option_definition[2].short_option = value_option; + m_option_definition[2].validator = nullptr; + m_option_definition[2].option_has_arg = OptionParser::eRequiredArgument; + m_option_definition[2].enum_values = {}; + m_option_definition[2].completion_type = 0; + m_option_definition[2].argument_type = eArgTypeNone; + m_option_definition[2].usage_text = m_value_usage_text.data(); + + m_option_definition[3].usage_mask = LLDB_OPT_SET_3; + m_option_definition[3].required = m_required_options.Test(ePythonFunction); + m_option_definition[3].long_option = "python-function"; + m_option_definition[3].short_option = class_option; + m_option_definition[3].validator = nullptr; + m_option_definition[3].option_has_arg = OptionParser::eRequiredArgument; + m_option_definition[3].enum_values = {}; + m_option_definition[3].completion_type = 0; + m_option_definition[3].argument_type = eArgTypePythonFunction; + m_option_definition[3].usage_text = m_class_usage_text.data(); +} + +Status OptionGroupPythonClassWithDict::SetOptionValue( + uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + switch (option_idx) { + case 0: + case 3: { + m_name.assign(std::string(option_arg)); + } break; + case 1: { + if (!m_dict_sp) + m_dict_sp = std::make_shared<StructuredData::Dictionary>(); + if (m_current_key.empty()) + m_current_key.assign(std::string(option_arg)); + else + error.SetErrorStringWithFormat("Key: \"%s\" missing value.", + m_current_key.c_str()); + + } break; + case 2: { + if (!m_dict_sp) + m_dict_sp = std::make_shared<StructuredData::Dictionary>(); + if (!m_current_key.empty()) { + if (!option_arg.empty()) { + double d = 0; + std::string opt = option_arg.lower(); + + if (llvm::to_integer(option_arg, d)) { + if (opt[0] == '-') + m_dict_sp->AddIntegerItem(m_current_key, static_cast<int64_t>(d)); + else + m_dict_sp->AddIntegerItem(m_current_key, + static_cast<uint64_t>(d)); + } else if (llvm::to_float(option_arg, d)) { + m_dict_sp->AddFloatItem(m_current_key, d); + } else if (opt == "true" || opt == "false") { + m_dict_sp->AddBooleanItem(m_current_key, opt == "true"); + } else { + m_dict_sp->AddStringItem(m_current_key, option_arg); + } + } + + m_current_key.clear(); + } + else + error.SetErrorStringWithFormat("Value: \"%s\" missing matching key.", + option_arg.str().c_str()); + } break; + default: + llvm_unreachable("Unimplemented option"); + } + return error; +} + +void OptionGroupPythonClassWithDict::OptionParsingStarting( + ExecutionContext *execution_context) { + m_current_key.erase(); + // Leave the dictionary shared pointer unset. That way you can tell that + // the user didn't pass any -k -v pairs. We want to be able to warn if these + // were passed when the function they passed won't use them. + m_dict_sp.reset(); + m_name.clear(); +} + +Status OptionGroupPythonClassWithDict::OptionParsingFinished( + ExecutionContext *execution_context) { + Status error; + // If we get here and there's contents in the m_current_key, somebody must + // have provided a key but no value. + if (!m_current_key.empty()) + error.SetErrorStringWithFormat("Key: \"%s\" missing value.", + m_current_key.c_str()); + return error; +} + diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupString.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupString.cpp new file mode 100644 index 000000000000..75faaac9f6dd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupString.cpp @@ -0,0 +1,45 @@ +//===-- OptionGroupString.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 "lldb/Interpreter/OptionGroupString.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupString::OptionGroupString(uint32_t usage_mask, bool required, + const char *long_option, int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text, + const char *default_value) + : m_value(default_value, default_value) { + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.validator = nullptr; + m_option_definition.option_has_arg = OptionParser::eRequiredArgument; + m_option_definition.enum_values = {}; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +Status OptionGroupString::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error(m_value.SetValueFromString(option_arg)); + return error; +} + +void OptionGroupString::OptionParsingStarting( + ExecutionContext *execution_context) { + m_value.Clear(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUInt64.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUInt64.cpp new file mode 100644 index 000000000000..486941a7555a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUInt64.cpp @@ -0,0 +1,45 @@ +//===-- OptionGroupUInt64.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 "lldb/Interpreter/OptionGroupUInt64.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupUInt64::OptionGroupUInt64(uint32_t usage_mask, bool required, + const char *long_option, int short_option, + uint32_t completion_type, + lldb::CommandArgumentType argument_type, + const char *usage_text, + uint64_t default_value) + : m_value(default_value, default_value) { + m_option_definition.usage_mask = usage_mask; + m_option_definition.required = required; + m_option_definition.long_option = long_option; + m_option_definition.short_option = short_option; + m_option_definition.validator = nullptr; + m_option_definition.option_has_arg = OptionParser::eRequiredArgument; + m_option_definition.enum_values = {}; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +Status OptionGroupUInt64::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error(m_value.SetValueFromString(option_arg)); + return error; +} + +void OptionGroupUInt64::OptionParsingStarting( + ExecutionContext *execution_context) { + m_value.Clear(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUUID.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUUID.cpp new file mode 100644 index 000000000000..7a0efbab0398 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupUUID.cpp @@ -0,0 +1,48 @@ +//===-- OptionGroupUUID.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 "lldb/Interpreter/OptionGroupUUID.h" + +#include "lldb/Host/OptionParser.h" + +using namespace lldb; +using namespace lldb_private; + +static constexpr OptionDefinition g_option_table[] = { + {LLDB_OPT_SET_1, false, "uuid", 'u', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeModuleUUID, "A module UUID value."}, +}; + +llvm::ArrayRef<OptionDefinition> OptionGroupUUID::GetDefinitions() { + return llvm::ArrayRef(g_option_table); +} + +Status OptionGroupUUID::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) { + case 'u': + error = m_uuid.SetValueFromString(option_arg); + if (error.Success()) + m_uuid.SetOptionWasSet(); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; +} + +void OptionGroupUUID::OptionParsingStarting( + ExecutionContext *execution_context) { + m_uuid.Clear(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp new file mode 100644 index 000000000000..294665fa6b45 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp @@ -0,0 +1,223 @@ +//===-- OptionGroupValueObjectDisplay.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 "lldb/Interpreter/OptionGroupValueObjectDisplay.h" + +#include "lldb/DataFormatters/ValueObjectPrinter.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Target/Target.h" + +#include "llvm/ADT/ArrayRef.h" + +using namespace lldb; +using namespace lldb_private; + +static const OptionDefinition g_option_table[] = { + {LLDB_OPT_SET_1, false, "dynamic-type", 'd', + OptionParser::eRequiredArgument, nullptr, GetDynamicValueTypes(), 0, + eArgTypeNone, "Show the object as its full dynamic type, not its static " + "type, if available."}, + {LLDB_OPT_SET_1, false, "synthetic-type", 'S', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, + "Show the object obeying its synthetic provider, if available."}, + {LLDB_OPT_SET_1, false, "depth", 'D', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeCount, "Set the max recurse depth when dumping " + "aggregate types (default is infinity)."}, + {LLDB_OPT_SET_1, false, "flat", 'F', OptionParser::eNoArgument, nullptr, + {}, 0, eArgTypeNone, "Display results in a flat format that uses " + "expression paths for each variable or member."}, + {LLDB_OPT_SET_1, false, "location", 'L', OptionParser::eNoArgument, nullptr, + {}, 0, eArgTypeNone, "Show variable location information."}, + {LLDB_OPT_SET_1, false, "object-description", 'O', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Display using a language-specific description API, if possible."}, + {LLDB_OPT_SET_1, false, "ptr-depth", 'P', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeCount, "The number of pointers to be traversed " + "when dumping values (default is zero)."}, + {LLDB_OPT_SET_1, false, "show-types", 'T', OptionParser::eNoArgument, + nullptr, {}, 0, eArgTypeNone, + "Show variable types when dumping values."}, + {LLDB_OPT_SET_1, false, "no-summary-depth", 'Y', + OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeCount, + "Set the depth at which omitting summary information stops (default is " + "1)."}, + {LLDB_OPT_SET_1, false, "raw-output", 'R', OptionParser::eNoArgument, + nullptr, {}, 0, eArgTypeNone, "Don't use formatting options."}, + {LLDB_OPT_SET_1, false, "show-all-children", 'A', OptionParser::eNoArgument, + nullptr, {}, 0, eArgTypeNone, + "Ignore the upper bound on the number of children to show."}, + {LLDB_OPT_SET_1, false, "validate", 'V', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeBoolean, "Show results of type validators."}, + {LLDB_OPT_SET_1, false, "element-count", 'Z', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, + "Treat the result of the expression as if its type is an array of this " + "many values."}}; + +llvm::ArrayRef<OptionDefinition> +OptionGroupValueObjectDisplay::GetDefinitions() { + return llvm::ArrayRef(g_option_table); +} + +Status OptionGroupValueObjectDisplay::SetOptionValue( + uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = g_option_table[option_idx].short_option; + bool success = false; + + switch (short_option) { + case 'd': { + int32_t result; + result = OptionArgParser::ToOptionEnum(option_arg, GetDynamicValueTypes(), + 2, error); + if (error.Success()) + use_dynamic = (lldb::DynamicValueType)result; + } break; + case 'T': + show_types = true; + break; + case 'L': + show_location = true; + break; + case 'F': + flat_output = true; + break; + case 'O': + use_objc = true; + break; + case 'R': + be_raw = true; + break; + case 'A': + ignore_cap = true; + break; + + case 'D': + if (option_arg.getAsInteger(0, max_depth)) { + max_depth = UINT32_MAX; + error.SetErrorStringWithFormat("invalid max depth '%s'", + option_arg.str().c_str()); + } else { + max_depth_is_default = false; + } + break; + + case 'Z': + if (option_arg.getAsInteger(0, elem_count)) { + elem_count = UINT32_MAX; + error.SetErrorStringWithFormat("invalid element count '%s'", + option_arg.str().c_str()); + } + break; + + case 'P': + if (option_arg.getAsInteger(0, ptr_depth)) { + ptr_depth = 0; + error.SetErrorStringWithFormat("invalid pointer depth '%s'", + option_arg.str().c_str()); + } + break; + + case 'Y': + if (option_arg.empty()) + no_summary_depth = 1; + else if (option_arg.getAsInteger(0, no_summary_depth)) { + no_summary_depth = 0; + error.SetErrorStringWithFormat("invalid pointer depth '%s'", + option_arg.str().c_str()); + } + break; + + case 'S': + use_synth = OptionArgParser::ToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid synthetic-type '%s'", + option_arg.str().c_str()); + break; + + case 'V': + run_validator = OptionArgParser::ToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid validate '%s'", + option_arg.str().c_str()); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; +} + +void OptionGroupValueObjectDisplay::OptionParsingStarting( + ExecutionContext *execution_context) { + // If these defaults change, be sure to modify AnyOptionWasSet(). + show_types = false; + no_summary_depth = 0; + show_location = false; + flat_output = false; + use_objc = false; + max_depth = UINT32_MAX; + max_depth_is_default = true; + ptr_depth = 0; + elem_count = 0; + use_synth = true; + be_raw = false; + ignore_cap = false; + run_validator = false; + + TargetSP target_sp = + execution_context ? execution_context->GetTargetSP() : TargetSP(); + if (target_sp) { + use_dynamic = target_sp->GetPreferDynamicValue(); + auto max_depth_config = target_sp->GetMaximumDepthOfChildrenToDisplay(); + max_depth = std::get<uint32_t>(max_depth_config); + max_depth_is_default = std::get<bool>(max_depth_config); + } else { + // If we don't have any targets, then dynamic values won't do us much good. + use_dynamic = lldb::eNoDynamicValues; + } +} + +DumpValueObjectOptions OptionGroupValueObjectDisplay::GetAsDumpOptions( + LanguageRuntimeDescriptionDisplayVerbosity lang_descr_verbosity, + lldb::Format format, lldb::TypeSummaryImplSP summary_sp) { + DumpValueObjectOptions options; + options.SetMaximumPointerDepth( + {DumpValueObjectOptions::PointerDepth::Mode::Always, ptr_depth}); + if (use_objc) + options.SetShowSummary(false); + else + options.SetOmitSummaryDepth(no_summary_depth); + options.SetMaximumDepth(max_depth, max_depth_is_default) + .SetShowTypes(show_types) + .SetShowLocation(show_location) + .SetUseObjectiveC(use_objc) + .SetUseDynamicType(use_dynamic) + .SetUseSyntheticValue(use_synth) + .SetFlatOutput(flat_output) + .SetIgnoreCap(ignore_cap) + .SetFormat(format) + .SetSummary(summary_sp); + + if (lang_descr_verbosity == + eLanguageRuntimeDescriptionDisplayVerbosityCompact) + options.SetHideRootType(use_objc).SetHideName(use_objc).SetHideValue( + use_objc); + + if (be_raw) + options.SetRawDisplay(); + + options.SetRunValidator(run_validator); + + options.SetElementCount(elem_count); + + return options; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupVariable.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupVariable.cpp new file mode 100644 index 000000000000..99644b3f423c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupVariable.cpp @@ -0,0 +1,141 @@ +//===-- OptionGroupVariable.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 "lldb/Interpreter/OptionGroupVariable.h" + +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Status.h" + +using namespace lldb; +using namespace lldb_private; + +// if you add any options here, remember to update the counters in +// OptionGroupVariable::GetNumDefinitions() +static constexpr OptionDefinition g_variable_options[] = { + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-args", 'a', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Omit function arguments."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-recognized-args", 't', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Omit recognized function arguments."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-locals", 'l', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Omit local variables."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "show-globals", 'g', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Show the current frame source file global and static variables."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "show-declaration", 'c', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Show variable declaration information (source file and line where the " + "variable was declared)."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "regex", 'r', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeRegularExpression, + "The <variable-name> argument for name lookups are regular expressions."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "scope", 's', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Show variable scope (argument, local, global, static)."}, + {LLDB_OPT_SET_1, false, "summary", 'y', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeName, + "Specify the summary that the variable output should use."}, + {LLDB_OPT_SET_2, false, "summary-string", 'z', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName, + "Specify a summary string to use to format the variable output."}, +}; + +static constexpr auto g_num_frame_options = 4; +static const auto g_variable_options_noframe = + llvm::ArrayRef<OptionDefinition>(g_variable_options) + .drop_front(g_num_frame_options); + +static Status ValidateNamedSummary(const char *str, void *) { + if (!str || !str[0]) + return Status("must specify a valid named summary"); + TypeSummaryImplSP summary_sp; + if (!DataVisualization::NamedSummaryFormats::GetSummaryFormat( + ConstString(str), summary_sp)) + return Status("must specify a valid named summary"); + return Status(); +} + +static Status ValidateSummaryString(const char *str, void *) { + if (!str || !str[0]) + return Status("must specify a non-empty summary string"); + return Status(); +} + +OptionGroupVariable::OptionGroupVariable(bool show_frame_options) + : include_frame_options(show_frame_options), show_args(false), + show_recognized_args(false), show_locals(false), show_globals(false), + use_regex(false), show_scope(false), show_decl(false), + summary(ValidateNamedSummary), summary_string(ValidateSummaryString) {} + +Status +OptionGroupVariable::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + llvm::ArrayRef<OptionDefinition> variable_options = + include_frame_options ? g_variable_options : g_variable_options_noframe; + const int short_option = variable_options[option_idx].short_option; + switch (short_option) { + case 'r': + use_regex = true; + break; + case 'a': + show_args = false; + break; + case 'l': + show_locals = false; + break; + case 'g': + show_globals = true; + break; + case 'c': + show_decl = true; + break; + case 's': + show_scope = true; + break; + case 't': + show_recognized_args = false; + break; + case 'y': + error = summary.SetCurrentValue(option_arg); + break; + case 'z': + error = summary_string.SetCurrentValue(option_arg); + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return error; +} + +void OptionGroupVariable::OptionParsingStarting( + ExecutionContext *execution_context) { + show_args = true; // Frame option only + show_recognized_args = true; // Frame option only + show_locals = true; // Frame option only + show_globals = false; // Frame option only + show_decl = false; + use_regex = false; + show_scope = false; + summary.Clear(); + summary_string.Clear(); +} + +llvm::ArrayRef<OptionDefinition> OptionGroupVariable::GetDefinitions() { + // Show the "--no-args", "--no-recognized-args", "--no-locals" and + // "--show-globals" options if we are showing frame specific options + return include_frame_options ? g_variable_options + : g_variable_options_noframe; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionGroupWatchpoint.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupWatchpoint.cpp new file mode 100644 index 000000000000..d1ae916cd74b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionGroupWatchpoint.cpp @@ -0,0 +1,113 @@ +//===-- OptionGroupWatchpoint.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 "lldb/Interpreter/OptionGroupWatchpoint.h" + +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Target/Language.h" +#include "lldb/lldb-enumerations.h" + +using namespace lldb; +using namespace lldb_private; + +static constexpr OptionEnumValueElement g_watch_type[] = { + { + OptionGroupWatchpoint::eWatchRead, + "read", + "Watch for read", + }, + { + OptionGroupWatchpoint::eWatchWrite, + "write", + "Watch for write", + }, + { + OptionGroupWatchpoint::eWatchModify, + "modify", + "Watch for modifications", + }, + { + OptionGroupWatchpoint::eWatchReadWrite, + "read_write", + "Watch for read/write", + }, +}; + +static constexpr OptionDefinition g_option_table[] = { + {LLDB_OPT_SET_1, false, "watch", 'w', OptionParser::eRequiredArgument, + nullptr, OptionEnumValues(g_watch_type), 0, eArgTypeWatchType, + "Specify the type of watching to perform."}, + {LLDB_OPT_SET_1, false, "size", 's', OptionParser::eRequiredArgument, + nullptr, {}, 0, eArgTypeByteSize, + "Number of bytes to use to watch a region."}, + {LLDB_OPT_SET_2, + false, + "language", + 'l', + OptionParser::eRequiredArgument, + nullptr, + {}, + 0, + eArgTypeLanguage, + "Language of expression to run"}}; + +Status +OptionGroupWatchpoint::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_arg, + ExecutionContext *execution_context) { + Status error; + const int short_option = g_option_table[option_idx].short_option; + switch (short_option) { + case 'l': { + language_type = Language::GetLanguageTypeFromString(option_arg); + if (language_type == eLanguageTypeUnknown) { + StreamString sstr; + sstr.Printf("Unknown language type: '%s' for expression. List of " + "supported languages:\n", + option_arg.str().c_str()); + Language::PrintSupportedLanguagesForExpressions(sstr, " ", "\n"); + error.SetErrorString(sstr.GetString()); + } + break; + } + case 'w': { + WatchType tmp_watch_type; + tmp_watch_type = (WatchType)OptionArgParser::ToOptionEnum( + option_arg, g_option_table[option_idx].enum_values, 0, error); + if (error.Success()) { + watch_type = tmp_watch_type; + watch_type_specified = true; + } + break; + } + case 's': + error = watch_size.SetValueFromString(option_arg); + if (watch_size.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid --size option value '%s'", + option_arg.str().c_str()); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; +} + +void OptionGroupWatchpoint::OptionParsingStarting( + ExecutionContext *execution_context) { + watch_type_specified = false; + watch_type = eWatchInvalid; + watch_size.Clear(); + language_type = eLanguageTypeUnknown; +} + +llvm::ArrayRef<OptionDefinition> OptionGroupWatchpoint::GetDefinitions() { + return llvm::ArrayRef(g_option_table); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValue.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValue.cpp new file mode 100644 index 000000000000..fe1d8829c5ad --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValue.cpp @@ -0,0 +1,643 @@ +//===-- OptionValue.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 "lldb/Interpreter/OptionValue.h" +#include "lldb/Interpreter/OptionValues.h" +#include "lldb/Utility/StringList.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +OptionValue::OptionValue(const OptionValue &other) { + std::lock_guard<std::mutex> lock(other.m_mutex); + + m_parent_wp = other.m_parent_wp; + m_callback = other.m_callback; + m_value_was_set = other.m_value_was_set; + +} + +OptionValue& OptionValue::operator=(const OptionValue &other) { + std::scoped_lock<std::mutex, std::mutex> lock(m_mutex, other.m_mutex); + + m_parent_wp = other.m_parent_wp; + m_callback = other.m_callback; + m_value_was_set = other.m_value_was_set; + + return *this; +} + +Status OptionValue::SetSubValue(const ExecutionContext *exe_ctx, + VarSetOperationType op, llvm::StringRef name, + llvm::StringRef value) { + Status error; + error.SetErrorString("SetSubValue is not supported"); + return error; +} + +OptionValueBoolean *OptionValue::GetAsBoolean() { + if (GetType() == OptionValue::eTypeBoolean) + return static_cast<OptionValueBoolean *>(this); + return nullptr; +} + +const OptionValueBoolean *OptionValue::GetAsBoolean() const { + if (GetType() == OptionValue::eTypeBoolean) + return static_cast<const OptionValueBoolean *>(this); + return nullptr; +} + +const OptionValueChar *OptionValue::GetAsChar() const { + if (GetType() == OptionValue::eTypeChar) + return static_cast<const OptionValueChar *>(this); + return nullptr; +} + +OptionValueChar *OptionValue::GetAsChar() { + if (GetType() == OptionValue::eTypeChar) + return static_cast<OptionValueChar *>(this); + return nullptr; +} + +OptionValueFileSpec *OptionValue::GetAsFileSpec() { + if (GetType() == OptionValue::eTypeFileSpec) + return static_cast<OptionValueFileSpec *>(this); + return nullptr; +} + +const OptionValueFileSpec *OptionValue::GetAsFileSpec() const { + if (GetType() == OptionValue::eTypeFileSpec) + return static_cast<const OptionValueFileSpec *>(this); + return nullptr; +} + +OptionValueFileSpecList *OptionValue::GetAsFileSpecList() { + if (GetType() == OptionValue::eTypeFileSpecList) + return static_cast<OptionValueFileSpecList *>(this); + return nullptr; +} + +const OptionValueFileSpecList *OptionValue::GetAsFileSpecList() const { + if (GetType() == OptionValue::eTypeFileSpecList) + return static_cast<const OptionValueFileSpecList *>(this); + return nullptr; +} + +OptionValueArch *OptionValue::GetAsArch() { + if (GetType() == OptionValue::eTypeArch) + return static_cast<OptionValueArch *>(this); + return nullptr; +} + +const OptionValueArch *OptionValue::GetAsArch() const { + if (GetType() == OptionValue::eTypeArch) + return static_cast<const OptionValueArch *>(this); + return nullptr; +} + +OptionValueArray *OptionValue::GetAsArray() { + if (GetType() == OptionValue::eTypeArray) + return static_cast<OptionValueArray *>(this); + return nullptr; +} + +const OptionValueArray *OptionValue::GetAsArray() const { + if (GetType() == OptionValue::eTypeArray) + return static_cast<const OptionValueArray *>(this); + return nullptr; +} + +OptionValueArgs *OptionValue::GetAsArgs() { + if (GetType() == OptionValue::eTypeArgs) + return static_cast<OptionValueArgs *>(this); + return nullptr; +} + +const OptionValueArgs *OptionValue::GetAsArgs() const { + if (GetType() == OptionValue::eTypeArgs) + return static_cast<const OptionValueArgs *>(this); + return nullptr; +} + +OptionValueDictionary *OptionValue::GetAsDictionary() { + if (GetType() == OptionValue::eTypeDictionary) + return static_cast<OptionValueDictionary *>(this); + return nullptr; +} + +const OptionValueDictionary *OptionValue::GetAsDictionary() const { + if (GetType() == OptionValue::eTypeDictionary) + return static_cast<const OptionValueDictionary *>(this); + return nullptr; +} + +OptionValueEnumeration *OptionValue::GetAsEnumeration() { + if (GetType() == OptionValue::eTypeEnum) + return static_cast<OptionValueEnumeration *>(this); + return nullptr; +} + +const OptionValueEnumeration *OptionValue::GetAsEnumeration() const { + if (GetType() == OptionValue::eTypeEnum) + return static_cast<const OptionValueEnumeration *>(this); + return nullptr; +} + +OptionValueFormat *OptionValue::GetAsFormat() { + if (GetType() == OptionValue::eTypeFormat) + return static_cast<OptionValueFormat *>(this); + return nullptr; +} + +const OptionValueFormat *OptionValue::GetAsFormat() const { + if (GetType() == OptionValue::eTypeFormat) + return static_cast<const OptionValueFormat *>(this); + return nullptr; +} + +OptionValueLanguage *OptionValue::GetAsLanguage() { + if (GetType() == OptionValue::eTypeLanguage) + return static_cast<OptionValueLanguage *>(this); + return nullptr; +} + +const OptionValueLanguage *OptionValue::GetAsLanguage() const { + if (GetType() == OptionValue::eTypeLanguage) + return static_cast<const OptionValueLanguage *>(this); + return nullptr; +} + +OptionValueFormatEntity *OptionValue::GetAsFormatEntity() { + if (GetType() == OptionValue::eTypeFormatEntity) + return static_cast<OptionValueFormatEntity *>(this); + return nullptr; +} + +const OptionValueFormatEntity *OptionValue::GetAsFormatEntity() const { + if (GetType() == OptionValue::eTypeFormatEntity) + return static_cast<const OptionValueFormatEntity *>(this); + return nullptr; +} + +OptionValuePathMappings *OptionValue::GetAsPathMappings() { + if (GetType() == OptionValue::eTypePathMap) + return static_cast<OptionValuePathMappings *>(this); + return nullptr; +} + +const OptionValuePathMappings *OptionValue::GetAsPathMappings() const { + if (GetType() == OptionValue::eTypePathMap) + return static_cast<const OptionValuePathMappings *>(this); + return nullptr; +} + +OptionValueProperties *OptionValue::GetAsProperties() { + if (GetType() == OptionValue::eTypeProperties) + return static_cast<OptionValueProperties *>(this); + return nullptr; +} + +const OptionValueProperties *OptionValue::GetAsProperties() const { + if (GetType() == OptionValue::eTypeProperties) + return static_cast<const OptionValueProperties *>(this); + return nullptr; +} + +OptionValueRegex *OptionValue::GetAsRegex() { + if (GetType() == OptionValue::eTypeRegex) + return static_cast<OptionValueRegex *>(this); + return nullptr; +} + +const OptionValueRegex *OptionValue::GetAsRegex() const { + if (GetType() == OptionValue::eTypeRegex) + return static_cast<const OptionValueRegex *>(this); + return nullptr; +} + +OptionValueSInt64 *OptionValue::GetAsSInt64() { + if (GetType() == OptionValue::eTypeSInt64) + return static_cast<OptionValueSInt64 *>(this); + return nullptr; +} + +const OptionValueSInt64 *OptionValue::GetAsSInt64() const { + if (GetType() == OptionValue::eTypeSInt64) + return static_cast<const OptionValueSInt64 *>(this); + return nullptr; +} + +OptionValueString *OptionValue::GetAsString() { + if (GetType() == OptionValue::eTypeString) + return static_cast<OptionValueString *>(this); + return nullptr; +} + +const OptionValueString *OptionValue::GetAsString() const { + if (GetType() == OptionValue::eTypeString) + return static_cast<const OptionValueString *>(this); + return nullptr; +} + +OptionValueUInt64 *OptionValue::GetAsUInt64() { + if (GetType() == OptionValue::eTypeUInt64) + return static_cast<OptionValueUInt64 *>(this); + return nullptr; +} + +const OptionValueUInt64 *OptionValue::GetAsUInt64() const { + if (GetType() == OptionValue::eTypeUInt64) + return static_cast<const OptionValueUInt64 *>(this); + return nullptr; +} + +OptionValueUUID *OptionValue::GetAsUUID() { + if (GetType() == OptionValue::eTypeUUID) + return static_cast<OptionValueUUID *>(this); + return nullptr; +} + +const OptionValueUUID *OptionValue::GetAsUUID() const { + if (GetType() == OptionValue::eTypeUUID) + return static_cast<const OptionValueUUID *>(this); + return nullptr; +} + +std::optional<bool> OptionValue::GetBooleanValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueBoolean *option_value = GetAsBoolean()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetBooleanValue(bool new_value) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueBoolean *option_value = GetAsBoolean()) { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +std::optional<char> OptionValue::GetCharValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueChar *option_value = GetAsChar()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetCharValue(char new_value) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueChar *option_value = GetAsChar()) { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +std::optional<int64_t> OptionValue::GetEnumerationValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueEnumeration *option_value = GetAsEnumeration()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetEnumerationValue(int64_t value) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueEnumeration *option_value = GetAsEnumeration()) { + option_value->SetCurrentValue(value); + return true; + } + return false; +} + +std::optional<FileSpec> OptionValue::GetFileSpecValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueFileSpec *option_value = GetAsFileSpec()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetFileSpecValue(FileSpec file_spec) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueFileSpec *option_value = GetAsFileSpec()) { + option_value->SetCurrentValue(file_spec, false); + return true; + } + return false; +} + +bool OptionValue::AppendFileSpecValue(FileSpec file_spec) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueFileSpecList *option_value = GetAsFileSpecList()) { + option_value->AppendCurrentValue(file_spec); + return true; + } + return false; +} + +std::optional<FileSpecList> OptionValue::GetFileSpecListValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueFileSpecList *option_value = GetAsFileSpecList()) + return option_value->GetCurrentValue(); + return {}; +} + +std::optional<lldb::Format> OptionValue::GetFormatValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueFormat *option_value = GetAsFormat()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetFormatValue(lldb::Format new_value) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueFormat *option_value = GetAsFormat()) { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +std::optional<lldb::LanguageType> OptionValue::GetLanguageValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueLanguage *option_value = GetAsLanguage()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetLanguageValue(lldb::LanguageType new_language) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueLanguage *option_value = GetAsLanguage()) { + option_value->SetCurrentValue(new_language); + return true; + } + return false; +} + +const FormatEntity::Entry *OptionValue::GetFormatEntity() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueFormatEntity *option_value = GetAsFormatEntity()) + return &option_value->GetCurrentValue(); + return nullptr; +} + +const RegularExpression *OptionValue::GetRegexValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueRegex *option_value = GetAsRegex()) + return option_value->GetCurrentValue(); + return nullptr; +} + +std::optional<int64_t> OptionValue::GetSInt64Value() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueSInt64 *option_value = GetAsSInt64()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetSInt64Value(int64_t new_value) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueSInt64 *option_value = GetAsSInt64()) { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +std::optional<llvm::StringRef> OptionValue::GetStringValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueString *option_value = GetAsString()) + return option_value->GetCurrentValueAsRef(); + return {}; +} + +bool OptionValue::SetStringValue(llvm::StringRef new_value) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueString *option_value = GetAsString()) { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +std::optional<uint64_t> OptionValue::GetUInt64Value() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueUInt64 *option_value = GetAsUInt64()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetUInt64Value(uint64_t new_value) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueUInt64 *option_value = GetAsUInt64()) { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + +std::optional<UUID> OptionValue::GetUUIDValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueUUID *option_value = GetAsUUID()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetUUIDValue(const UUID &uuid) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueUUID *option_value = GetAsUUID()) { + option_value->SetCurrentValue(uuid); + return true; + } + return false; +} + +std::optional<ArchSpec> OptionValue::GetArchSpecValue() const { + std::lock_guard<std::mutex> lock(m_mutex); + if (const OptionValueArch *option_value = GetAsArch()) + return option_value->GetCurrentValue(); + return {}; +} + +bool OptionValue::SetArchSpecValue(ArchSpec arch_spec) { + std::lock_guard<std::mutex> lock(m_mutex); + if (OptionValueArch *option_value = GetAsArch()) { + option_value->SetCurrentValue(arch_spec, false); + return true; + } + return false; +} + +const char *OptionValue::GetBuiltinTypeAsCString(Type t) { + switch (t) { + case eTypeInvalid: + return "invalid"; + case eTypeArch: + return "arch"; + case eTypeArgs: + return "arguments"; + case eTypeArray: + return "array"; + case eTypeBoolean: + return "boolean"; + case eTypeChar: + return "char"; + case eTypeDictionary: + return "dictionary"; + case eTypeEnum: + return "enum"; + case eTypeFileLineColumn: + return "file:line:column specifier"; + case eTypeFileSpec: + return "file"; + case eTypeFileSpecList: + return "file-list"; + case eTypeFormat: + return "format"; + case eTypeFormatEntity: + return "format-string"; + case eTypeLanguage: + return "language"; + case eTypePathMap: + return "path-map"; + case eTypeProperties: + return "properties"; + case eTypeRegex: + return "regex"; + case eTypeSInt64: + return "int"; + case eTypeString: + return "string"; + case eTypeUInt64: + return "unsigned"; + case eTypeUUID: + return "uuid"; + } + return nullptr; +} + +lldb::OptionValueSP OptionValue::CreateValueFromCStringForTypeMask( + const char *value_cstr, uint32_t type_mask, Status &error) { + // If only 1 bit is set in the type mask for a dictionary or array then we + // know how to decode a value from a cstring + lldb::OptionValueSP value_sp; + switch (type_mask) { + case 1u << eTypeArch: + value_sp = std::make_shared<OptionValueArch>(); + break; + case 1u << eTypeBoolean: + value_sp = std::make_shared<OptionValueBoolean>(false); + break; + case 1u << eTypeChar: + value_sp = std::make_shared<OptionValueChar>('\0'); + break; + case 1u << eTypeFileSpec: + value_sp = std::make_shared<OptionValueFileSpec>(); + break; + case 1u << eTypeFormat: + value_sp = std::make_shared<OptionValueFormat>(eFormatInvalid); + break; + case 1u << eTypeFormatEntity: + value_sp = std::make_shared<OptionValueFormatEntity>(nullptr); + break; + case 1u << eTypeLanguage: + value_sp = std::make_shared<OptionValueLanguage>(eLanguageTypeUnknown); + break; + case 1u << eTypeSInt64: + value_sp = std::make_shared<OptionValueSInt64>(); + break; + case 1u << eTypeString: + value_sp = std::make_shared<OptionValueString>(); + break; + case 1u << eTypeUInt64: + value_sp = std::make_shared<OptionValueUInt64>(); + break; + case 1u << eTypeUUID: + value_sp = std::make_shared<OptionValueUUID>(); + break; + } + + if (value_sp) + error = value_sp->SetValueFromString(value_cstr, eVarSetOperationAssign); + else + error.SetErrorString("unsupported type mask"); + return value_sp; +} + +bool OptionValue::DumpQualifiedName(Stream &strm) const { + bool dumped_something = false; + lldb::OptionValueSP m_parent_sp(m_parent_wp.lock()); + if (m_parent_sp) { + if (m_parent_sp->DumpQualifiedName(strm)) + dumped_something = true; + } + llvm::StringRef name(GetName()); + if (!name.empty()) { + if (dumped_something) + strm.PutChar('.'); + else + dumped_something = true; + strm << name; + } + return dumped_something; +} + +OptionValueSP OptionValue::DeepCopy(const OptionValueSP &new_parent) const { + auto clone = Clone(); + clone->SetParent(new_parent); + return clone; +} + +void OptionValue::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) {} + +Status OptionValue::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationReplace: + error.SetErrorStringWithFormat( + "%s objects do not support the 'replace' operation", + GetTypeAsCString()); + break; + case eVarSetOperationInsertBefore: + error.SetErrorStringWithFormat( + "%s objects do not support the 'insert-before' operation", + GetTypeAsCString()); + break; + case eVarSetOperationInsertAfter: + error.SetErrorStringWithFormat( + "%s objects do not support the 'insert-after' operation", + GetTypeAsCString()); + break; + case eVarSetOperationRemove: + error.SetErrorStringWithFormat( + "%s objects do not support the 'remove' operation", GetTypeAsCString()); + break; + case eVarSetOperationAppend: + error.SetErrorStringWithFormat( + "%s objects do not support the 'append' operation", GetTypeAsCString()); + break; + case eVarSetOperationClear: + error.SetErrorStringWithFormat( + "%s objects do not support the 'clear' operation", GetTypeAsCString()); + break; + case eVarSetOperationAssign: + error.SetErrorStringWithFormat( + "%s objects do not support the 'assign' operation", GetTypeAsCString()); + break; + case eVarSetOperationInvalid: + error.SetErrorStringWithFormat("invalid operation performed on a %s object", + GetTypeAsCString()); + break; + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueArch.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArch.cpp new file mode 100644 index 000000000000..71a3627fbe5e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArch.cpp @@ -0,0 +1,71 @@ +//===-- OptionValueArch.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 "lldb/Interpreter/OptionValueArch.h" + +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueArch::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + + if (m_current_value.IsValid()) { + const char *arch_name = m_current_value.GetArchitectureName(); + if (arch_name) + strm.PutCString(arch_name); + } + } +} + +Status OptionValueArch::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + std::string value_str = value.trim().str(); + if (m_current_value.SetTriple(value_str.c_str())) { + m_value_was_set = true; + NotifyValueChanged(); + } else + error.SetErrorStringWithFormat("unsupported architecture '%s'", + value_str.c_str()); + break; + } + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} + +void OptionValueArch::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) { + lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( + interpreter, lldb::eArchitectureCompletion, request, nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueArgs.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArgs.cpp new file mode 100644 index 000000000000..963651640539 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArgs.cpp @@ -0,0 +1,21 @@ +//===-- OptionValueArgs.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 "lldb/Interpreter/OptionValueArgs.h" + +#include "lldb/Utility/Args.h" + +using namespace lldb; +using namespace lldb_private; + +size_t OptionValueArgs::GetArgs(Args &args) const { + args.Clear(); + for (const auto &value : m_values) + args.AppendArgument(value->GetValueAs<llvm::StringRef>().value_or("")); + return args.GetArgumentCount(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueArray.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArray.cpp new file mode 100644 index 000000000000..08b5f86202d3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueArray.cpp @@ -0,0 +1,320 @@ +//===-- OptionValueArray.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 "lldb/Interpreter/OptionValueArray.h" + +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + const Type array_element_type = ConvertTypeMaskToType(m_type_mask); + if (dump_mask & eDumpOptionType) { + if ((GetType() == eTypeArray) && (m_type_mask != eTypeInvalid)) + strm.Printf("(%s of %ss)", GetTypeAsCString(), + GetBuiltinTypeAsCString(array_element_type)); + else + strm.Printf("(%s)", GetTypeAsCString()); + } + if (dump_mask & eDumpOptionValue) { + const bool one_line = dump_mask & eDumpOptionCommand; + const uint32_t size = m_values.size(); + if (dump_mask & eDumpOptionType) + strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : ""); + if (!one_line) + strm.IndentMore(); + for (uint32_t i = 0; i < size; ++i) { + if (!one_line) { + strm.Indent(); + strm.Printf("[%u]: ", i); + } + const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; + switch (array_element_type) { + default: + case eTypeArray: + case eTypeDictionary: + case eTypeProperties: + case eTypeFileSpecList: + case eTypePathMap: + m_values[i]->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options); + break; + + case eTypeBoolean: + case eTypeChar: + case eTypeEnum: + case eTypeFileSpec: + case eTypeFileLineColumn: + case eTypeFormat: + case eTypeSInt64: + case eTypeString: + case eTypeUInt64: + case eTypeUUID: + // No need to show the type for dictionaries of simple items + m_values[i]->DumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | + extra_dump_options); + break; + } + + if (!one_line) { + if (i < (size - 1)) + strm.EOL(); + } else { + strm << ' '; + } + } + if (!one_line) + strm.IndentLess(); + } +} + +llvm::json::Value OptionValueArray::ToJSON(const ExecutionContext *exe_ctx) { + llvm::json::Array json_array; + const uint32_t size = m_values.size(); + for (uint32_t i = 0; i < size; ++i) + json_array.emplace_back(m_values[i]->ToJSON(exe_ctx)); + return json_array; +} + +Status OptionValueArray::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Args args(value.str()); + Status error = SetArgs(args, op); + if (error.Success()) + NotifyValueChanged(); + return error; +} + +lldb::OptionValueSP +OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx, + llvm::StringRef name, Status &error) const { + if (name.empty() || name.front() != '[') { + error.SetErrorStringWithFormat( + "invalid value path '%s', %s values only support '[<index>]' subvalues " + "where <index> is a positive or negative array index", + name.str().c_str(), GetTypeAsCString()); + return nullptr; + } + + name = name.drop_front(); + llvm::StringRef index, sub_value; + std::tie(index, sub_value) = name.split(']'); + if (index.size() == name.size()) { + // Couldn't find a closing bracket + return nullptr; + } + + const size_t array_count = m_values.size(); + int32_t idx = 0; + if (index.getAsInteger(0, idx)) + return nullptr; + + uint32_t new_idx = UINT32_MAX; + if (idx < 0) { + // Access from the end of the array if the index is negative + new_idx = array_count - idx; + } else { + // Just a standard index + new_idx = idx; + } + + if (new_idx < array_count) { + if (m_values[new_idx]) { + if (!sub_value.empty()) + return m_values[new_idx]->GetSubValue(exe_ctx, sub_value, error); + else + return m_values[new_idx]; + } + } else { + if (array_count == 0) + error.SetErrorStringWithFormat( + "index %i is not valid for an empty array", idx); + else if (idx > 0) + error.SetErrorStringWithFormat( + "index %i out of range, valid values are 0 through %" PRIu64, + idx, (uint64_t)(array_count - 1)); + else + error.SetErrorStringWithFormat("negative index %i out of range, " + "valid values are -1 through " + "-%" PRIu64, + idx, (uint64_t)array_count); + } + return OptionValueSP(); +} + +size_t OptionValueArray::GetArgs(Args &args) const { + args.Clear(); + const uint32_t size = m_values.size(); + for (uint32_t i = 0; i < size; ++i) { + auto string_value = m_values[i]->GetValueAs<llvm::StringRef>(); + if (string_value) + args.AppendArgument(*string_value); + } + + return args.GetArgumentCount(); +} + +Status OptionValueArray::SetArgs(const Args &args, VarSetOperationType op) { + Status error; + const size_t argc = args.GetArgumentCount(); + switch (op) { + case eVarSetOperationInvalid: + error.SetErrorString("unsupported operation"); + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + if (argc > 1) { + uint32_t idx; + const uint32_t count = GetSize(); + if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { + error.SetErrorStringWithFormat( + "invalid insert array index %s, index must be 0 through %u", + args.GetArgumentAtIndex(0), count); + } else { + if (op == eVarSetOperationInsertAfter) + ++idx; + for (size_t i = 1; i < argc; ++i, ++idx) { + lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( + args.GetArgumentAtIndex(i), m_type_mask, error)); + if (value_sp) { + if (error.Fail()) + return error; + if (idx >= m_values.size()) + m_values.push_back(value_sp); + else + m_values.insert(m_values.begin() + idx, value_sp); + } else { + error.SetErrorString( + "array of complex types must subclass OptionValueArray"); + return error; + } + } + } + } else { + error.SetErrorString("insert operation takes an array index followed by " + "one or more values"); + } + break; + + case eVarSetOperationRemove: + if (argc > 0) { + const uint32_t size = m_values.size(); + std::vector<int> remove_indexes; + bool all_indexes_valid = true; + size_t i; + for (i = 0; i < argc; ++i) { + size_t idx; + if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx >= size) { + all_indexes_valid = false; + break; + } else + remove_indexes.push_back(idx); + } + + if (all_indexes_valid) { + size_t num_remove_indexes = remove_indexes.size(); + if (num_remove_indexes) { + // Sort and then erase in reverse so indexes are always valid + if (num_remove_indexes > 1) { + llvm::sort(remove_indexes); + for (std::vector<int>::const_reverse_iterator + pos = remove_indexes.rbegin(), + end = remove_indexes.rend(); + pos != end; ++pos) { + m_values.erase(m_values.begin() + *pos); + } + } else { + // Only one index + m_values.erase(m_values.begin() + remove_indexes.front()); + } + } + } else { + error.SetErrorStringWithFormat( + "invalid array index '%s', aborting remove operation", + args.GetArgumentAtIndex(i)); + } + } else { + error.SetErrorString("remove operation takes one or more array indices"); + } + break; + + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + if (argc > 1) { + uint32_t idx; + const uint32_t count = GetSize(); + if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { + error.SetErrorStringWithFormat( + "invalid replace array index %s, index must be 0 through %u", + args.GetArgumentAtIndex(0), count); + } else { + for (size_t i = 1; i < argc; ++i, ++idx) { + lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( + args.GetArgumentAtIndex(i), m_type_mask, error)); + if (value_sp) { + if (error.Fail()) + return error; + if (idx < count) + m_values[idx] = value_sp; + else + m_values.push_back(value_sp); + } else { + error.SetErrorString( + "array of complex types must subclass OptionValueArray"); + return error; + } + } + } + } else { + error.SetErrorString("replace operation takes an array index followed by " + "one or more values"); + } + break; + + case eVarSetOperationAssign: + m_values.clear(); + // Fall through to append case + [[fallthrough]]; + case eVarSetOperationAppend: + for (size_t i = 0; i < argc; ++i) { + lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( + args.GetArgumentAtIndex(i), m_type_mask, error)); + if (value_sp) { + if (error.Fail()) + return error; + m_value_was_set = true; + AppendValue(value_sp); + } else { + error.SetErrorString( + "array of complex types must subclass OptionValueArray"); + } + } + break; + } + return error; +} + +OptionValueSP +OptionValueArray::DeepCopy(const OptionValueSP &new_parent) const { + auto copy_sp = OptionValue::DeepCopy(new_parent); + // copy_sp->GetAsArray cannot be used here as it doesn't work for derived + // types that override GetType returning a different value. + auto *array_value_ptr = static_cast<OptionValueArray *>(copy_sp.get()); + lldbassert(array_value_ptr); + + for (auto &value : array_value_ptr->m_values) + value = value->DeepCopy(copy_sp); + + return copy_sp; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueBoolean.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueBoolean.cpp new file mode 100644 index 000000000000..4ac2ed5efe75 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueBoolean.cpp @@ -0,0 +1,83 @@ +//===-- OptionValueBoolean.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 "lldb/Interpreter/OptionValueBoolean.h" + +#include "lldb/Host/PosixApi.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueBoolean::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + // if (dump_mask & eDumpOptionName) + // DumpQualifiedName (strm); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + strm.PutCString(m_current_value ? "true" : "false"); + } +} + +Status OptionValueBoolean::SetValueFromString(llvm::StringRef value_str, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + bool success = false; + bool value = OptionArgParser::ToBoolean(value_str, false, &success); + if (success) { + m_value_was_set = true; + m_current_value = value; + NotifyValueChanged(); + } else { + if (value_str.size() == 0) + error.SetErrorString("invalid boolean string value <empty>"); + else + error.SetErrorStringWithFormat("invalid boolean string value: '%s'", + value_str.str().c_str()); + } + } break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value_str, op); + break; + } + return error; +} + +void OptionValueBoolean::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) { + llvm::StringRef autocomplete_entries[] = {"true", "false", "on", "off", + "yes", "no", "1", "0"}; + + auto entries = llvm::ArrayRef(autocomplete_entries); + + // only suggest "true" or "false" by default + if (request.GetCursorArgumentPrefix().empty()) + entries = entries.take_front(2); + + for (auto entry : entries) + request.TryCompleteCurrentArg(entry); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueChar.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueChar.cpp new file mode 100644 index 000000000000..7fe72c9aa221 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueChar.cpp @@ -0,0 +1,59 @@ +//===-- OptionValueChar.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 "lldb/Interpreter/OptionValueChar.h" + +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueChar::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + if (m_current_value != '\0') + strm.PutChar(m_current_value); + else + strm.PutCString("(null)"); + } +} + +Status OptionValueChar::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + bool success = false; + char char_value = OptionArgParser::ToChar(value, '\0', &success); + if (success) { + m_current_value = char_value; + m_value_was_set = true; + } else + error.SetErrorStringWithFormat("'%s' cannot be longer than 1 character", + value.str().c_str()); + } break; + + default: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueDictionary.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueDictionary.cpp new file mode 100644 index 000000000000..c7d501fd4521 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueDictionary.cpp @@ -0,0 +1,347 @@ +//===-- OptionValueDictionary.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 "lldb/Interpreter/OptionValueDictionary.h" + +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/OptionValueEnumeration.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/State.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + const Type dict_type = ConvertTypeMaskToType(m_type_mask); + if (dump_mask & eDumpOptionType) { + if (m_type_mask != eTypeInvalid) + strm.Printf("(%s of %ss)", GetTypeAsCString(), + GetBuiltinTypeAsCString(dict_type)); + else + strm.Printf("(%s)", GetTypeAsCString()); + } + if (dump_mask & eDumpOptionValue) { + const bool one_line = dump_mask & eDumpOptionCommand; + if (dump_mask & eDumpOptionType) + strm.PutCString(" ="); + + if (!one_line) + strm.IndentMore(); + + // m_values is not guaranteed to be sorted alphabetically, so for + // consistentcy we will sort them here before dumping + std::map<llvm::StringRef, OptionValue *> sorted_values; + for (const auto &value : m_values) { + sorted_values[value.first()] = value.second.get(); + } + for (const auto &value : sorted_values) { + OptionValue *option_value = value.second; + + if (one_line) + strm << ' '; + else + strm.EOL(); + + strm.Indent(value.first); + + const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; + switch (dict_type) { + default: + case eTypeArray: + case eTypeDictionary: + case eTypeProperties: + case eTypeFileSpecList: + case eTypePathMap: + strm.PutChar(' '); + option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options); + break; + + case eTypeBoolean: + case eTypeChar: + case eTypeEnum: + case eTypeFileLineColumn: + case eTypeFileSpec: + case eTypeFormat: + case eTypeSInt64: + case eTypeString: + case eTypeUInt64: + case eTypeUUID: + // No need to show the type for dictionaries of simple items + strm.PutCString("="); + option_value->DumpValue(exe_ctx, strm, + (dump_mask & (~eDumpOptionType)) | + extra_dump_options); + break; + } + } + if (!one_line) + strm.IndentLess(); + } +} + +llvm::json::Value +OptionValueDictionary::ToJSON(const ExecutionContext *exe_ctx) { + llvm::json::Object dict; + for (const auto &value : m_values) { + dict.try_emplace(value.first(), value.second->ToJSON(exe_ctx)); + } + return dict; +} + +size_t OptionValueDictionary::GetArgs(Args &args) const { + args.Clear(); + for (const auto &value : m_values) { + StreamString strm; + strm.Printf("%s=", value.first().data()); + value.second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw); + args.AppendArgument(strm.GetString()); + } + return args.GetArgumentCount(); +} + +Status OptionValueDictionary::SetArgs(const Args &args, + VarSetOperationType op) { + Status error; + const size_t argc = args.GetArgumentCount(); + switch (op) { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationAppend: + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (argc == 0) { + error.SetErrorString( + "assign operation takes one or more key=value arguments"); + return error; + } + for (const auto &entry : args) { + if (entry.ref().empty()) { + error.SetErrorString("empty argument"); + return error; + } + if (!entry.ref().contains('=')) { + error.SetErrorString( + "assign operation takes one or more key=value arguments"); + return error; + } + + llvm::StringRef key, value; + std::tie(key, value) = entry.ref().split('='); + bool key_valid = false; + if (key.empty()) { + error.SetErrorString("empty dictionary key"); + return error; + } + + if (key.front() == '[') { + // Key name starts with '[', so the key value must be in single or + // double quotes like: ['<key>'] ["<key>"] + if ((key.size() > 2) && (key.back() == ']')) { + // Strip leading '[' and trailing ']' + key = key.substr(1, key.size() - 2); + const char quote_char = key.front(); + if ((quote_char == '\'') || (quote_char == '"')) { + if ((key.size() > 2) && (key.back() == quote_char)) { + // Strip the quotes + key = key.substr(1, key.size() - 2); + key_valid = true; + } + } else { + // square brackets, no quotes + key_valid = true; + } + } + } else { + // No square brackets or quotes + key_valid = true; + } + if (!key_valid) { + error.SetErrorStringWithFormat( + "invalid key \"%s\", the key must be a bare string or " + "surrounded by brackets with optional quotes: [<key>] or " + "['<key>'] or [\"<key>\"]", + key.str().c_str()); + return error; + } + + if (m_type_mask == 1u << eTypeEnum) { + auto enum_value = + std::make_shared<OptionValueEnumeration>(m_enum_values, 0); + error = enum_value->SetValueFromString(value); + if (error.Fail()) + return error; + m_value_was_set = true; + SetValueForKey(key, enum_value, true); + } else { + lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( + value.str().c_str(), m_type_mask, error)); + if (value_sp) { + if (error.Fail()) + return error; + m_value_was_set = true; + SetValueForKey(key, value_sp, true); + } else { + error.SetErrorString("dictionaries that can contain multiple types " + "must subclass OptionValueArray"); + } + } + } + break; + + case eVarSetOperationRemove: + if (argc > 0) { + for (size_t i = 0; i < argc; ++i) { + llvm::StringRef key(args.GetArgumentAtIndex(i)); + if (!DeleteValueForKey(key)) { + error.SetErrorStringWithFormat( + "no value found named '%s', aborting remove operation", + key.data()); + break; + } + } + } else { + error.SetErrorString("remove operation takes one or more key arguments"); + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(llvm::StringRef(), op); + break; + } + return error; +} + +Status OptionValueDictionary::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Args args(value.str()); + Status error = SetArgs(args, op); + if (error.Success()) + NotifyValueChanged(); + return error; +} + +lldb::OptionValueSP +OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx, + llvm::StringRef name, Status &error) const { + lldb::OptionValueSP value_sp; + if (name.empty()) + return nullptr; + + llvm::StringRef left, temp; + std::tie(left, temp) = name.split('['); + if (left.size() == name.size()) { + error.SetErrorStringWithFormat("invalid value path '%s', %s values only " + "support '[<key>]' subvalues where <key> " + "a string value optionally delimited by " + "single or double quotes", + name.str().c_str(), GetTypeAsCString()); + return nullptr; + } + assert(!temp.empty()); + + llvm::StringRef key, quote_char; + + if (temp[0] == '\"' || temp[0] == '\'') { + quote_char = temp.take_front(); + temp = temp.drop_front(); + } + + llvm::StringRef sub_name; + std::tie(key, sub_name) = temp.split(']'); + + if (!key.consume_back(quote_char) || key.empty()) { + error.SetErrorStringWithFormat("invalid value path '%s', " + "key names must be formatted as ['<key>'] where <key> " + "is a string that doesn't contain quotes and the quote" + " char is optional", name.str().c_str()); + return nullptr; + } + + value_sp = GetValueForKey(key); + if (!value_sp) { + error.SetErrorStringWithFormat( + "dictionary does not contain a value for the key name '%s'", + key.str().c_str()); + return nullptr; + } + + if (sub_name.empty()) + return value_sp; + return value_sp->GetSubValue(exe_ctx, sub_name, error); +} + +Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx, + VarSetOperationType op, + llvm::StringRef name, + llvm::StringRef value) { + Status error; + lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, error)); + if (value_sp) + error = value_sp->SetValueFromString(value, op); + else { + if (error.AsCString() == nullptr) + error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str()); + } + return error; +} + +lldb::OptionValueSP +OptionValueDictionary::GetValueForKey(llvm::StringRef key) const { + lldb::OptionValueSP value_sp; + auto pos = m_values.find(key); + if (pos != m_values.end()) + value_sp = pos->second; + return value_sp; +} + +bool OptionValueDictionary::SetValueForKey(llvm::StringRef key, + const lldb::OptionValueSP &value_sp, + bool can_replace) { + // Make sure the value_sp object is allowed to contain values of the type + // passed in... + if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) { + if (!can_replace) { + auto pos = m_values.find(key); + if (pos != m_values.end()) + return false; + } + m_values[key] = value_sp; + return true; + } + return false; +} + +bool OptionValueDictionary::DeleteValueForKey(llvm::StringRef key) { + auto pos = m_values.find(key); + if (pos != m_values.end()) { + m_values.erase(pos); + return true; + } + return false; +} + +OptionValueSP +OptionValueDictionary::DeepCopy(const OptionValueSP &new_parent) const { + auto copy_sp = OptionValue::DeepCopy(new_parent); + // copy_sp->GetAsDictionary cannot be used here as it doesn't work for derived + // types that override GetType returning a different value. + auto *dict_value_ptr = static_cast<OptionValueDictionary *>(copy_sp.get()); + lldbassert(dict_value_ptr); + + for (auto &value : dict_value_ptr->m_values) + value.second = value.second->DeepCopy(copy_sp); + + return copy_sp; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueEnumeration.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueEnumeration.cpp new file mode 100644 index 000000000000..f75519c577c5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueEnumeration.cpp @@ -0,0 +1,110 @@ +//===-- OptionValueEnumeration.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 "lldb/Interpreter/OptionValueEnumeration.h" + +#include "lldb/Utility/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +OptionValueEnumeration::OptionValueEnumeration( + const OptionEnumValues &enumerators, enum_type value) + : m_current_value(value), m_default_value(value) { + SetEnumerations(enumerators); +} + +void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + const size_t count = m_enumerations.GetSize(); + for (size_t i = 0; i < count; ++i) { + if (m_enumerations.GetValueAtIndexUnchecked(i).value == m_current_value) { + strm.PutCString(m_enumerations.GetCStringAtIndex(i).GetStringRef()); + return; + } + } + strm.Printf("%" PRIu64, (uint64_t)m_current_value); + } +} + +Status OptionValueEnumeration::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + ConstString const_enumerator_name(value.trim()); + const EnumerationMapEntry *enumerator_entry = + m_enumerations.FindFirstValueForName(const_enumerator_name); + if (enumerator_entry) { + m_current_value = enumerator_entry->value.value; + NotifyValueChanged(); + } else { + StreamString error_strm; + error_strm.Printf("invalid enumeration value '%s'", value.str().c_str()); + const size_t count = m_enumerations.GetSize(); + if (count) { + error_strm.Printf(", valid values are: %s", + m_enumerations.GetCStringAtIndex(0).GetCString()); + for (size_t i = 1; i < count; ++i) { + error_strm.Printf(", %s", + m_enumerations.GetCStringAtIndex(i).GetCString()); + } + } + error.SetErrorString(error_strm.GetString()); + } + break; + } + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} + +void OptionValueEnumeration::SetEnumerations( + const OptionEnumValues &enumerators) { + m_enumerations.Clear(); + + for (const auto &enumerator : enumerators) { + ConstString const_enumerator_name(enumerator.string_value); + EnumeratorInfo enumerator_info = {enumerator.value, enumerator.usage}; + m_enumerations.Append(const_enumerator_name, enumerator_info); + } + + m_enumerations.Sort(); +} + +void OptionValueEnumeration::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) { + const uint32_t num_enumerators = m_enumerations.GetSize(); + if (!request.GetCursorArgumentPrefix().empty()) { + for (size_t i = 0; i < num_enumerators; ++i) { + llvm::StringRef name = m_enumerations.GetCStringAtIndex(i).GetStringRef(); + request.TryCompleteCurrentArg(name); + } + return; + } + for (size_t i = 0; i < num_enumerators; ++i) + request.AddCompletion(m_enumerations.GetCStringAtIndex(i).GetStringRef()); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileColonLine.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileColonLine.cpp new file mode 100644 index 000000000000..a68967b81797 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileColonLine.cpp @@ -0,0 +1,137 @@ +//===-- OptionValueFileColonLine.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 "lldb/Interpreter/OptionValueFileColonLine.h" + +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +// This is an OptionValue for parsing file:line:column specifications. +// I set the completer to "source file" which isn't quite right, but we can +// only usefully complete in the file name part of it so it should be good +// enough. +OptionValueFileColonLine::OptionValueFileColonLine() = default; + +OptionValueFileColonLine::OptionValueFileColonLine(llvm::StringRef input) + +{ + SetValueFromString(input, eVarSetOperationAssign); +} + +void OptionValueFileColonLine::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + + if (m_file_spec) + strm << '"' << m_file_spec.GetPath().c_str() << '"'; + if (m_line_number != LLDB_INVALID_LINE_NUMBER) + strm.Printf(":%d", m_line_number); + if (m_column_number != LLDB_INVALID_COLUMN_NUMBER) + strm.Printf(":%d", m_column_number); + } +} + +Status OptionValueFileColonLine::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (value.size() > 0) { + // This is in the form filename:linenumber:column. + // I wish we could use filename:linenumber.column, that would make the + // parsing unambiguous and so much easier... + // But clang & gcc both print the output with two : so we're stuck with + // the two colons. Practically, the only actual ambiguity this introduces + // is with files like "foo:10", which doesn't seem terribly likely. + + // Providing the column is optional, so the input value might have one or + // two colons. First pick off the last colon separated piece. + // It has to be there, since the line number is required: + llvm::StringRef last_piece; + llvm::StringRef left_of_last_piece; + + std::tie(left_of_last_piece, last_piece) = value.rsplit(':'); + if (last_piece.empty()) { + error.SetErrorStringWithFormat("Line specifier must include file and " + "line: '%s'", + value.str().c_str()); + return error; + } + + // Now see if there's another colon and if so pull out the middle piece: + // Then check whether the middle piece is an integer. If it is, then it + // was the line number, and if it isn't we're going to assume that there + // was a colon in the filename (see note at the beginning of the function) + // and ignore it. + llvm::StringRef file_name; + llvm::StringRef middle_piece; + + std::tie(file_name, middle_piece) = left_of_last_piece.rsplit(':'); + if (middle_piece.empty() || + !llvm::to_integer(middle_piece, m_line_number)) { + // The middle piece was empty or not an integer, so there were only two + // legit pieces; our original division was right. Reassign the file + // name and pull out the line number: + file_name = left_of_last_piece; + if (!llvm::to_integer(last_piece, m_line_number)) { + error.SetErrorStringWithFormat("Bad line number value '%s' in: '%s'", + last_piece.str().c_str(), + value.str().c_str()); + return error; + } + } else { + // There were three pieces, and we've got the line number. So now + // we just need to check the column number which was the last peice. + if (!llvm::to_integer(last_piece, m_column_number)) { + error.SetErrorStringWithFormat("Bad column value '%s' in: '%s'", + last_piece.str().c_str(), + value.str().c_str()); + return error; + } + } + + m_value_was_set = true; + m_file_spec.SetFile(file_name, FileSpec::Style::native); + NotifyValueChanged(); + } else { + error.SetErrorString("invalid value string"); + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} + +void OptionValueFileColonLine::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) { + lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( + interpreter, m_completion_mask, request, nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpec.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpec.cpp new file mode 100644 index 000000000000..35a1081dba1e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpec.cpp @@ -0,0 +1,101 @@ +//===-- OptionValueFileSpec.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 "lldb/Interpreter/OptionValueFileSpec.h" + +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/State.h" + +using namespace lldb; +using namespace lldb_private; + +OptionValueFileSpec::OptionValueFileSpec(bool resolve) : m_resolve(resolve) {} + +OptionValueFileSpec::OptionValueFileSpec(const FileSpec &value, bool resolve) + : m_current_value(value), m_default_value(value), + + m_resolve(resolve) {} + +OptionValueFileSpec::OptionValueFileSpec(const FileSpec ¤t_value, + const FileSpec &default_value, + bool resolve) + : m_current_value(current_value), m_default_value(default_value), + + m_resolve(resolve) {} + +void OptionValueFileSpec::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + + if (m_current_value) { + strm << '"' << m_current_value.GetPath().c_str() << '"'; + } + } +} + +Status OptionValueFileSpec::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (value.size() > 0) { + value = value.trim("\"' \t"); + m_value_was_set = true; + m_current_value.SetFile(value.str(), FileSpec::Style::native); + if (m_resolve) + FileSystem::Instance().Resolve(m_current_value); + m_data_sp.reset(); + m_data_mod_time = llvm::sys::TimePoint<>(); + NotifyValueChanged(); + } else { + error.SetErrorString("invalid value string"); + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} + +void OptionValueFileSpec::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) { + lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( + interpreter, m_completion_mask, request, nullptr); +} + +const lldb::DataBufferSP &OptionValueFileSpec::GetFileContents() { + if (m_current_value) { + const auto file_mod_time = FileSystem::Instance().GetModificationTime(m_current_value); + if (m_data_sp && m_data_mod_time == file_mod_time) + return m_data_sp; + m_data_sp = + FileSystem::Instance().CreateDataBuffer(m_current_value.GetPath()); + m_data_mod_time = file_mod_time; + } + return m_data_sp; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpecList.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpecList.cpp new file mode 100644 index 000000000000..b47feb9989dd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFileSpecList.cpp @@ -0,0 +1,166 @@ +//===-- OptionValueFileSpecList.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 "lldb/Interpreter/OptionValueFileSpecList.h" + +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueFileSpecList::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + std::lock_guard<std::recursive_mutex> lock(m_mutex); + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + const bool one_line = dump_mask & eDumpOptionCommand; + const uint32_t size = m_current_value.GetSize(); + if (dump_mask & eDumpOptionType) + strm.Printf(" =%s", + (m_current_value.GetSize() > 0 && !one_line) ? "\n" : ""); + if (!one_line) + strm.IndentMore(); + for (uint32_t i = 0; i < size; ++i) { + if (!one_line) { + strm.Indent(); + strm.Printf("[%u]: ", i); + } + m_current_value.GetFileSpecAtIndex(i).Dump(strm.AsRawOstream()); + if (one_line) + strm << ' '; + } + if (!one_line) + strm.IndentLess(); + } +} + +Status OptionValueFileSpecList::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + std::lock_guard<std::recursive_mutex> lock(m_mutex); + Status error; + Args args(value.str()); + const size_t argc = args.GetArgumentCount(); + + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + if (argc > 1) { + uint32_t idx; + const uint32_t count = m_current_value.GetSize(); + if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { + error.SetErrorStringWithFormat( + "invalid file list index %s, index must be 0 through %u", + args.GetArgumentAtIndex(0), count); + } else { + for (size_t i = 1; i < argc; ++i, ++idx) { + FileSpec file(args.GetArgumentAtIndex(i)); + if (idx < count) + m_current_value.Replace(idx, file); + else + m_current_value.Append(file); + } + NotifyValueChanged(); + } + } else { + error.SetErrorString("replace operation takes an array index followed by " + "one or more values"); + } + break; + + case eVarSetOperationAssign: + m_current_value.Clear(); + // Fall through to append case + [[fallthrough]]; + case eVarSetOperationAppend: + if (argc > 0) { + m_value_was_set = true; + for (size_t i = 0; i < argc; ++i) { + FileSpec file(args.GetArgumentAtIndex(i)); + m_current_value.Append(file); + } + NotifyValueChanged(); + } else { + error.SetErrorString( + "assign operation takes at least one file path argument"); + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + if (argc > 1) { + uint32_t idx; + const uint32_t count = m_current_value.GetSize(); + if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { + error.SetErrorStringWithFormat( + "invalid insert file list index %s, index must be 0 through %u", + args.GetArgumentAtIndex(0), count); + } else { + if (op == eVarSetOperationInsertAfter) + ++idx; + for (size_t i = 1; i < argc; ++i, ++idx) { + FileSpec file(args.GetArgumentAtIndex(i)); + m_current_value.Insert(idx, file); + } + NotifyValueChanged(); + } + } else { + error.SetErrorString("insert operation takes an array index followed by " + "one or more values"); + } + break; + + case eVarSetOperationRemove: + if (argc > 0) { + std::vector<int> remove_indexes; + bool all_indexes_valid = true; + size_t i; + for (i = 0; all_indexes_valid && i < argc; ++i) { + int idx; + if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx)) + all_indexes_valid = false; + else + remove_indexes.push_back(idx); + } + + if (all_indexes_valid) { + size_t num_remove_indexes = remove_indexes.size(); + if (num_remove_indexes) { + // Sort and then erase in reverse so indexes are always valid + llvm::sort(remove_indexes); + for (size_t j = num_remove_indexes - 1; j < num_remove_indexes; ++j) { + m_current_value.Remove(j); + } + } + NotifyValueChanged(); + } else { + error.SetErrorStringWithFormat( + "invalid array index '%s', aborting remove operation", + args.GetArgumentAtIndex(i)); + } + } else { + error.SetErrorString("remove operation takes one or more array index"); + } + break; + + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} + +OptionValueSP OptionValueFileSpecList::Clone() const { + std::lock_guard<std::recursive_mutex> lock(m_mutex); + return Cloneable::Clone(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormat.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormat.cpp new file mode 100644 index 000000000000..ab89f673e96f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormat.cpp @@ -0,0 +1,62 @@ +//===-- OptionValueFormat.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 "lldb/Interpreter/OptionValueFormat.h" + +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueFormat::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + strm.PutCString(FormatManager::GetFormatAsCString(m_current_value)); + } +} + +llvm::json::Value OptionValueFormat::ToJSON(const ExecutionContext *exe_ctx) { + return FormatManager::GetFormatAsCString(m_current_value); +} + +Status OptionValueFormat::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + Format new_format; + error = OptionArgParser::ToFormat(value.str().c_str(), new_format, nullptr); + if (error.Success()) { + m_value_was_set = true; + m_current_value = new_format; + NotifyValueChanged(); + } + } break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormatEntity.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormatEntity.cpp new file mode 100644 index 000000000000..98216e13ad83 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueFormatEntity.cpp @@ -0,0 +1,122 @@ +//===-- OptionValueFormatEntity.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 "lldb/Interpreter/OptionValueFormatEntity.h" + +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" +using namespace lldb; +using namespace lldb_private; + +OptionValueFormatEntity::OptionValueFormatEntity(const char *default_format) { + if (default_format && default_format[0]) { + llvm::StringRef default_format_str(default_format); + Status error = FormatEntity::Parse(default_format_str, m_default_entry); + if (error.Success()) { + m_default_format = default_format; + m_current_format = default_format; + m_current_entry = m_default_entry; + } + } +} + +void OptionValueFormatEntity::Clear() { + m_current_entry = m_default_entry; + m_current_format = m_default_format; + m_value_was_set = false; +} + +static void EscapeBackticks(llvm::StringRef str, std::string &dst) { + dst.clear(); + dst.reserve(str.size()); + + for (size_t i = 0, e = str.size(); i != e; ++i) { + char c = str[i]; + if (c == '`') { + if (i == 0 || str[i - 1] != '\\') + dst += '\\'; + } + dst += c; + } +} + +void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + std::string escaped; + EscapeBackticks(m_current_format, escaped); + strm << '"' << escaped << '"'; + } +} + +llvm::json::Value +OptionValueFormatEntity::ToJSON(const ExecutionContext *exe_ctx) { + std::string escaped; + EscapeBackticks(m_current_format, escaped); + return escaped; +} + +Status OptionValueFormatEntity::SetValueFromString(llvm::StringRef value_str, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + // Check if the string starts with a quote character after removing leading + // and trailing spaces. If it does start with a quote character, make sure + // it ends with the same quote character and remove the quotes before we + // parse the format string. If the string doesn't start with a quote, leave + // the string alone and parse as is. + llvm::StringRef trimmed_value_str = value_str.trim(); + if (!trimmed_value_str.empty()) { + const char first_char = trimmed_value_str[0]; + if (first_char == '"' || first_char == '\'') { + const size_t trimmed_len = trimmed_value_str.size(); + if (trimmed_len == 1 || value_str[trimmed_len - 1] != first_char) { + error.SetErrorString("mismatched quotes"); + return error; + } + value_str = trimmed_value_str.substr(1, trimmed_len - 2); + } + } + FormatEntity::Entry entry; + error = FormatEntity::Parse(value_str, entry); + if (error.Success()) { + m_current_entry = std::move(entry); + m_current_format = std::string(value_str); + m_value_was_set = true; + NotifyValueChanged(); + } + } break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value_str, op); + break; + } + return error; +} + +void OptionValueFormatEntity::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) { + FormatEntity::AutoComplete(request); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueLanguage.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueLanguage.cpp new file mode 100644 index 000000000000..409fcf882bcb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueLanguage.cpp @@ -0,0 +1,73 @@ +//===-- OptionValueLanguage.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 "lldb/Interpreter/OptionValueLanguage.h" + +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Target/Language.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueLanguage::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + if (m_current_value != eLanguageTypeUnknown) + strm.PutCString(Language::GetNameForLanguageType(m_current_value)); + } +} + +llvm::json::Value OptionValueLanguage::ToJSON(const ExecutionContext *exe_ctx) { + return Language::GetNameForLanguageType(m_current_value); +} + +Status OptionValueLanguage::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + LanguageSet languages_for_types = Language::GetLanguagesSupportingTypeSystems(); + LanguageType new_type = Language::GetLanguageTypeFromString(value.trim()); + if (new_type && languages_for_types[new_type]) { + m_value_was_set = true; + m_current_value = new_type; + } else { + StreamString error_strm; + error_strm.Printf("invalid language type '%s', ", value.str().c_str()); + error_strm.Printf("valid values are:\n"); + for (int bit : languages_for_types.bitvector.set_bits()) { + auto language = (LanguageType)bit; + error_strm.Printf(" %s\n", + Language::GetNameForLanguageType(language)); + } + error.SetErrorString(error_strm.GetString()); + } + } break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValuePathMappings.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValuePathMappings.cpp new file mode 100644 index 000000000000..7cfc445f5580 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValuePathMappings.cpp @@ -0,0 +1,196 @@ +//===-- OptionValuePathMappings.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 "lldb/Interpreter/OptionValuePathMappings.h" + +#include "lldb/Host/FileSystem.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +static bool VerifyPathExists(const char *path) { + if (path && path[0]) + return FileSystem::Instance().Exists(path); + else + return false; +} + +void OptionValuePathMappings::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.Printf(" =%s", (m_path_mappings.GetSize() > 0) ? "\n" : ""); + m_path_mappings.Dump(&strm); + } +} + +llvm::json::Value +OptionValuePathMappings::ToJSON(const ExecutionContext *exe_ctx) { + return m_path_mappings.ToJSON(); +} + +Status OptionValuePathMappings::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + Args args(value.str()); + const size_t argc = args.GetArgumentCount(); + + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + // Must be at least one index + 1 pair of paths, and the pair count must be + // even + if (argc >= 3 && (((argc - 1) & 1) == 0)) { + uint32_t idx; + const uint32_t count = m_path_mappings.GetSize(); + if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { + error.SetErrorStringWithFormat( + "invalid file list index %s, index must be 0 through %u", + args.GetArgumentAtIndex(0), count); + } else { + bool changed = false; + for (size_t i = 1; i < argc; idx++, i += 2) { + const char *orginal_path = args.GetArgumentAtIndex(i); + const char *replace_path = args.GetArgumentAtIndex(i + 1); + if (VerifyPathExists(replace_path)) { + if (!m_path_mappings.Replace(orginal_path, replace_path, idx, + m_notify_changes)) + m_path_mappings.Append(orginal_path, replace_path, + m_notify_changes); + changed = true; + } else { + std::string previousError = + error.Fail() ? std::string(error.AsCString()) + "\n" : ""; + error.SetErrorStringWithFormat( + "%sthe replacement path doesn't exist: \"%s\"", + previousError.c_str(), replace_path); + } + } + if (changed) + NotifyValueChanged(); + } + } else { + error.SetErrorString("replace operation takes an array index followed by " + "one or more path pairs"); + } + break; + + case eVarSetOperationAssign: + if (argc < 2 || (argc & 1)) { + error.SetErrorString("assign operation takes one or more path pairs"); + break; + } + m_path_mappings.Clear(m_notify_changes); + // Fall through to append case + [[fallthrough]]; + case eVarSetOperationAppend: + if (argc < 2 || (argc & 1)) { + error.SetErrorString("append operation takes one or more path pairs"); + break; + } else { + bool changed = false; + for (size_t i = 0; i < argc; i += 2) { + const char *orginal_path = args.GetArgumentAtIndex(i); + const char *replace_path = args.GetArgumentAtIndex(i + 1); + if (VerifyPathExists(replace_path)) { + m_path_mappings.Append(orginal_path, replace_path, m_notify_changes); + m_value_was_set = true; + changed = true; + } else { + std::string previousError = + error.Fail() ? std::string(error.AsCString()) + "\n" : ""; + error.SetErrorStringWithFormat( + "%sthe replacement path doesn't exist: \"%s\"", + previousError.c_str(), replace_path); + } + } + if (changed) + NotifyValueChanged(); + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + // Must be at least one index + 1 pair of paths, and the pair count must be + // even + if (argc >= 3 && (((argc - 1) & 1) == 0)) { + uint32_t idx; + const uint32_t count = m_path_mappings.GetSize(); + if (!llvm::to_integer(args.GetArgumentAtIndex(0), idx) || idx > count) { + error.SetErrorStringWithFormat( + "invalid file list index %s, index must be 0 through %u", + args.GetArgumentAtIndex(0), count); + } else { + bool changed = false; + if (op == eVarSetOperationInsertAfter) + ++idx; + for (size_t i = 1; i < argc; i += 2) { + const char *orginal_path = args.GetArgumentAtIndex(i); + const char *replace_path = args.GetArgumentAtIndex(i + 1); + if (VerifyPathExists(replace_path)) { + m_path_mappings.Insert(orginal_path, replace_path, idx, + m_notify_changes); + changed = true; + idx++; + } else { + std::string previousError = + error.Fail() ? std::string(error.AsCString()) + "\n" : ""; + error.SetErrorStringWithFormat( + "%sthe replacement path doesn't exist: \"%s\"", + previousError.c_str(), replace_path); + } + } + if (changed) + NotifyValueChanged(); + } + } else { + error.SetErrorString("insert operation takes an array index followed by " + "one or more path pairs"); + } + break; + + case eVarSetOperationRemove: + if (argc > 0) { + std::vector<int> remove_indexes; + for (size_t i = 0; i < argc; ++i) { + int idx; + if (!llvm::to_integer(args.GetArgumentAtIndex(i), idx) || idx < 0 || + idx >= (int)m_path_mappings.GetSize()) { + error.SetErrorStringWithFormat( + "invalid array index '%s', aborting remove operation", + args.GetArgumentAtIndex(i)); + break; + } else + remove_indexes.push_back(idx); + } + + // Sort and then erase in reverse so indexes are always valid + llvm::sort(remove_indexes); + for (auto index : llvm::reverse(remove_indexes)) + m_path_mappings.Remove(index, m_notify_changes); + NotifyValueChanged(); + } else { + error.SetErrorString("remove operation takes one or more array index"); + } + break; + + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueProperties.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueProperties.cpp new file mode 100644 index 000000000000..8719a2b63656 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueProperties.cpp @@ -0,0 +1,491 @@ +//===-- OptionValueProperties.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 "lldb/Interpreter/OptionValueProperties.h" + +#include "lldb/Utility/Flags.h" + +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Interpreter/OptionValues.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +OptionValueProperties::OptionValueProperties(llvm::StringRef name) + : m_name(name.str()) {} + +void OptionValueProperties::Initialize(const PropertyDefinitions &defs) { + for (const auto &definition : defs) { + Property property(definition); + assert(property.IsValid()); + m_name_to_index.insert({property.GetName(), m_properties.size()}); + property.GetValue()->SetParent(shared_from_this()); + m_properties.push_back(property); + } +} + +void OptionValueProperties::SetValueChangedCallback( + size_t property_idx, std::function<void()> callback) { + Property *property = ProtectedGetPropertyAtIndex(property_idx); + if (property) + property->SetValueChangedCallback(std::move(callback)); +} + +void OptionValueProperties::AppendProperty(llvm::StringRef name, + llvm::StringRef desc, bool is_global, + const OptionValueSP &value_sp) { + Property property(name, desc, is_global, value_sp); + m_name_to_index.insert({name, m_properties.size()}); + m_properties.push_back(property); + value_sp->SetParent(shared_from_this()); +} + +lldb::OptionValueSP +OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx, + llvm::StringRef key) const { + auto iter = m_name_to_index.find(key); + if (iter == m_name_to_index.end()) + return OptionValueSP(); + const size_t idx = iter->second; + if (idx >= m_properties.size()) + return OptionValueSP(); + return GetPropertyAtIndex(idx, exe_ctx)->GetValue(); +} + +lldb::OptionValueSP +OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx, + llvm::StringRef name, Status &error) const { + lldb::OptionValueSP value_sp; + if (name.empty()) + return OptionValueSP(); + + llvm::StringRef sub_name; + llvm::StringRef key; + size_t key_len = name.find_first_of(".[{"); + if (key_len != llvm::StringRef::npos) { + key = name.take_front(key_len); + sub_name = name.drop_front(key_len); + } else + key = name; + + value_sp = GetValueForKey(exe_ctx, key); + if (sub_name.empty() || !value_sp) + return value_sp; + + switch (sub_name[0]) { + case '.': { + lldb::OptionValueSP return_val_sp; + return_val_sp = + value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), error); + if (!return_val_sp) { + if (Properties::IsSettingExperimental(sub_name.drop_front())) { + const size_t experimental_len = + Properties::GetExperimentalSettingsName().size(); + if (sub_name[experimental_len + 1] == '.') + return_val_sp = value_sp->GetSubValue( + exe_ctx, sub_name.drop_front(experimental_len + 2), error); + // It isn't an error if an experimental setting is not present. + if (!return_val_sp) + error.Clear(); + } + } + return return_val_sp; + } + case '[': + // Array or dictionary access for subvalues like: "[12]" -- access + // 12th array element "['hello']" -- dictionary access of key named hello + return value_sp->GetSubValue(exe_ctx, sub_name, error); + + default: + value_sp.reset(); + break; + } + return value_sp; +} + +Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx, + VarSetOperationType op, + llvm::StringRef name, + llvm::StringRef value) { + Status error; + llvm::SmallVector<llvm::StringRef, 8> components; + name.split(components, '.'); + bool name_contains_experimental = false; + for (const auto &part : components) + if (Properties::IsSettingExperimental(part)) + name_contains_experimental = true; + + lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, error)); + if (value_sp) + error = value_sp->SetValueFromString(value, op); + else { + // Don't set an error if the path contained .experimental. - those are + // allowed to be missing and should silently fail. + if (!name_contains_experimental && error.AsCString() == nullptr) { + error.SetErrorStringWithFormat("invalid value path '%s'", + name.str().c_str()); + } + } + return error; +} + +size_t OptionValueProperties::GetPropertyIndex(llvm::StringRef name) const { + auto iter = m_name_to_index.find(name); + if (iter == m_name_to_index.end()) + return SIZE_MAX; + return iter->second; +} + +const Property * +OptionValueProperties::GetProperty(llvm::StringRef name, + const ExecutionContext *exe_ctx) const { + auto iter = m_name_to_index.find(name); + if (iter == m_name_to_index.end()) + return nullptr; + return GetPropertyAtIndex(iter->second, exe_ctx); +} + +lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex( + size_t idx, const ExecutionContext *exe_ctx) const { + const Property *setting = GetPropertyAtIndex(idx, exe_ctx); + if (setting) + return setting->GetValue(); + return OptionValueSP(); +} + +OptionValuePathMappings * +OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings( + size_t idx, const ExecutionContext *exe_ctx) const { + OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx)); + if (value_sp) + return value_sp->GetAsPathMappings(); + return nullptr; +} + +OptionValueFileSpecList * +OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList( + size_t idx, const ExecutionContext *exe_ctx) const { + OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx)); + if (value_sp) + return value_sp->GetAsFileSpecList(); + return nullptr; +} + +bool OptionValueProperties::GetPropertyAtIndexAsArgs( + size_t idx, Args &args, const ExecutionContext *exe_ctx) const { + const Property *property = GetPropertyAtIndex(idx, exe_ctx); + if (!property) + return false; + + OptionValue *value = property->GetValue().get(); + if (!value) + return false; + + const OptionValueArgs *arguments = value->GetAsArgs(); + if (arguments) { + arguments->GetArgs(args); + return true; + } + + const OptionValueArray *array = value->GetAsArray(); + if (array) { + array->GetArgs(args); + return true; + } + + const OptionValueDictionary *dict = value->GetAsDictionary(); + if (dict) { + dict->GetArgs(args); + return true; + } + + return false; +} + +bool OptionValueProperties::SetPropertyAtIndexFromArgs( + size_t idx, const Args &args, const ExecutionContext *exe_ctx) { + const Property *property = GetPropertyAtIndex(idx, exe_ctx); + if (!property) + return false; + + OptionValue *value = property->GetValue().get(); + if (!value) + return false; + + OptionValueArgs *arguments = value->GetAsArgs(); + if (arguments) + return arguments->SetArgs(args, eVarSetOperationAssign).Success(); + + OptionValueArray *array = value->GetAsArray(); + if (array) + return array->SetArgs(args, eVarSetOperationAssign).Success(); + + OptionValueDictionary *dict = value->GetAsDictionary(); + if (dict) + return dict->SetArgs(args, eVarSetOperationAssign).Success(); + + return false; +} + +OptionValueDictionary * +OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary( + size_t idx, const ExecutionContext *exe_ctx) const { + const Property *property = GetPropertyAtIndex(idx, exe_ctx); + if (property) + return property->GetValue()->GetAsDictionary(); + return nullptr; +} + +OptionValueFileSpec * +OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec( + size_t idx, const ExecutionContext *exe_ctx) const { + const Property *property = GetPropertyAtIndex(idx, exe_ctx); + if (property) { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetAsFileSpec(); + } + return nullptr; +} + +OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64( + size_t idx, const ExecutionContext *exe_ctx) const { + const Property *property = GetPropertyAtIndex(idx, exe_ctx); + if (property) { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetAsSInt64(); + } + return nullptr; +} + +OptionValueUInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueUInt64( + size_t idx, const ExecutionContext *exe_ctx) const { + const Property *property = GetPropertyAtIndex(idx, exe_ctx); + if (property) { + OptionValue *value = property->GetValue().get(); + if (value) + return value->GetAsUInt64(); + } + return nullptr; +} + +OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString( + size_t idx, const ExecutionContext *exe_ctx) const { + OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx)); + if (value_sp) + return value_sp->GetAsString(); + return nullptr; +} + +void OptionValueProperties::Clear() { + const size_t num_properties = m_properties.size(); + for (size_t i = 0; i < num_properties; ++i) + m_properties[i].GetValue()->Clear(); +} + +Status OptionValueProperties::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + + // Args args(value_cstr); + // const size_t argc = args.GetArgumentCount(); + switch (op) { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + case eVarSetOperationRemove: + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + + return error; +} + +void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx, + Stream &strm, uint32_t dump_mask) { + const size_t num_properties = m_properties.size(); + for (size_t i = 0; i < num_properties; ++i) { + const Property *property = GetPropertyAtIndex(i, exe_ctx); + if (property) { + OptionValue *option_value = property->GetValue().get(); + assert(option_value); + const bool transparent_value = option_value->ValueIsTransparent(); + property->Dump(exe_ctx, strm, dump_mask); + if (!transparent_value) + strm.EOL(); + } + } +} + +llvm::json::Value +OptionValueProperties::ToJSON(const ExecutionContext *exe_ctx) { + llvm::json::Object json_properties; + const size_t num_properties = m_properties.size(); + for (size_t i = 0; i < num_properties; ++i) { + const Property *property = GetPropertyAtIndex(i, exe_ctx); + if (property) { + OptionValue *option_value = property->GetValue().get(); + assert(option_value); + json_properties.try_emplace(property->GetName(), + option_value->ToJSON(exe_ctx)); + } + } + return json_properties; +} + +Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx, + Stream &strm, + llvm::StringRef property_path, + uint32_t dump_mask, + bool is_json) { + Status error; + lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, property_path, error)); + if (value_sp) { + if (!value_sp->ValueIsTransparent()) { + if (dump_mask & eDumpOptionName) + strm.PutCString(property_path); + if (dump_mask & ~eDumpOptionName) + strm.PutChar(' '); + } + if (is_json) { + strm.Printf( + "%s", + llvm::formatv("{0:2}", value_sp->ToJSON(exe_ctx)).str().c_str()); + } else + value_sp->DumpValue(exe_ctx, strm, dump_mask); + } + return error; +} + +OptionValuePropertiesSP +OptionValueProperties::CreateLocalCopy(const Properties &global_properties) { + auto global_props_sp = global_properties.GetValueProperties(); + lldbassert(global_props_sp); + + auto copy_sp = global_props_sp->DeepCopy(global_props_sp->GetParent()); + return std::static_pointer_cast<OptionValueProperties>(copy_sp); +} + +OptionValueSP +OptionValueProperties::DeepCopy(const OptionValueSP &new_parent) const { + auto copy_sp = OptionValue::DeepCopy(new_parent); + // copy_sp->GetAsProperties cannot be used here as it doesn't work for derived + // types that override GetType returning a different value. + auto *props_value_ptr = static_cast<OptionValueProperties *>(copy_sp.get()); + lldbassert(props_value_ptr); + + for (auto &property : props_value_ptr->m_properties) { + // Duplicate any values that are not global when constructing properties + // from a global copy. + if (!property.IsGlobal()) { + auto value_sp = property.GetValue()->DeepCopy(copy_sp); + property.SetOptionValue(value_sp); + } + } + return copy_sp; +} + +const Property * +OptionValueProperties::GetPropertyAtPath(const ExecutionContext *exe_ctx, + llvm::StringRef name) const { + if (name.empty()) + return nullptr; + + const Property *property = nullptr; + llvm::StringRef sub_name; + llvm::StringRef key; + size_t key_len = name.find_first_of(".[{"); + + if (key_len != llvm::StringRef::npos) { + key = name.take_front(key_len); + sub_name = name.drop_front(key_len); + } else + key = name; + + property = GetProperty(key, exe_ctx); + if (sub_name.empty() || !property) + return property; + + if (sub_name[0] == '.') { + OptionValueProperties *sub_properties = + property->GetValue()->GetAsProperties(); + if (sub_properties) + return sub_properties->GetPropertyAtPath(exe_ctx, sub_name.drop_front()); + } + return nullptr; +} + +void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter, + Stream &strm) const { + size_t max_name_len = 0; + const size_t num_properties = m_properties.size(); + for (size_t i = 0; i < num_properties; ++i) { + const Property *property = ProtectedGetPropertyAtIndex(i); + if (property) + max_name_len = std::max<size_t>(property->GetName().size(), max_name_len); + } + for (size_t i = 0; i < num_properties; ++i) { + const Property *property = ProtectedGetPropertyAtIndex(i); + if (property) + property->DumpDescription(interpreter, strm, max_name_len, false); + } +} + +void OptionValueProperties::Apropos( + llvm::StringRef keyword, + std::vector<const Property *> &matching_properties) const { + const size_t num_properties = m_properties.size(); + StreamString strm; + for (size_t i = 0; i < num_properties; ++i) { + const Property *property = ProtectedGetPropertyAtIndex(i); + if (property) { + const OptionValueProperties *properties = + property->GetValue()->GetAsProperties(); + if (properties) { + properties->Apropos(keyword, matching_properties); + } else { + bool match = false; + llvm::StringRef name = property->GetName(); + if (name.contains_insensitive(keyword)) + match = true; + else { + llvm::StringRef desc = property->GetDescription(); + if (desc.contains_insensitive(keyword)) + match = true; + } + if (match) { + matching_properties.push_back(property); + } + } + } + } +} + +lldb::OptionValuePropertiesSP +OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx, + llvm::StringRef name) { + lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name)); + if (option_value_sp) { + OptionValueProperties *ov_properties = option_value_sp->GetAsProperties(); + if (ov_properties) + return ov_properties->shared_from_this(); + } + return lldb::OptionValuePropertiesSP(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueRegex.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueRegex.cpp new file mode 100644 index 000000000000..bbeca8da7714 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueRegex.cpp @@ -0,0 +1,61 @@ +//===-- OptionValueRegex.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 "lldb/Interpreter/OptionValueRegex.h" + +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueRegex::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + if (m_regex.IsValid()) { + llvm::StringRef regex_text = m_regex.GetText(); + strm.Printf("%s", regex_text.str().c_str()); + } + } +} + +Status OptionValueRegex::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationInvalid: + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + error = OptionValue::SetValueFromString(value, op); + break; + + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + m_regex = RegularExpression(value); + if (m_regex.IsValid()) { + m_value_was_set = true; + NotifyValueChanged(); + } else if (llvm::Error err = m_regex.GetError()) { + error.SetErrorString(llvm::toString(std::move(err))); + } else { + error.SetErrorString("regex error"); + } + break; + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueSInt64.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueSInt64.cpp new file mode 100644 index 000000000000..c1db5056cd94 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueSInt64.cpp @@ -0,0 +1,70 @@ +//===-- OptionValueSInt64.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 "lldb/Interpreter/OptionValueSInt64.h" + +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueSInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + // printf ("%p: DumpValue (exe_ctx=%p, strm, mask) m_current_value = %" + // PRIi64 + // "\n", this, exe_ctx, m_current_value); + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + // if (dump_mask & eDumpOptionName) + // DumpQualifiedName (strm); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + strm.Printf("%" PRIi64, m_current_value); + } +} + +Status OptionValueSInt64::SetValueFromString(llvm::StringRef value_ref, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + llvm::StringRef value_trimmed = value_ref.trim(); + int64_t value; + if (llvm::to_integer(value_trimmed, value)) { + if (value >= m_min_value && value <= m_max_value) { + m_value_was_set = true; + m_current_value = value; + NotifyValueChanged(); + } else + error.SetErrorStringWithFormat( + "%" PRIi64 " is out of range, valid values must be between %" PRIi64 + " and %" PRIi64 ".", + value, m_min_value, m_max_value); + } else { + error.SetErrorStringWithFormat("invalid int64_t string value: '%s'", + value_ref.str().c_str()); + } + } break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value_ref, op); + break; + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueString.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueString.cpp new file mode 100644 index 000000000000..b4fec91bc33f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueString.cpp @@ -0,0 +1,143 @@ +//===-- OptionValueString.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 "lldb/Interpreter/OptionValueString.h" + +#include "lldb/Host/OptionParser.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueString::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + if (!m_current_value.empty() || m_value_was_set) { + if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) { + std::string expanded_escape_value; + Args::ExpandEscapedCharacters(m_current_value.c_str(), + expanded_escape_value); + if (dump_mask & eDumpOptionRaw) + strm.Printf("%s", expanded_escape_value.c_str()); + else + strm.Printf("\"%s\"", expanded_escape_value.c_str()); + } else { + if (dump_mask & eDumpOptionRaw) + strm.Printf("%s", m_current_value.c_str()); + else + strm.Printf("\"%s\"", m_current_value.c_str()); + } + } + } +} + +Status OptionValueString::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + + std::string value_str = value.str(); + value = value.trim(); + if (value.size() > 0) { + switch (value.front()) { + case '"': + case '\'': { + if (value.size() <= 1 || value.back() != value.front()) { + error.SetErrorString("mismatched quotes"); + return error; + } + value = value.drop_front().drop_back(); + } break; + } + value_str = value.str(); + } + + switch (op) { + case eVarSetOperationInvalid: + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + if (m_validator) { + error = m_validator(value_str.c_str(), m_validator_baton); + if (error.Fail()) + return error; + } + error = OptionValue::SetValueFromString(value, op); + break; + + case eVarSetOperationAppend: { + std::string new_value(m_current_value); + if (value.size() > 0) { + if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) { + std::string str; + Args::EncodeEscapeSequences(value_str.c_str(), str); + new_value.append(str); + } else + new_value.append(std::string(value)); + } + if (m_validator) { + error = m_validator(new_value.c_str(), m_validator_baton); + if (error.Fail()) + return error; + } + m_current_value.assign(new_value); + NotifyValueChanged(); + } break; + + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (m_validator) { + error = m_validator(value_str.c_str(), m_validator_baton); + if (error.Fail()) + return error; + } + m_value_was_set = true; + if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) { + Args::EncodeEscapeSequences(value_str.c_str(), m_current_value); + } else { + SetCurrentValue(value_str); + } + NotifyValueChanged(); + break; + } + return error; +} + +Status OptionValueString::SetCurrentValue(llvm::StringRef value) { + if (m_validator) { + Status error(m_validator(value.str().c_str(), m_validator_baton)); + if (error.Fail()) + return error; + } + m_current_value.assign(std::string(value)); + return Status(); +} + +Status OptionValueString::AppendToCurrentValue(const char *value) { + if (value && value[0]) { + if (m_validator) { + std::string new_value(m_current_value); + new_value.append(value); + Status error(m_validator(value, m_validator_baton)); + if (error.Fail()) + return error; + m_current_value.assign(new_value); + } else + m_current_value.append(value); + } + return Status(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueUInt64.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueUInt64.cpp new file mode 100644 index 000000000000..2e69c164e32a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueUInt64.cpp @@ -0,0 +1,75 @@ +//===-- OptionValueUInt64.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 "lldb/Interpreter/OptionValueUInt64.h" + +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::OptionValueSP OptionValueUInt64::Create(llvm::StringRef value_str, + Status &error) { + lldb::OptionValueSP value_sp(new OptionValueUInt64()); + error = value_sp->SetValueFromString(value_str); + if (error.Fail()) + value_sp.reset(); + return value_sp; +} + +void OptionValueUInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + strm.Printf("%" PRIu64, m_current_value); + } +} + +Status OptionValueUInt64::SetValueFromString(llvm::StringRef value_ref, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + llvm::StringRef value_trimmed = value_ref.trim(); + uint64_t value; + if (llvm::to_integer(value_trimmed, value)) { + if (value >= m_min_value && value <= m_max_value) { + m_value_was_set = true; + m_current_value = value; + NotifyValueChanged(); + } else { + error.SetErrorStringWithFormat( + "%" PRIu64 " is out of range, valid values must be between %" PRIu64 + " and %" PRIu64 ".", + value, m_min_value, m_max_value); + } + } else { + error.SetErrorStringWithFormat("invalid uint64_t string value: '%s'", + value_ref.str().c_str()); + } + } break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value_ref, op); + break; + } + return error; +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/OptionValueUUID.cpp b/contrib/llvm-project/lldb/source/Interpreter/OptionValueUUID.cpp new file mode 100644 index 000000000000..ff35870a76e8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/OptionValueUUID.cpp @@ -0,0 +1,81 @@ +//===-- OptionValueUUID.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 "lldb/Interpreter/OptionValueUUID.h" + +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +void OptionValueUUID::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) { + if (dump_mask & eDumpOptionType) + strm.Printf("(%s)", GetTypeAsCString()); + if (dump_mask & eDumpOptionValue) { + if (dump_mask & eDumpOptionType) + strm.PutCString(" = "); + m_uuid.Dump(strm); + } +} + +Status OptionValueUUID::SetValueFromString(llvm::StringRef value, + VarSetOperationType op) { + Status error; + switch (op) { + case eVarSetOperationClear: + Clear(); + NotifyValueChanged(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: { + if (!m_uuid.SetFromStringRef(value)) + error.SetErrorStringWithFormat("invalid uuid string value '%s'", + value.str().c_str()); + else { + m_value_was_set = true; + NotifyValueChanged(); + } + } break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromString(value, op); + break; + } + return error; +} + +void OptionValueUUID::AutoComplete(CommandInterpreter &interpreter, + CompletionRequest &request) { + ExecutionContext exe_ctx(interpreter.GetExecutionContext()); + Target *target = exe_ctx.GetTargetPtr(); + if (!target) + return; + auto prefix = request.GetCursorArgumentPrefix(); + llvm::SmallVector<uint8_t, 20> uuid_bytes; + if (!UUID::DecodeUUIDBytesFromString(prefix, uuid_bytes).empty()) + return; + const size_t num_modules = target->GetImages().GetSize(); + for (size_t i = 0; i < num_modules; ++i) { + ModuleSP module_sp(target->GetImages().GetModuleAtIndex(i)); + if (!module_sp) + continue; + const UUID &module_uuid = module_sp->GetUUID(); + if (!module_uuid.IsValid()) + continue; + request.TryCompleteCurrentArg(module_uuid.GetAsString()); + } +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/Options.cpp b/contrib/llvm-project/lldb/source/Interpreter/Options.cpp new file mode 100644 index 000000000000..c5e75e0b9dce --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/Options.cpp @@ -0,0 +1,1364 @@ +//===-- Options.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 "lldb/Interpreter/Options.h" + +#include <algorithm> +#include <bitset> +#include <map> +#include <set> + +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +// Options +Options::Options() { BuildValidOptionSets(); } + +Options::~Options() = default; + +void Options::NotifyOptionParsingStarting(ExecutionContext *execution_context) { + m_seen_options.clear(); + // Let the subclass reset its option values + OptionParsingStarting(execution_context); +} + +Status +Options::NotifyOptionParsingFinished(ExecutionContext *execution_context) { + return OptionParsingFinished(execution_context); +} + +void Options::OptionSeen(int option_idx) { m_seen_options.insert(option_idx); } + +// Returns true is set_a is a subset of set_b; Otherwise returns false. + +bool Options::IsASubset(const OptionSet &set_a, const OptionSet &set_b) { + bool is_a_subset = true; + OptionSet::const_iterator pos_a; + OptionSet::const_iterator pos_b; + + // set_a is a subset of set_b if every member of set_a is also a member of + // set_b + + for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a) { + pos_b = set_b.find(*pos_a); + if (pos_b == set_b.end()) + is_a_subset = false; + } + + return is_a_subset; +} + +// Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) && +// !ElementOf (x, set_b) } + +size_t Options::OptionsSetDiff(const OptionSet &set_a, const OptionSet &set_b, + OptionSet &diffs) { + size_t num_diffs = 0; + OptionSet::const_iterator pos_a; + OptionSet::const_iterator pos_b; + + for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a) { + pos_b = set_b.find(*pos_a); + if (pos_b == set_b.end()) { + ++num_diffs; + diffs.insert(*pos_a); + } + } + + return num_diffs; +} + +// Returns the union of set_a and set_b. Does not put duplicate members into +// the union. + +void Options::OptionsSetUnion(const OptionSet &set_a, const OptionSet &set_b, + OptionSet &union_set) { + OptionSet::const_iterator pos; + OptionSet::iterator pos_union; + + // Put all the elements of set_a into the union. + + for (pos = set_a.begin(); pos != set_a.end(); ++pos) + union_set.insert(*pos); + + // Put all the elements of set_b that are not already there into the union. + for (pos = set_b.begin(); pos != set_b.end(); ++pos) { + pos_union = union_set.find(*pos); + if (pos_union == union_set.end()) + union_set.insert(*pos); + } +} + +bool Options::VerifyOptions(CommandReturnObject &result) { + bool options_are_valid = false; + + int num_levels = GetRequiredOptions().size(); + if (num_levels) { + for (int i = 0; i < num_levels && !options_are_valid; ++i) { + // This is the correct set of options if: 1). m_seen_options contains + // all of m_required_options[i] (i.e. all the required options at this + // level are a subset of m_seen_options); AND 2). { m_seen_options - + // m_required_options[i] is a subset of m_options_options[i] (i.e. all + // the rest of m_seen_options are in the set of optional options at this + // level. + + // Check to see if all of m_required_options[i] are a subset of + // m_seen_options + if (IsASubset(GetRequiredOptions()[i], m_seen_options)) { + // Construct the set difference: remaining_options = {m_seen_options} - + // {m_required_options[i]} + OptionSet remaining_options; + OptionsSetDiff(m_seen_options, GetRequiredOptions()[i], + remaining_options); + // Check to see if remaining_options is a subset of + // m_optional_options[i] + if (IsASubset(remaining_options, GetOptionalOptions()[i])) + options_are_valid = true; + } + } + } else { + options_are_valid = true; + } + + if (options_are_valid) { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } else { + result.AppendError("invalid combination of options for the given command"); + } + + return options_are_valid; +} + +// This is called in the Options constructor, though we could call it lazily if +// that ends up being a performance problem. + +void Options::BuildValidOptionSets() { + // Check to see if we already did this. + if (m_required_options.size() != 0) + return; + + // Check to see if there are any options. + int num_options = NumCommandOptions(); + if (num_options == 0) + return; + + auto opt_defs = GetDefinitions(); + m_required_options.resize(1); + m_optional_options.resize(1); + + // First count the number of option sets we've got. Ignore + // LLDB_ALL_OPTION_SETS... + + uint32_t num_option_sets = 0; + + for (const auto &def : opt_defs) { + uint32_t this_usage_mask = def.usage_mask; + if (this_usage_mask == LLDB_OPT_SET_ALL) { + if (num_option_sets == 0) + num_option_sets = 1; + } else { + for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) { + if (this_usage_mask & (1 << j)) { + if (num_option_sets <= j) + num_option_sets = j + 1; + } + } + } + } + + if (num_option_sets > 0) { + m_required_options.resize(num_option_sets); + m_optional_options.resize(num_option_sets); + + for (const auto &def : opt_defs) { + for (uint32_t j = 0; j < num_option_sets; j++) { + if (def.usage_mask & 1 << j) { + if (def.required) + m_required_options[j].insert(def.short_option); + else + m_optional_options[j].insert(def.short_option); + } + } + } + } +} + +uint32_t Options::NumCommandOptions() { return GetDefinitions().size(); } + +Option *Options::GetLongOptions() { + // Check to see if this has already been done. + if (m_getopt_table.empty()) { + auto defs = GetDefinitions(); + if (defs.empty()) + return nullptr; + + std::map<int, uint32_t> option_seen; + + m_getopt_table.resize(defs.size() + 1); + for (size_t i = 0; i < defs.size(); ++i) { + const int short_opt = defs[i].short_option; + + m_getopt_table[i].definition = &defs[i]; + m_getopt_table[i].flag = nullptr; + m_getopt_table[i].val = short_opt; + + if (option_seen.find(short_opt) == option_seen.end()) { + option_seen[short_opt] = i; + } else if (short_opt) { + m_getopt_table[i].val = 0; + std::map<int, uint32_t>::const_iterator pos = + option_seen.find(short_opt); + StreamString strm; + if (defs[i].HasShortOption()) + Debugger::ReportError( + llvm::formatv( + "option[{0}] --{1} has a short option -{2} that " + "conflicts with option[{3}] --{4}, short option won't " + "be used for --{5}", + i, defs[i].long_option, short_opt, pos->second, + m_getopt_table[pos->second].definition->long_option, + defs[i].long_option) + .str()); + else + Debugger::ReportError( + llvm::formatv( + "option[{0}] --{1} has a short option {2:x} that " + "conflicts with option[{3}] --{4}, short option won't " + "be used for --{5}", + (int)i, defs[i].long_option, short_opt, pos->second, + m_getopt_table[pos->second].definition->long_option, + defs[i].long_option) + .str()); + } + } + + // getopt_long_only requires a NULL final entry in the table: + + m_getopt_table.back().definition = nullptr; + m_getopt_table.back().flag = nullptr; + m_getopt_table.back().val = 0; + } + + if (m_getopt_table.empty()) + return nullptr; + + return &m_getopt_table.front(); +} + +// This function takes INDENT, which tells how many spaces to output at the +// front of each line; SPACES, which is a string containing 80 spaces; and +// TEXT, which is the text that is to be output. It outputs the text, on +// multiple lines if necessary, to RESULT, with INDENT spaces at the front of +// each line. It breaks lines on spaces, tabs or newlines, shortening the line +// if necessary to not break in the middle of a word. It assumes that each +// output line should contain a maximum of OUTPUT_MAX_COLUMNS characters. + +void Options::OutputFormattedUsageText(Stream &strm, + const OptionDefinition &option_def, + uint32_t output_max_columns) { + std::string actual_text; + if (option_def.validator) { + const char *condition = option_def.validator->ShortConditionString(); + if (condition) { + actual_text = "["; + actual_text.append(condition); + actual_text.append("] "); + } + } + actual_text.append(option_def.usage_text); + + // Will it all fit on one line? + + if (static_cast<uint32_t>(actual_text.length() + strm.GetIndentLevel()) < + output_max_columns) { + // Output it as a single line. + strm.Indent(actual_text); + strm.EOL(); + } else { + // We need to break it up into multiple lines. + + int text_width = output_max_columns - strm.GetIndentLevel() - 1; + int start = 0; + int end = start; + int final_end = actual_text.length(); + int sub_len; + + while (end < final_end) { + // Don't start the 'text' on a space, since we're already outputting the + // indentation. + while ((start < final_end) && (actual_text[start] == ' ')) + start++; + + end = start + text_width; + if (end > final_end) + end = final_end; + else { + // If we're not at the end of the text, make sure we break the line on + // white space. + while (end > start && actual_text[end] != ' ' && + actual_text[end] != '\t' && actual_text[end] != '\n') + end--; + } + + sub_len = end - start; + if (start != 0) + strm.EOL(); + strm.Indent(); + assert(start < final_end); + assert(start + sub_len <= final_end); + strm.Write(actual_text.c_str() + start, sub_len); + start = end + 1; + } + strm.EOL(); + } +} + +bool Options::SupportsLongOption(const char *long_option) { + if (!long_option || !long_option[0]) + return false; + + auto opt_defs = GetDefinitions(); + if (opt_defs.empty()) + return false; + + const char *long_option_name = long_option; + if (long_option[0] == '-' && long_option[1] == '-') + long_option_name += 2; + + for (auto &def : opt_defs) { + if (!def.long_option) + continue; + + if (strcmp(def.long_option, long_option_name) == 0) + return true; + } + + return false; +} + +enum OptionDisplayType { + eDisplayBestOption, + eDisplayShortOption, + eDisplayLongOption +}; + +static bool PrintOption(const OptionDefinition &opt_def, + OptionDisplayType display_type, const char *header, + const char *footer, bool show_optional, Stream &strm) { + if (display_type == eDisplayShortOption && !opt_def.HasShortOption()) + return false; + + if (header && header[0]) + strm.PutCString(header); + + if (show_optional && !opt_def.required) + strm.PutChar('['); + const bool show_short_option = + opt_def.HasShortOption() && display_type != eDisplayLongOption; + if (show_short_option) + strm.Printf("-%c", opt_def.short_option); + else + strm.Printf("--%s", opt_def.long_option); + switch (opt_def.option_has_arg) { + case OptionParser::eNoArgument: + break; + case OptionParser::eRequiredArgument: + strm.Printf(" <%s>", CommandObject::GetArgumentName(opt_def.argument_type)); + break; + + case OptionParser::eOptionalArgument: + strm.Printf("%s[<%s>]", show_short_option ? "" : "=", + CommandObject::GetArgumentName(opt_def.argument_type)); + break; + } + if (show_optional && !opt_def.required) + strm.PutChar(']'); + if (footer && footer[0]) + strm.PutCString(footer); + return true; +} + +void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd, + uint32_t screen_width) { + auto opt_defs = GetDefinitions(); + const uint32_t save_indent_level = strm.GetIndentLevel(); + llvm::StringRef name = cmd.GetCommandName(); + StreamString arguments_str; + cmd.GetFormattedCommandArguments(arguments_str); + + const uint32_t num_options = NumCommandOptions(); + if (num_options == 0) + return; + + const bool only_print_args = cmd.IsDashDashCommand(); + if (!only_print_args) + strm.PutCString("\nCommand Options Usage:\n"); + + strm.IndentMore(2); + + // First, show each usage level set of options, e.g. <cmd> [options-for- + // level-0] + // <cmd> + // [options-for-level-1] + // etc. + + if (!only_print_args) { + uint32_t num_option_sets = GetRequiredOptions().size(); + for (uint32_t opt_set = 0; opt_set < num_option_sets; ++opt_set) { + if (opt_set > 0) + strm.Printf("\n"); + strm.Indent(name); + + // Different option sets may require different args. + StreamString args_str; + uint32_t opt_set_mask = 1 << opt_set; + cmd.GetFormattedCommandArguments(args_str, opt_set_mask); + + // First go through and print all options that take no arguments as a + // single string. If a command has "-a" "-b" and "-c", this will show up + // as [-abc] + + // We use a set here so that they will be sorted. + std::set<int> required_options; + std::set<int> optional_options; + + for (auto &def : opt_defs) { + if (def.usage_mask & opt_set_mask && def.HasShortOption() && + def.option_has_arg == OptionParser::eNoArgument) { + if (def.required) { + required_options.insert(def.short_option); + } else { + optional_options.insert(def.short_option); + } + } + } + + if (!required_options.empty()) { + strm.PutCString(" -"); + for (int short_option : required_options) + strm.PutChar(short_option); + } + + if (!optional_options.empty()) { + strm.PutCString(" [-"); + for (int short_option : optional_options) + strm.PutChar(short_option); + strm.PutChar(']'); + } + + // First go through and print the required options (list them up front). + for (auto &def : opt_defs) { + if (def.usage_mask & opt_set_mask && def.HasShortOption() && + def.required && def.option_has_arg != OptionParser::eNoArgument) + PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm); + } + + // Now go through again, and this time only print the optional options. + for (auto &def : opt_defs) { + if (def.usage_mask & opt_set_mask && !def.required && + def.option_has_arg != OptionParser::eNoArgument) + PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm); + } + + if (args_str.GetSize() > 0) { + if (cmd.WantsRawCommandString()) + strm.Printf(" --"); + strm << " " << args_str.GetString(); + } + } + } + + if ((only_print_args || cmd.WantsRawCommandString()) && + arguments_str.GetSize() > 0) { + if (!only_print_args) + strm.PutChar('\n'); + strm.Indent(name); + strm << " " << arguments_str.GetString(); + } + + if (!only_print_args) { + strm.Printf("\n\n"); + + // Now print out all the detailed information about the various options: + // long form, short form and help text: + // -short <argument> ( --long_name <argument> ) + // help text + + strm.IndentMore(5); + + // Put the command options in a sorted container, so we can output + // them alphabetically by short_option. + std::multimap<int, uint32_t> options_ordered; + for (auto def : llvm::enumerate(opt_defs)) + options_ordered.insert( + std::make_pair(def.value().short_option, def.index())); + + // Go through each option, find the table entry and write out the detailed + // help information for that option. + + bool first_option_printed = false; + + for (auto pos : options_ordered) { + // Put a newline separation between arguments + if (first_option_printed) + strm.EOL(); + else + first_option_printed = true; + + OptionDefinition opt_def = opt_defs[pos.second]; + + strm.Indent(); + if (opt_def.short_option && opt_def.HasShortOption()) { + PrintOption(opt_def, eDisplayShortOption, nullptr, nullptr, false, + strm); + PrintOption(opt_def, eDisplayLongOption, " ( ", " )", false, strm); + } else { + // Short option is not printable, just print long option + PrintOption(opt_def, eDisplayLongOption, nullptr, nullptr, false, strm); + } + strm.EOL(); + + strm.IndentMore(5); + + if (opt_def.usage_text) + OutputFormattedUsageText(strm, opt_def, screen_width); + if (!opt_def.enum_values.empty()) { + strm.Indent(); + strm.Printf("Values: "); + bool is_first = true; + for (const auto &enum_value : opt_def.enum_values) { + if (is_first) { + strm.Printf("%s", enum_value.string_value); + is_first = false; + } + else + strm.Printf(" | %s", enum_value.string_value); + } + strm.EOL(); + } + strm.IndentLess(5); + } + } + + // Restore the indent level + strm.SetIndentLevel(save_indent_level); +} + +// This function is called when we have been given a potentially incomplete set +// of options, such as when an alias has been defined (more options might be +// added at at the time the alias is invoked). We need to verify that the +// options in the set m_seen_options are all part of a set that may be used +// together, but m_seen_options may be missing some of the "required" options. + +bool Options::VerifyPartialOptions(CommandReturnObject &result) { + bool options_are_valid = false; + + int num_levels = GetRequiredOptions().size(); + if (num_levels) { + for (int i = 0; i < num_levels && !options_are_valid; ++i) { + // In this case we are treating all options as optional rather than + // required. Therefore a set of options is correct if m_seen_options is a + // subset of the union of m_required_options and m_optional_options. + OptionSet union_set; + OptionsSetUnion(GetRequiredOptions()[i], GetOptionalOptions()[i], + union_set); + if (IsASubset(m_seen_options, union_set)) + options_are_valid = true; + } + } + + return options_are_valid; +} + +bool Options::HandleOptionCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector, + CommandInterpreter &interpreter) { + // For now we just scan the completions to see if the cursor position is in + // an option or its argument. Otherwise we'll call HandleArgumentCompletion. + // In the future we can use completion to validate options as well if we + // want. + + auto opt_defs = GetDefinitions(); + + llvm::StringRef cur_opt_str = request.GetCursorArgumentPrefix(); + + for (size_t i = 0; i < opt_element_vector.size(); i++) { + size_t opt_pos = static_cast<size_t>(opt_element_vector[i].opt_pos); + size_t opt_arg_pos = static_cast<size_t>(opt_element_vector[i].opt_arg_pos); + int opt_defs_index = opt_element_vector[i].opt_defs_index; + if (opt_pos == request.GetCursorIndex()) { + // We're completing the option itself. + + if (opt_defs_index == OptionArgElement::eBareDash) { + // We're completing a bare dash. That means all options are open. + // FIXME: We should scan the other options provided and only complete + // options + // within the option group they belong to. + std::string opt_str = "-a"; + + for (auto &def : opt_defs) { + if (!def.short_option) + continue; + opt_str[1] = def.short_option; + request.AddCompletion(opt_str, def.usage_text); + } + + return true; + } else if (opt_defs_index == OptionArgElement::eBareDoubleDash) { + std::string full_name("--"); + for (auto &def : opt_defs) { + if (!def.short_option) + continue; + + full_name.erase(full_name.begin() + 2, full_name.end()); + full_name.append(def.long_option); + request.AddCompletion(full_name, def.usage_text); + } + return true; + } else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) { + // We recognized it, if it an incomplete long option, complete it + // anyway (getopt_long_only is happy with shortest unique string, but + // it's still a nice thing to do.) Otherwise return The string so the + // upper level code will know this is a full match and add the " ". + const OptionDefinition &opt = opt_defs[opt_defs_index]; + llvm::StringRef long_option = opt.long_option; + if (cur_opt_str.starts_with("--") && cur_opt_str != long_option) { + request.AddCompletion("--" + long_option.str(), opt.usage_text); + return true; + } else + request.AddCompletion(request.GetCursorArgumentPrefix()); + return true; + } else { + // FIXME - not handling wrong options yet: + // Check to see if they are writing a long option & complete it. + // I think we will only get in here if the long option table has two + // elements + // that are not unique up to this point. getopt_long_only does + // shortest unique match for long options already. + if (cur_opt_str.consume_front("--")) { + for (auto &def : opt_defs) { + llvm::StringRef long_option(def.long_option); + if (long_option.starts_with(cur_opt_str)) + request.AddCompletion("--" + long_option.str(), def.usage_text); + } + } + return true; + } + + } else if (opt_arg_pos == request.GetCursorIndex()) { + // Okay the cursor is on the completion of an argument. See if it has a + // completion, otherwise return no matches. + if (opt_defs_index != -1) { + HandleOptionArgumentCompletion(request, opt_element_vector, i, + interpreter); + return true; + } else { + // No completion callback means no completions... + return true; + } + + } else { + // Not the last element, keep going. + continue; + } + } + return false; +} + +void Options::HandleOptionArgumentCompletion( + CompletionRequest &request, OptionElementVector &opt_element_vector, + int opt_element_index, CommandInterpreter &interpreter) { + auto opt_defs = GetDefinitions(); + std::unique_ptr<SearchFilter> filter_up; + + int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; + + // See if this is an enumeration type option, and if so complete it here: + + const auto &enum_values = opt_defs[opt_defs_index].enum_values; + if (!enum_values.empty()) + for (const auto &enum_value : enum_values) + request.TryCompleteCurrentArg(enum_value.string_value); + + // If this is a source file or symbol type completion, and there is a -shlib + // option somewhere in the supplied arguments, then make a search filter for + // that shared library. + // FIXME: Do we want to also have an "OptionType" so we don't have to match + // string names? + + uint32_t completion_mask = opt_defs[opt_defs_index].completion_type; + + if (completion_mask == 0) { + lldb::CommandArgumentType option_arg_type = + opt_defs[opt_defs_index].argument_type; + if (option_arg_type != eArgTypeNone) { + const CommandObject::ArgumentTableEntry *arg_entry = + CommandObject::FindArgumentDataByType( + opt_defs[opt_defs_index].argument_type); + if (arg_entry) + completion_mask = arg_entry->completion_type; + } + } + + if (completion_mask & lldb::eSourceFileCompletion || + completion_mask & lldb::eSymbolCompletion) { + for (size_t i = 0; i < opt_element_vector.size(); i++) { + int cur_defs_index = opt_element_vector[i].opt_defs_index; + + // trying to use <0 indices will definitely cause problems + if (cur_defs_index == OptionArgElement::eUnrecognizedArg || + cur_defs_index == OptionArgElement::eBareDash || + cur_defs_index == OptionArgElement::eBareDoubleDash) + continue; + + int cur_arg_pos = opt_element_vector[i].opt_arg_pos; + const char *cur_opt_name = opt_defs[cur_defs_index].long_option; + + // If this is the "shlib" option and there was an argument provided, + // restrict it to that shared library. + if (cur_opt_name && strcmp(cur_opt_name, "shlib") == 0 && + cur_arg_pos != -1) { + const char *module_name = + request.GetParsedLine().GetArgumentAtIndex(cur_arg_pos); + if (module_name) { + FileSpec module_spec(module_name); + lldb::TargetSP target_sp = + interpreter.GetDebugger().GetSelectedTarget(); + // Search filters require a target... + if (target_sp) + filter_up = + std::make_unique<SearchFilterByModule>(target_sp, module_spec); + } + break; + } + } + } + + lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( + interpreter, completion_mask, request, filter_up.get()); +} + +void OptionGroupOptions::Append(OptionGroup *group) { + auto group_option_defs = group->GetDefinitions(); + for (uint32_t i = 0; i < group_option_defs.size(); ++i) { + m_option_infos.push_back(OptionInfo(group, i)); + m_option_defs.push_back(group_option_defs[i]); + } +} + +const OptionGroup *OptionGroupOptions::GetGroupWithOption(char short_opt) { + for (uint32_t i = 0; i < m_option_defs.size(); i++) { + OptionDefinition opt_def = m_option_defs[i]; + if (opt_def.short_option == short_opt) + return m_option_infos[i].option_group; + } + return nullptr; +} + +void OptionGroupOptions::Append(OptionGroup *group, uint32_t src_mask, + uint32_t dst_mask) { + auto group_option_defs = group->GetDefinitions(); + for (uint32_t i = 0; i < group_option_defs.size(); ++i) { + if (group_option_defs[i].usage_mask & src_mask) { + m_option_infos.push_back(OptionInfo(group, i)); + m_option_defs.push_back(group_option_defs[i]); + m_option_defs.back().usage_mask = dst_mask; + } + } +} + +void OptionGroupOptions::Append( + OptionGroup *group, llvm::ArrayRef<llvm::StringRef> exclude_long_options) { + auto group_option_defs = group->GetDefinitions(); + for (uint32_t i = 0; i < group_option_defs.size(); ++i) { + const auto &definition = group_option_defs[i]; + if (llvm::is_contained(exclude_long_options, definition.long_option)) + continue; + + m_option_infos.push_back(OptionInfo(group, i)); + m_option_defs.push_back(definition); + } +} + +void OptionGroupOptions::Finalize() { + m_did_finalize = true; +} + +Status OptionGroupOptions::SetOptionValue(uint32_t option_idx, + llvm::StringRef option_value, + ExecutionContext *execution_context) { + // After calling OptionGroupOptions::Append(...), you must finalize the + // groups by calling OptionGroupOptions::Finlize() + assert(m_did_finalize); + Status error; + if (option_idx < m_option_infos.size()) { + error = m_option_infos[option_idx].option_group->SetOptionValue( + m_option_infos[option_idx].option_index, option_value, + execution_context); + + } else { + error.SetErrorString("invalid option index"); // Shouldn't happen... + } + return error; +} + +void OptionGroupOptions::OptionParsingStarting( + ExecutionContext *execution_context) { + std::set<OptionGroup *> group_set; + OptionInfos::iterator pos, end = m_option_infos.end(); + for (pos = m_option_infos.begin(); pos != end; ++pos) { + OptionGroup *group = pos->option_group; + if (group_set.find(group) == group_set.end()) { + group->OptionParsingStarting(execution_context); + group_set.insert(group); + } + } +} +Status +OptionGroupOptions::OptionParsingFinished(ExecutionContext *execution_context) { + std::set<OptionGroup *> group_set; + Status error; + OptionInfos::iterator pos, end = m_option_infos.end(); + for (pos = m_option_infos.begin(); pos != end; ++pos) { + OptionGroup *group = pos->option_group; + if (group_set.find(group) == group_set.end()) { + error = group->OptionParsingFinished(execution_context); + group_set.insert(group); + if (error.Fail()) + return error; + } + } + return error; +} + +// OptionParser permutes the arguments while processing them, so we create a +// temporary array holding to avoid modification of the input arguments. The +// options themselves are never modified, but the API expects a char * anyway, +// hence the const_cast. +static std::vector<char *> GetArgvForParsing(const Args &args) { + std::vector<char *> result; + // OptionParser always skips the first argument as it is based on getopt(). + result.push_back(const_cast<char *>("<FAKE-ARG0>")); + for (const Args::ArgEntry &entry : args) + result.push_back(const_cast<char *>(entry.c_str())); + result.push_back(nullptr); + return result; +} + +// Given a permuted argument, find it's position in the original Args vector. +static Args::const_iterator FindOriginalIter(const char *arg, + const Args &original) { + return llvm::find_if( + original, [arg](const Args::ArgEntry &D) { return D.c_str() == arg; }); +} + +// Given a permuted argument, find it's index in the original Args vector. +static size_t FindOriginalIndex(const char *arg, const Args &original) { + return std::distance(original.begin(), FindOriginalIter(arg, original)); +} + +// Construct a new Args object, consisting of the entries from the original +// arguments, but in the permuted order. +static Args ReconstituteArgsAfterParsing(llvm::ArrayRef<char *> parsed, + const Args &original) { + Args result; + for (const char *arg : parsed) { + auto pos = FindOriginalIter(arg, original); + assert(pos != original.end()); + result.AppendArgument(pos->ref(), pos->GetQuoteChar()); + } + return result; +} + +static size_t FindArgumentIndexForOption(const Args &args, + const Option &long_option) { + std::string short_opt = llvm::formatv("-{0}", char(long_option.val)).str(); + std::string long_opt = + std::string(llvm::formatv("--{0}", long_option.definition->long_option)); + for (const auto &entry : llvm::enumerate(args)) { + if (entry.value().ref().starts_with(short_opt) || + entry.value().ref().starts_with(long_opt)) + return entry.index(); + } + + return size_t(-1); +} + +static std::string BuildShortOptions(const Option *long_options) { + std::string storage; + llvm::raw_string_ostream sstr(storage); + + // Leading : tells getopt to return a : for a missing option argument AND to + // suppress error messages. + sstr << ":"; + + for (size_t i = 0; long_options[i].definition != nullptr; ++i) { + if (long_options[i].flag == nullptr) { + sstr << (char)long_options[i].val; + switch (long_options[i].definition->option_has_arg) { + default: + case OptionParser::eNoArgument: + break; + case OptionParser::eRequiredArgument: + sstr << ":"; + break; + case OptionParser::eOptionalArgument: + sstr << "::"; + break; + } + } + } + return std::move(sstr.str()); +} + +llvm::Expected<Args> Options::ParseAlias(const Args &args, + OptionArgVector *option_arg_vector, + std::string &input_line) { + Option *long_options = GetLongOptions(); + + if (long_options == nullptr) { + return llvm::createStringError("Invalid long options"); + } + + std::string short_options = BuildShortOptions(long_options); + + Args args_copy = args; + std::vector<char *> argv = GetArgvForParsing(args); + + std::unique_lock<std::mutex> lock; + OptionParser::Prepare(lock); + int val; + while (true) { + int long_options_index = -1; + val = OptionParser::Parse(argv, short_options, long_options, + &long_options_index); + + if (val == ':') { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "last option requires an argument"); + } + + if (val == -1) + break; + + if (val == '?') { + return llvm::createStringError("Unknown or ambiguous option"); + } + + if (val == 0) + continue; + + OptionSeen(val); + + // Look up the long option index + if (long_options_index == -1) { + for (int j = 0; long_options[j].definition || long_options[j].flag || + long_options[j].val; + ++j) { + if (long_options[j].val == val) { + long_options_index = j; + break; + } + } + } + + // See if the option takes an argument, and see if one was supplied. + if (long_options_index == -1) { + return llvm::createStringError( + llvm::formatv("Invalid option with value '{0}'.", char(val)).str()); + } + + StreamString option_str; + option_str.Printf("-%c", val); + const OptionDefinition *def = long_options[long_options_index].definition; + int has_arg = + (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg; + + const char *option_arg = nullptr; + switch (has_arg) { + case OptionParser::eRequiredArgument: + if (OptionParser::GetOptionArgument() == nullptr) { + return llvm::createStringError( + llvm::formatv("Option '{0}' is missing argument specifier.", + option_str.GetString()) + .str()); + } + [[fallthrough]]; + case OptionParser::eOptionalArgument: + option_arg = OptionParser::GetOptionArgument(); + [[fallthrough]]; + case OptionParser::eNoArgument: + break; + default: + return llvm::createStringError( + llvm::formatv("error with options table; invalid value in has_arg " + "field for option '{0}'.", + char(val)) + .str()); + } + // Find option in the argument list; also see if it was supposed to take an + // argument and if one was supplied. Remove option (and argument, if + // given) from the argument list. Also remove them from the + // raw_input_string, if one was passed in. + // Note: We also need to preserve any option argument values that were + // surrounded by backticks, as we lose track of them in the + // option_args_vector. + size_t idx = + FindArgumentIndexForOption(args_copy, long_options[long_options_index]); + std::string option_to_insert; + if (option_arg) { + if (idx != size_t(-1) && has_arg) { + bool arg_has_backtick = args_copy[idx + 1].GetQuoteChar() == '`'; + if (arg_has_backtick) + option_to_insert = "`"; + option_to_insert += option_arg; + if (arg_has_backtick) + option_to_insert += "`"; + } else + option_to_insert = option_arg; + } else + option_to_insert = CommandInterpreter::g_no_argument; + + option_arg_vector->emplace_back(std::string(option_str.GetString()), + has_arg, option_to_insert); + + if (idx == size_t(-1)) + continue; + + if (!input_line.empty()) { + llvm::StringRef tmp_arg = args_copy[idx].ref(); + size_t pos = input_line.find(std::string(tmp_arg)); + if (pos != std::string::npos) + input_line.erase(pos, tmp_arg.size()); + } + args_copy.DeleteArgumentAtIndex(idx); + if ((option_to_insert != CommandInterpreter::g_no_argument) && + (OptionParser::GetOptionArgument() != nullptr) && + (idx < args_copy.GetArgumentCount()) && + (args_copy[idx].ref() == OptionParser::GetOptionArgument())) { + if (input_line.size() > 0) { + size_t pos = input_line.find(option_to_insert); + if (pos != std::string::npos) + input_line.erase(pos, option_to_insert.size()); + } + args_copy.DeleteArgumentAtIndex(idx); + } + } + + return std::move(args_copy); +} + +OptionElementVector Options::ParseForCompletion(const Args &args, + uint32_t cursor_index) { + OptionElementVector option_element_vector; + Option *long_options = GetLongOptions(); + option_element_vector.clear(); + + if (long_options == nullptr) + return option_element_vector; + + std::string short_options = BuildShortOptions(long_options); + + std::unique_lock<std::mutex> lock; + OptionParser::Prepare(lock); + OptionParser::EnableError(false); + + int val; + auto opt_defs = GetDefinitions(); + + std::vector<char *> dummy_vec = GetArgvForParsing(args); + + bool failed_once = false; + uint32_t dash_dash_pos = -1; + + while (true) { + bool missing_argument = false; + int long_options_index = -1; + + val = OptionParser::Parse(dummy_vec, short_options, long_options, + &long_options_index); + + if (val == -1) { + // When we're completing a "--" which is the last option on line, + if (failed_once) + break; + + failed_once = true; + + // If this is a bare "--" we mark it as such so we can complete it + // successfully later. Handling the "--" is a little tricky, since that + // may mean end of options or arguments, or the user might want to + // complete options by long name. I make this work by checking whether + // the cursor is in the "--" argument, and if so I assume we're + // completing the long option, otherwise I let it pass to + // OptionParser::Parse which will terminate the option parsing. Note, in + // either case we continue parsing the line so we can figure out what + // other options were passed. This will be useful when we come to + // restricting completions based on what other options we've seen on the + // line. + + if (static_cast<size_t>(OptionParser::GetOptionIndex()) < + dummy_vec.size() && + (strcmp(dummy_vec[OptionParser::GetOptionIndex() - 1], "--") == 0)) { + dash_dash_pos = FindOriginalIndex( + dummy_vec[OptionParser::GetOptionIndex() - 1], args); + if (dash_dash_pos == cursor_index) { + option_element_vector.push_back( + OptionArgElement(OptionArgElement::eBareDoubleDash, dash_dash_pos, + OptionArgElement::eBareDoubleDash)); + continue; + } else + break; + } else + break; + } else if (val == '?') { + option_element_vector.push_back(OptionArgElement( + OptionArgElement::eUnrecognizedArg, + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], + args), + OptionArgElement::eUnrecognizedArg)); + continue; + } else if (val == 0) { + continue; + } else if (val == ':') { + // This is a missing argument. + val = OptionParser::GetOptionErrorCause(); + missing_argument = true; + } + + OptionSeen(val); + + // Look up the long option index + if (long_options_index == -1) { + for (int j = 0; long_options[j].definition || long_options[j].flag || + long_options[j].val; + ++j) { + if (long_options[j].val == val) { + long_options_index = j; + break; + } + } + } + + // See if the option takes an argument, and see if one was supplied. + if (long_options_index >= 0) { + int opt_defs_index = -1; + for (size_t i = 0; i < opt_defs.size(); i++) { + if (opt_defs[i].short_option != val) + continue; + opt_defs_index = i; + break; + } + + const OptionDefinition *def = long_options[long_options_index].definition; + int has_arg = + (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg; + switch (has_arg) { + case OptionParser::eNoArgument: + option_element_vector.push_back(OptionArgElement( + opt_defs_index, + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], + args), + 0)); + break; + case OptionParser::eRequiredArgument: + if (OptionParser::GetOptionArgument() != nullptr) { + int arg_index; + if (missing_argument) + arg_index = -1; + else + arg_index = OptionParser::GetOptionIndex() - 2; + + option_element_vector.push_back(OptionArgElement( + opt_defs_index, + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2], + args), + arg_index)); + } else { + option_element_vector.push_back(OptionArgElement( + opt_defs_index, + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], + args), + -1)); + } + break; + case OptionParser::eOptionalArgument: + option_element_vector.push_back(OptionArgElement( + opt_defs_index, + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2], + args), + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], + args))); + break; + default: + // The options table is messed up. Here we'll just continue + option_element_vector.push_back(OptionArgElement( + OptionArgElement::eUnrecognizedArg, + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], + args), + OptionArgElement::eUnrecognizedArg)); + break; + } + } else { + option_element_vector.push_back(OptionArgElement( + OptionArgElement::eUnrecognizedArg, + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], + args), + OptionArgElement::eUnrecognizedArg)); + } + } + + // Finally we have to handle the case where the cursor index points at a + // single "-". We want to mark that in the option_element_vector, but only + // if it is not after the "--". But it turns out that OptionParser::Parse + // just ignores an isolated "-". So we have to look it up by hand here. We + // only care if it is AT the cursor position. Note, a single quoted dash is + // not the same as a single dash... + + const Args::ArgEntry &cursor = args[cursor_index]; + if ((static_cast<int32_t>(dash_dash_pos) == -1 || + cursor_index < dash_dash_pos) && + !cursor.IsQuoted() && cursor.ref() == "-") { + option_element_vector.push_back( + OptionArgElement(OptionArgElement::eBareDash, cursor_index, + OptionArgElement::eBareDash)); + } + return option_element_vector; +} + +llvm::Expected<Args> Options::Parse(const Args &args, + ExecutionContext *execution_context, + lldb::PlatformSP platform_sp, + bool require_validation) { + Status error; + Option *long_options = GetLongOptions(); + if (long_options == nullptr) { + return llvm::createStringError("Invalid long options."); + } + + std::string short_options = BuildShortOptions(long_options); + std::vector<char *> argv = GetArgvForParsing(args); + std::unique_lock<std::mutex> lock; + OptionParser::Prepare(lock); + int val; + while (true) { + int long_options_index = -1; + val = OptionParser::Parse(argv, short_options, long_options, + &long_options_index); + + if (val == ':') { + error.SetErrorString("last option requires an argument"); + break; + } + + if (val == -1) + break; + + // Did we get an error? + if (val == '?') { + error.SetErrorString("unknown or ambiguous option"); + break; + } + // The option auto-set itself + if (val == 0) + continue; + + OptionSeen(val); + + // Lookup the long option index + if (long_options_index == -1) { + for (int i = 0; long_options[i].definition || long_options[i].flag || + long_options[i].val; + ++i) { + if (long_options[i].val == val) { + long_options_index = i; + break; + } + } + } + // Call the callback with the option + if (long_options_index >= 0 && + long_options[long_options_index].definition) { + const OptionDefinition *def = long_options[long_options_index].definition; + + if (!platform_sp) { + // User did not pass in an explicit platform. Try to grab from the + // execution context. + TargetSP target_sp = + execution_context ? execution_context->GetTargetSP() : TargetSP(); + platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP(); + } + OptionValidator *validator = def->validator; + + if (!platform_sp && require_validation) { + // Caller requires validation but we cannot validate as we don't have + // the mandatory platform against which to validate. + return llvm::createStringError( + "cannot validate options: no platform available"); + } + + bool validation_failed = false; + if (platform_sp) { + // Ensure we have an execution context, empty or not. + ExecutionContext dummy_context; + ExecutionContext *exe_ctx_p = + execution_context ? execution_context : &dummy_context; + if (validator && !validator->IsValid(*platform_sp, *exe_ctx_p)) { + validation_failed = true; + error.SetErrorStringWithFormat("Option \"%s\" invalid. %s", + def->long_option, + def->validator->LongConditionString()); + } + } + + // As long as validation didn't fail, we set the option value. + if (!validation_failed) + error = + SetOptionValue(long_options_index, + (def->option_has_arg == OptionParser::eNoArgument) + ? nullptr + : OptionParser::GetOptionArgument(), + execution_context); + // If the Option setting returned an error, we should stop parsing + // and return the error. + if (error.Fail()) + break; + } else { + error.SetErrorStringWithFormat("invalid option with value '%i'", val); + } + } + + if (error.Fail()) + return error.ToError(); + + argv.pop_back(); + argv.erase(argv.begin(), argv.begin() + OptionParser::GetOptionIndex()); + return ReconstituteArgsAfterParsing(argv, args); +} + +llvm::Error lldb_private::CreateOptionParsingError( + llvm::StringRef option_arg, const char short_option, + llvm::StringRef long_option, llvm::StringRef additional_context) { + std::string buffer; + llvm::raw_string_ostream stream(buffer); + stream << "Invalid value ('" << option_arg << "') for -" << short_option; + if (!long_option.empty()) + stream << " (" << long_option << ")"; + if (!additional_context.empty()) + stream << ": " << additional_context; + return llvm::createStringError(llvm::inconvertibleErrorCode(), buffer); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/Property.cpp b/contrib/llvm-project/lldb/source/Interpreter/Property.cpp new file mode 100644 index 000000000000..56e45363be89 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/Property.cpp @@ -0,0 +1,309 @@ +//===-- Property.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 "lldb/Interpreter/Property.h" + +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValues.h" +#include "lldb/Target/Language.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +Property::Property(const PropertyDefinition &definition) + : m_name(definition.name), m_description(definition.description), + m_is_global(definition.global) { + switch (definition.type) { + case OptionValue::eTypeInvalid: + case OptionValue::eTypeProperties: + break; + case OptionValue::eTypeArch: + // "definition.default_uint_value" is not used + // "definition.default_cstr_value" as a string value that represents the + // default string value for the architecture/triple + m_value_sp = + std::make_shared<OptionValueArch>(definition.default_cstr_value); + break; + + case OptionValue::eTypeArgs: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp = std::make_shared<OptionValueArgs>(); + break; + + case OptionValue::eTypeArray: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp = + std::make_shared<OptionValueArray>(OptionValue::ConvertTypeToMask( + (OptionValue::Type)definition.default_uint_value)); + break; + + case OptionValue::eTypeBoolean: + // "definition.default_uint_value" is the default boolean value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the + // default value. + if (definition.default_cstr_value) + m_value_sp = + std::make_shared<OptionValueBoolean>(OptionArgParser::ToBoolean( + llvm::StringRef(definition.default_cstr_value), false, nullptr)); + else + m_value_sp = std::make_shared<OptionValueBoolean>( + definition.default_uint_value != 0); + break; + + case OptionValue::eTypeChar: { + llvm::StringRef s(definition.default_cstr_value ? definition.default_cstr_value : ""); + m_value_sp = std::make_shared<OptionValueChar>( + OptionArgParser::ToChar(s, '\0', nullptr)); + break; + } + case OptionValue::eTypeDictionary: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp = std::make_shared<OptionValueDictionary>( + OptionValue::ConvertTypeToMask( + (OptionValue::Type)definition.default_uint_value), + definition.enum_values); + break; + + case OptionValue::eTypeEnum: + // "definition.default_uint_value" is the default enumeration value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the + // default value. + { + OptionValueEnumeration *enum_value = new OptionValueEnumeration( + definition.enum_values, definition.default_uint_value); + m_value_sp.reset(enum_value); + if (definition.default_cstr_value) { + if (enum_value + ->SetValueFromString( + llvm::StringRef(definition.default_cstr_value)) + .Success()) { + enum_value->SetDefaultValue(enum_value->GetCurrentValue()); + // Call Clear() since we don't want the value to appear as having + // been set since we called SetValueFromString() above. Clear will + // set the current value to the default and clear the boolean that + // says that the value has been set. + enum_value->Clear(); + } + } + } + break; + + case OptionValue::eTypeFileLineColumn: + // "definition.default_uint_value" is not used for a + // OptionValue::eTypeFileSpecList + m_value_sp = std::make_shared<OptionValueFileColonLine>(); + break; + + case OptionValue::eTypeFileSpec: { + // "definition.default_uint_value" represents if the + // "definition.default_cstr_value" should be resolved or not + const bool resolve = definition.default_uint_value != 0; + FileSpec file_spec = FileSpec(definition.default_cstr_value); + if (resolve) + FileSystem::Instance().Resolve(file_spec); + m_value_sp = std::make_shared<OptionValueFileSpec>(file_spec, resolve); + break; + } + + case OptionValue::eTypeFileSpecList: + // "definition.default_uint_value" is not used for a + // OptionValue::eTypeFileSpecList + m_value_sp = std::make_shared<OptionValueFileSpecList>(); + break; + + case OptionValue::eTypeFormat: + // "definition.default_uint_value" is the default format enumeration value + // if "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the + // default value. + { + Format new_format = eFormatInvalid; + if (definition.default_cstr_value) + OptionArgParser::ToFormat(definition.default_cstr_value, new_format, + nullptr); + else + new_format = (Format)definition.default_uint_value; + m_value_sp = std::make_shared<OptionValueFormat>(new_format); + } + break; + + case OptionValue::eTypeLanguage: + // "definition.default_uint_value" is the default language enumeration + // value if "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the + // default value. + { + LanguageType new_lang = eLanguageTypeUnknown; + if (definition.default_cstr_value) + Language::GetLanguageTypeFromString( + llvm::StringRef(definition.default_cstr_value)); + else + new_lang = (LanguageType)definition.default_uint_value; + m_value_sp = std::make_shared<OptionValueLanguage>(new_lang); + } + break; + + case OptionValue::eTypeFormatEntity: + // "definition.default_cstr_value" as a string value that represents the + // default + m_value_sp = std::make_shared<OptionValueFormatEntity>( + definition.default_cstr_value); + break; + + case OptionValue::eTypePathMap: + // "definition.default_uint_value" tells us if notifications should occur + // for path mappings + m_value_sp = std::make_shared<OptionValuePathMappings>( + definition.default_uint_value != 0); + break; + + case OptionValue::eTypeRegex: + // "definition.default_uint_value" is used to the regular expression flags + // "definition.default_cstr_value" the default regular expression value + // value. + m_value_sp = + std::make_shared<OptionValueRegex>(definition.default_cstr_value); + break; + + case OptionValue::eTypeSInt64: { + // "definition.default_uint_value" is the default integer value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the + // default value. + int64_t value = 0; + // FIXME: improve error handling for llvm::to_integer() + if (definition.default_cstr_value) + llvm::to_integer(definition.default_cstr_value, value); + m_value_sp = std::make_shared<OptionValueSInt64>( + definition.default_cstr_value ? value : definition.default_uint_value); + break; + } + case OptionValue::eTypeUInt64: { + uint64_t value = 0; + // FIXME: improve error handling for llvm::to_integer() + if (definition.default_cstr_value) + llvm::to_integer(definition.default_cstr_value, value); + // "definition.default_uint_value" is the default unsigned integer value if + // "definition.default_cstr_value" is NULL, otherwise interpret + // "definition.default_cstr_value" as a string value that represents the + // default value. + m_value_sp = std::make_shared<OptionValueUInt64>( + definition.default_cstr_value ? value : definition.default_uint_value); + break; + } + case OptionValue::eTypeUUID: + // "definition.default_uint_value" is not used for a OptionValue::eTypeUUID + // "definition.default_cstr_value" can contain a default UUID value + { + UUID uuid; + if (definition.default_cstr_value) + uuid.SetFromStringRef(definition.default_cstr_value); + m_value_sp = std::make_shared<OptionValueUUID>(uuid); + } + break; + + case OptionValue::eTypeString: + // "definition.default_uint_value" can contain the string option flags + // OR'ed together "definition.default_cstr_value" can contain a default + // string value + { + OptionValueString *string_value = + new OptionValueString(definition.default_cstr_value); + if (definition.default_uint_value != 0) + string_value->GetOptions().Reset(definition.default_uint_value); + m_value_sp.reset(string_value); + } + break; + } + assert(m_value_sp && "invalid property definition"); +} + +Property::Property(llvm::StringRef name, llvm::StringRef desc, bool is_global, + const lldb::OptionValueSP &value_sp) + : m_name(name), m_description(desc), m_value_sp(value_sp), + m_is_global(is_global) {} + +bool Property::DumpQualifiedName(Stream &strm) const { + if (!m_name.empty()) { + if (m_value_sp->DumpQualifiedName(strm)) + strm.PutChar('.'); + strm << m_name; + return true; + } + return false; +} + +void Property::Dump(const ExecutionContext *exe_ctx, Stream &strm, + uint32_t dump_mask) const { + if (m_value_sp) { + const bool dump_desc = dump_mask & OptionValue::eDumpOptionDescription; + const bool dump_cmd = dump_mask & OptionValue::eDumpOptionCommand; + const bool transparent = m_value_sp->ValueIsTransparent(); + if (dump_cmd && !transparent) + strm << "settings set -f "; + if (dump_desc || !transparent) { + if ((dump_mask & OptionValue::eDumpOptionName) && !m_name.empty()) { + DumpQualifiedName(strm); + if (dump_mask & ~OptionValue::eDumpOptionName) + strm.PutChar(' '); + } + } + if (dump_desc) { + llvm::StringRef desc = GetDescription(); + if (!desc.empty()) + strm << "-- " << desc; + + if (transparent && (dump_mask == (OptionValue::eDumpOptionName | + OptionValue::eDumpOptionDescription))) + strm.EOL(); + } + m_value_sp->DumpValue(exe_ctx, strm, dump_mask); + } +} + +void Property::DumpDescription(CommandInterpreter &interpreter, Stream &strm, + uint32_t output_width, + bool display_qualified_name) const { + if (!m_value_sp) + return; + llvm::StringRef desc = GetDescription(); + + if (desc.empty()) + return; + + StreamString qualified_name; + const OptionValueProperties *sub_properties = m_value_sp->GetAsProperties(); + if (sub_properties) { + strm.EOL(); + + if (m_value_sp->DumpQualifiedName(qualified_name)) + strm.Printf("'%s' variables:\n\n", qualified_name.GetData()); + sub_properties->DumpAllDescriptions(interpreter, strm); + } else { + if (display_qualified_name) { + StreamString qualified_name; + DumpQualifiedName(qualified_name); + interpreter.OutputFormattedHelpText(strm, qualified_name.GetString(), + "--", desc, output_width); + } else { + interpreter.OutputFormattedHelpText(strm, m_name, "--", desc, + output_width); + } + } +} + +void Property::SetValueChangedCallback(std::function<void()> callback) { + if (m_value_sp) + m_value_sp->SetValueChangedCallback(std::move(callback)); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp b/contrib/llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp new file mode 100644 index 000000000000..fa23964a52ff --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -0,0 +1,276 @@ +//===-- ScriptInterpreter.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 "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StringList.h" +#if defined(_WIN32) +#include "lldb/Host/windows/ConnectionGenericFileWindows.h" +#endif +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <optional> +#include <string> + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreter::ScriptInterpreter(Debugger &debugger, + lldb::ScriptLanguage script_lang) + : m_debugger(debugger), m_script_lang(script_lang) {} + +void ScriptInterpreter::CollectDataForBreakpointCommandCallback( + std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, + CommandReturnObject &result) { + result.AppendError( + "This script interpreter does not support breakpoint callbacks."); +} + +void ScriptInterpreter::CollectDataForWatchpointCommandCallback( + WatchpointOptions *bp_options, CommandReturnObject &result) { + result.AppendError( + "This script interpreter does not support watchpoint callbacks."); +} + +StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() { + return nullptr; +} + +bool ScriptInterpreter::LoadScriptingModule(const char *filename, + const LoadScriptOptions &options, + lldb_private::Status &error, + StructuredData::ObjectSP *module_sp, + FileSpec extra_search_dir) { + error.SetErrorString( + "This script interpreter does not support importing modules."); + return false; +} + +std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) { + switch (language) { + case eScriptLanguageNone: + return "None"; + case eScriptLanguagePython: + return "Python"; + case eScriptLanguageLua: + return "Lua"; + case eScriptLanguageUnknown: + return "Unknown"; + } + llvm_unreachable("Unhandled ScriptInterpreter!"); +} + +lldb::DataExtractorSP +ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const { + return data.m_opaque_sp; +} + +lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint( + const lldb::SBBreakpoint &breakpoint) const { + return breakpoint.m_opaque_wp.lock(); +} + +lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo( + const lldb::SBAttachInfo &attach_info) const { + return attach_info.m_opaque_sp; +} + +lldb::ProcessLaunchInfoSP ScriptInterpreter::GetOpaqueTypeFromSBLaunchInfo( + const lldb::SBLaunchInfo &launch_info) const { + return std::make_shared<ProcessLaunchInfo>( + *reinterpret_cast<ProcessLaunchInfo *>(launch_info.m_opaque_sp.get())); +} + +Status +ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const { + if (error.m_opaque_up) + return *error.m_opaque_up; + + return Status(); +} + +Event * +ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const { + return event.m_opaque_ptr; +} + +lldb::StreamSP ScriptInterpreter::GetOpaqueTypeFromSBStream( + const lldb::SBStream &stream) const { + if (stream.m_opaque_up) { + lldb::StreamSP s = std::make_shared<lldb_private::StreamString>(); + *s << reinterpret_cast<StreamString *>(stream.m_opaque_up.get())->m_packet; + return s; + } + + return nullptr; +} + +std::optional<MemoryRegionInfo> +ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo( + const lldb::SBMemoryRegionInfo &mem_region) const { + if (!mem_region.m_opaque_up) + return std::nullopt; + return *mem_region.m_opaque_up.get(); +} + +lldb::ScriptLanguage +ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) { + if (language.equals_insensitive(LanguageToString(eScriptLanguageNone))) + return eScriptLanguageNone; + if (language.equals_insensitive(LanguageToString(eScriptLanguagePython))) + return eScriptLanguagePython; + if (language.equals_insensitive(LanguageToString(eScriptLanguageLua))) + return eScriptLanguageLua; + return eScriptLanguageUnknown; +} + +Status ScriptInterpreter::SetBreakpointCommandCallback( + std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, + const char *callback_text) { + Status error; + for (BreakpointOptions &bp_options : bp_options_vec) { + error = SetBreakpointCommandCallback(bp_options, callback_text, + /*is_callback=*/false); + if (!error.Success()) + break; + } + return error; +} + +Status ScriptInterpreter::SetBreakpointCommandCallbackFunction( + std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, + const char *function_name, StructuredData::ObjectSP extra_args_sp) { + Status error; + for (BreakpointOptions &bp_options : bp_options_vec) { + error = SetBreakpointCommandCallbackFunction(bp_options, function_name, + extra_args_sp); + if (!error.Success()) + return error; + } + return error; +} + +std::unique_ptr<ScriptInterpreterLocker> +ScriptInterpreter::AcquireInterpreterLock() { + return std::make_unique<ScriptInterpreterLocker>(); +} + +static void ReadThreadBytesReceived(void *baton, const void *src, + size_t src_len) { + if (src && src_len) { + Stream *strm = (Stream *)baton; + strm->Write(src, src_len); + strm->Flush(); + } +} + +llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> +ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger, + CommandReturnObject *result) { + if (enable_io) + return std::unique_ptr<ScriptInterpreterIORedirect>( + new ScriptInterpreterIORedirect(debugger, result)); + + auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), + File::eOpenOptionReadOnly); + if (!nullin) + return nullin.takeError(); + + auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), + File::eOpenOptionWriteOnly); + if (!nullout) + return nullin.takeError(); + + return std::unique_ptr<ScriptInterpreterIORedirect>( + new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout))); +} + +ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( + std::unique_ptr<File> input, std::unique_ptr<File> output) + : m_input_file_sp(std::move(input)), + m_output_file_sp(std::make_shared<StreamFile>(std::move(output))), + m_error_file_sp(m_output_file_sp), + m_communication("lldb.ScriptInterpreterIORedirect.comm"), + m_disconnect(false) {} + +ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( + Debugger &debugger, CommandReturnObject *result) + : m_communication("lldb.ScriptInterpreterIORedirect.comm"), + m_disconnect(false) { + + if (result) { + m_input_file_sp = debugger.GetInputFileSP(); + + Pipe pipe; + Status pipe_result = pipe.CreateNew(false); +#if defined(_WIN32) + lldb::file_t read_file = pipe.GetReadNativeHandle(); + pipe.ReleaseReadFileDescriptor(); + std::unique_ptr<ConnectionGenericFile> conn_up = + std::make_unique<ConnectionGenericFile>(read_file, true); +#else + std::unique_ptr<ConnectionFileDescriptor> conn_up = + std::make_unique<ConnectionFileDescriptor>( + pipe.ReleaseReadFileDescriptor(), true); +#endif + + if (conn_up->IsConnected()) { + m_communication.SetConnection(std::move(conn_up)); + m_communication.SetReadThreadBytesReceivedCallback( + ReadThreadBytesReceived, &result->GetOutputStream()); + m_communication.StartReadThread(); + m_disconnect = true; + + FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w"); + m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true); + m_error_file_sp = m_output_file_sp; + if (outfile_handle) + ::setbuf(outfile_handle, nullptr); + + result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP()); + result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP()); + } + } + + if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp) + debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp, + m_error_file_sp); +} + +void ScriptInterpreterIORedirect::Flush() { + if (m_output_file_sp) + m_output_file_sp->Flush(); + if (m_error_file_sp) + m_error_file_sp->Flush(); +} + +ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() { + if (!m_disconnect) + return; + + assert(m_output_file_sp); + assert(m_error_file_sp); + assert(m_output_file_sp == m_error_file_sp); + + // Close the write end of the pipe since we are done with our one line + // script. This should cause the read thread that output_comm is using to + // exit. + m_output_file_sp->GetFile().Close(); + // The close above should cause this thread to exit when it gets to the end + // of file, so let it get all its data. + m_communication.JoinReadThread(); + // Now we can close the read end of the pipe. + m_communication.Disconnect(); +} diff --git a/contrib/llvm-project/lldb/source/Interpreter/embedded_interpreter.py b/contrib/llvm-project/lldb/source/Interpreter/embedded_interpreter.py new file mode 100644 index 000000000000..a487592ef1ae --- /dev/null +++ b/contrib/llvm-project/lldb/source/Interpreter/embedded_interpreter.py @@ -0,0 +1,136 @@ +import sys + +if sys.version_info[0] < 3: + import __builtin__ as builtins +else: + import builtins +import code +import lldb +import traceback + +try: + import readline + import rlcompleter +except ImportError: + have_readline = False +except AttributeError: + # This exception gets hit by the rlcompleter when Linux is using + # the readline suppression import. + have_readline = False +else: + have_readline = True + if "libedit" in readline.__doc__: + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("tab: complete") + +# When running one line, we might place the string to run in this string +# in case it would be hard to correctly escape a string's contents + +g_run_one_line_str = None + + +def get_terminal_size(fd): + try: + import fcntl + import termios + import struct + + hw = struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234")) + except: + hw = (0, 0) + return hw + + +class LLDBExit(SystemExit): + pass + + +def strip_and_check_exit(line): + line = line.rstrip() + if line in ("exit", "quit"): + raise LLDBExit + return line + + +def readfunc(prompt): + line = input(prompt) + return strip_and_check_exit(line) + + +def readfunc_stdio(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + line = sys.stdin.readline() + # Readline always includes a trailing newline character unless the file + # ends with an incomplete line. An empty line indicates EOF. + if not line: + raise EOFError + return strip_and_check_exit(line) + + +def run_python_interpreter(local_dict): + # Pass in the dictionary, for continuity from one session to the next. + try: + fd = sys.stdin.fileno() + interacted = False + if get_terminal_size(fd)[1] == 0: + try: + import termios + + old = termios.tcgetattr(fd) + if old[3] & termios.ECHO: + # Need to turn off echoing and restore + new = termios.tcgetattr(fd) + new[3] = new[3] & ~termios.ECHO + try: + termios.tcsetattr(fd, termios.TCSADRAIN, new) + interacted = True + code.interact( + banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()'.", + readfunc=readfunc_stdio, + local=local_dict, + ) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old) + except: + pass + # Don't need to turn off echoing + if not interacted: + code.interact( + banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.", + readfunc=readfunc_stdio, + local=local_dict, + ) + else: + # We have a real interactive terminal + code.interact( + banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.", + readfunc=readfunc, + local=local_dict, + ) + except LLDBExit: + pass + except SystemExit as e: + if e.code: + print("Script exited with code %s" % e.code) + + +def run_one_line(local_dict, input_string): + global g_run_one_line_str + try: + input_string = strip_and_check_exit(input_string) + repl = code.InteractiveConsole(local_dict) + if input_string: + # A newline is appended to support one-line statements containing + # control flow. For example "if True: print(1)" silently does + # nothing, but works with a newline: "if True: print(1)\n". + input_string += "\n" + repl.runsource(input_string) + elif g_run_one_line_str: + repl.runsource(g_run_one_line_str) + except LLDBExit: + pass + except SystemExit as e: + if e.code: + print("Script exited with code %s" % e.code) |