Path: blob/main/contrib/llvm-project/lldb/source/Commands/CommandObjectCommands.cpp
39587 views
//===-- CommandObjectCommands.cpp -----------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "CommandObjectCommands.h"9#include "CommandObjectHelp.h"10#include "CommandObjectRegexCommand.h"11#include "lldb/Core/Debugger.h"12#include "lldb/Core/IOHandler.h"13#include "lldb/Interpreter/CommandHistory.h"14#include "lldb/Interpreter/CommandInterpreter.h"15#include "lldb/Interpreter/CommandOptionArgumentTable.h"16#include "lldb/Interpreter/CommandReturnObject.h"17#include "lldb/Interpreter/OptionArgParser.h"18#include "lldb/Interpreter/OptionValueBoolean.h"19#include "lldb/Interpreter/OptionValueString.h"20#include "lldb/Interpreter/OptionValueUInt64.h"21#include "lldb/Interpreter/Options.h"22#include "lldb/Interpreter/ScriptInterpreter.h"23#include "lldb/Utility/Args.h"24#include "lldb/Utility/StringList.h"25#include "llvm/ADT/StringRef.h"26#include <optional>2728using namespace lldb;29using namespace lldb_private;3031// CommandObjectCommandsSource3233#define LLDB_OPTIONS_source34#include "CommandOptions.inc"3536class CommandObjectCommandsSource : public CommandObjectParsed {37public:38CommandObjectCommandsSource(CommandInterpreter &interpreter)39: CommandObjectParsed(40interpreter, "command source",41"Read and execute LLDB commands from the file <filename>.",42nullptr) {43AddSimpleArgumentList(eArgTypeFilename);44}4546~CommandObjectCommandsSource() override = default;4748std::optional<std::string> GetRepeatCommand(Args ¤t_command_args,49uint32_t index) override {50return std::string("");51}5253Options *GetOptions() override { return &m_options; }5455protected:56class CommandOptions : public Options {57public:58CommandOptions()59: m_stop_on_error(true), m_silent_run(false), m_stop_on_continue(true),60m_cmd_relative_to_command_file(false) {}6162~CommandOptions() override = default;6364Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,65ExecutionContext *execution_context) override {66Status error;67const int short_option = m_getopt_table[option_idx].val;6869switch (short_option) {70case 'e':71error = m_stop_on_error.SetValueFromString(option_arg);72break;7374case 'c':75error = m_stop_on_continue.SetValueFromString(option_arg);76break;7778case 'C':79m_cmd_relative_to_command_file = true;80break;8182case 's':83error = m_silent_run.SetValueFromString(option_arg);84break;8586default:87llvm_unreachable("Unimplemented option");88}8990return error;91}9293void OptionParsingStarting(ExecutionContext *execution_context) override {94m_stop_on_error.Clear();95m_silent_run.Clear();96m_stop_on_continue.Clear();97m_cmd_relative_to_command_file.Clear();98}99100llvm::ArrayRef<OptionDefinition> GetDefinitions() override {101return llvm::ArrayRef(g_source_options);102}103104// Instance variables to hold the values for command options.105106OptionValueBoolean m_stop_on_error;107OptionValueBoolean m_silent_run;108OptionValueBoolean m_stop_on_continue;109OptionValueBoolean m_cmd_relative_to_command_file;110};111112void DoExecute(Args &command, CommandReturnObject &result) override {113if (command.GetArgumentCount() != 1) {114result.AppendErrorWithFormat(115"'%s' takes exactly one executable filename argument.\n",116GetCommandName().str().c_str());117return;118}119120FileSpec source_dir = {};121if (m_options.m_cmd_relative_to_command_file) {122source_dir = GetDebugger().GetCommandInterpreter().GetCurrentSourceDir();123if (!source_dir) {124result.AppendError("command source -C can only be specified "125"from a command file");126result.SetStatus(eReturnStatusFailed);127return;128}129}130131FileSpec cmd_file(command[0].ref());132if (source_dir) {133// Prepend the source_dir to the cmd_file path:134if (!cmd_file.IsRelative()) {135result.AppendError("command source -C can only be used "136"with a relative path.");137result.SetStatus(eReturnStatusFailed);138return;139}140cmd_file.MakeAbsolute(source_dir);141}142143FileSystem::Instance().Resolve(cmd_file);144145CommandInterpreterRunOptions options;146// If any options were set, then use them147if (m_options.m_stop_on_error.OptionWasSet() ||148m_options.m_silent_run.OptionWasSet() ||149m_options.m_stop_on_continue.OptionWasSet()) {150if (m_options.m_stop_on_continue.OptionWasSet())151options.SetStopOnContinue(152m_options.m_stop_on_continue.GetCurrentValue());153154if (m_options.m_stop_on_error.OptionWasSet())155options.SetStopOnError(m_options.m_stop_on_error.GetCurrentValue());156157// Individual silent setting is override for global command echo settings.158if (m_options.m_silent_run.GetCurrentValue()) {159options.SetSilent(true);160} else {161options.SetPrintResults(true);162options.SetPrintErrors(true);163options.SetEchoCommands(m_interpreter.GetEchoCommands());164options.SetEchoCommentCommands(m_interpreter.GetEchoCommentCommands());165}166}167168m_interpreter.HandleCommandsFromFile(cmd_file, options, result);169}170171CommandOptions m_options;172};173174#pragma mark CommandObjectCommandsAlias175// CommandObjectCommandsAlias176177#define LLDB_OPTIONS_alias178#include "CommandOptions.inc"179180static const char *g_python_command_instructions =181"Enter your Python command(s). Type 'DONE' to end.\n"182"You must define a Python function with this signature:\n"183"def my_command_impl(debugger, args, exe_ctx, result, internal_dict):\n";184185class CommandObjectCommandsAlias : public CommandObjectRaw {186protected:187class CommandOptions : public OptionGroup {188public:189CommandOptions() = default;190191~CommandOptions() override = default;192193llvm::ArrayRef<OptionDefinition> GetDefinitions() override {194return llvm::ArrayRef(g_alias_options);195}196197Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,198ExecutionContext *execution_context) override {199Status error;200201const int short_option = GetDefinitions()[option_idx].short_option;202std::string option_str(option_value);203204switch (short_option) {205case 'h':206m_help.SetCurrentValue(option_str);207m_help.SetOptionWasSet();208break;209210case 'H':211m_long_help.SetCurrentValue(option_str);212m_long_help.SetOptionWasSet();213break;214215default:216llvm_unreachable("Unimplemented option");217}218219return error;220}221222void OptionParsingStarting(ExecutionContext *execution_context) override {223m_help.Clear();224m_long_help.Clear();225}226227OptionValueString m_help;228OptionValueString m_long_help;229};230231OptionGroupOptions m_option_group;232CommandOptions m_command_options;233234public:235Options *GetOptions() override { return &m_option_group; }236237CommandObjectCommandsAlias(CommandInterpreter &interpreter)238: CommandObjectRaw(239interpreter, "command alias",240"Define a custom command in terms of an existing command.") {241m_option_group.Append(&m_command_options);242m_option_group.Finalize();243244SetHelpLong(245"'alias' allows the user to create a short-cut or abbreviation for long \246commands, multi-word commands, and commands that take particular options. \247Below are some simple examples of how one might use the 'alias' command:"248R"(249250(lldb) command alias sc script251252Creates the abbreviation 'sc' for the 'script' command.253254(lldb) command alias bp breakpoint255256)"257" Creates the abbreviation 'bp' for the 'breakpoint' command. Since \258breakpoint commands are two-word commands, the user would still need to \259enter the second word after 'bp', e.g. 'bp enable' or 'bp delete'."260R"(261262(lldb) command alias bpl breakpoint list263264Creates the abbreviation 'bpl' for the two-word command 'breakpoint list'.265266)"267"An alias can include some options for the command, with the values either \268filled in at the time the alias is created, or specified as positional \269arguments, to be filled in when the alias is invoked. The following example \270shows how to create aliases with options:"271R"(272273(lldb) command alias bfl breakpoint set -f %1 -l %2274275)"276" Creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \277options already part of the alias. So if the user wants to set a breakpoint \278by file and line without explicitly having to use the -f and -l options, the \279user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \280for the actual arguments that will be passed when the alias command is used. \281The number in the placeholder refers to the position/order the actual value \282occupies when the alias is used. All the occurrences of '%1' in the alias \283will be replaced with the first argument, all the occurrences of '%2' in the \284alias will be replaced with the second argument, and so on. This also allows \285actual arguments to be used multiple times within an alias (see 'process \286launch' example below)."287R"(288289)"290"Note: the positional arguments must substitute as whole words in the resultant \291command, so you can't at present do something like this to append the file extension \292\".cpp\":"293R"(294295(lldb) command alias bcppfl breakpoint set -f %1.cpp -l %2296297)"298"For more complex aliasing, use the \"command regex\" command instead. In the \299'bfl' case above, the actual file value will be filled in with the first argument \300following 'bfl' and the actual line number value will be filled in with the second \301argument. The user would use this alias as follows:"302R"(303304(lldb) command alias bfl breakpoint set -f %1 -l %2305(lldb) bfl my-file.c 137306307This would be the same as if the user had entered 'breakpoint set -f my-file.c -l 137'.308309Another example:310311(lldb) command alias pltty process launch -s -o %1 -e %1312(lldb) pltty /dev/tty0313314Interpreted as 'process launch -s -o /dev/tty0 -e /dev/tty0'315316)"317"If the user always wanted to pass the same value to a particular option, the \318alias could be defined with that value directly in the alias as a constant, \319rather than using a positional placeholder:"320R"(321322(lldb) command alias bl3 breakpoint set -f %1 -l 3323324Always sets a breakpoint on line 3 of whatever file is indicated.)");325326CommandArgumentEntry arg1;327CommandArgumentEntry arg2;328CommandArgumentEntry arg3;329CommandArgumentData alias_arg;330CommandArgumentData cmd_arg;331CommandArgumentData options_arg;332333// Define the first (and only) variant of this arg.334alias_arg.arg_type = eArgTypeAliasName;335alias_arg.arg_repetition = eArgRepeatPlain;336337// There is only one variant this argument could be; put it into the338// argument entry.339arg1.push_back(alias_arg);340341// Define the first (and only) variant of this arg.342cmd_arg.arg_type = eArgTypeCommandName;343cmd_arg.arg_repetition = eArgRepeatPlain;344345// There is only one variant this argument could be; put it into the346// argument entry.347arg2.push_back(cmd_arg);348349// Define the first (and only) variant of this arg.350options_arg.arg_type = eArgTypeAliasOptions;351options_arg.arg_repetition = eArgRepeatOptional;352353// There is only one variant this argument could be; put it into the354// argument entry.355arg3.push_back(options_arg);356357// Push the data for the first argument into the m_arguments vector.358m_arguments.push_back(arg1);359m_arguments.push_back(arg2);360m_arguments.push_back(arg3);361}362363~CommandObjectCommandsAlias() override = default;364365protected:366void DoExecute(llvm::StringRef raw_command_line,367CommandReturnObject &result) override {368if (raw_command_line.empty()) {369result.AppendError("'command alias' requires at least two arguments");370return;371}372373ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext();374m_option_group.NotifyOptionParsingStarting(&exe_ctx);375376OptionsWithRaw args_with_suffix(raw_command_line);377378if (args_with_suffix.HasArgs())379if (!ParseOptionsAndNotify(args_with_suffix.GetArgs(), result,380m_option_group, exe_ctx))381return;382383llvm::StringRef raw_command_string = args_with_suffix.GetRawPart();384Args args(raw_command_string);385386if (args.GetArgumentCount() < 2) {387result.AppendError("'command alias' requires at least two arguments");388return;389}390391// Get the alias command.392393auto alias_command = args[0].ref();394if (alias_command.starts_with("-")) {395result.AppendError("aliases starting with a dash are not supported");396if (alias_command == "--help" || alias_command == "--long-help") {397result.AppendWarning("if trying to pass options to 'command alias' add "398"a -- at the end of the options");399}400return;401}402403// Strip the new alias name off 'raw_command_string' (leave it on args,404// which gets passed to 'Execute', which does the stripping itself.405size_t pos = raw_command_string.find(alias_command);406if (pos == 0) {407raw_command_string = raw_command_string.substr(alias_command.size());408pos = raw_command_string.find_first_not_of(' ');409if ((pos != std::string::npos) && (pos > 0))410raw_command_string = raw_command_string.substr(pos);411} else {412result.AppendError("Error parsing command string. No alias created.");413return;414}415416// Verify that the command is alias-able.417if (m_interpreter.CommandExists(alias_command)) {418result.AppendErrorWithFormat(419"'%s' is a permanent debugger command and cannot be redefined.\n",420args[0].c_str());421return;422}423424if (m_interpreter.UserMultiwordCommandExists(alias_command)) {425result.AppendErrorWithFormat(426"'%s' is a user container command and cannot be overwritten.\n"427"Delete it first with 'command container delete'\n",428args[0].c_str());429return;430}431432// Get CommandObject that is being aliased. The command name is read from433// the front of raw_command_string. raw_command_string is returned with the434// name of the command object stripped off the front.435llvm::StringRef original_raw_command_string = raw_command_string;436CommandObject *cmd_obj =437m_interpreter.GetCommandObjectForCommand(raw_command_string);438439if (!cmd_obj) {440result.AppendErrorWithFormat("invalid command given to 'command alias'. "441"'%s' does not begin with a valid command."442" No alias created.",443original_raw_command_string.str().c_str());444} else if (!cmd_obj->WantsRawCommandString()) {445// Note that args was initialized with the original command, and has not446// been updated to this point. Therefore can we pass it to the version of447// Execute that does not need/expect raw input in the alias.448HandleAliasingNormalCommand(args, result);449} else {450HandleAliasingRawCommand(alias_command, raw_command_string, *cmd_obj,451result);452}453}454455bool HandleAliasingRawCommand(llvm::StringRef alias_command,456llvm::StringRef raw_command_string,457CommandObject &cmd_obj,458CommandReturnObject &result) {459// Verify & handle any options/arguments passed to the alias command460461OptionArgVectorSP option_arg_vector_sp =462OptionArgVectorSP(new OptionArgVector);463464const bool include_aliases = true;465// Look up the command using command's name first. This is to resolve466// aliases when you are making nested aliases. But if you don't find467// it that way, then it wasn't an alias and we can just use the object468// we were passed in.469CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact(470cmd_obj.GetCommandName(), include_aliases);471if (!cmd_obj_sp)472cmd_obj_sp = cmd_obj.shared_from_this();473474if (m_interpreter.AliasExists(alias_command) ||475m_interpreter.UserCommandExists(alias_command)) {476result.AppendWarningWithFormat(477"Overwriting existing definition for '%s'.\n",478alias_command.str().c_str());479}480if (CommandAlias *alias = m_interpreter.AddAlias(481alias_command, cmd_obj_sp, raw_command_string)) {482if (m_command_options.m_help.OptionWasSet())483alias->SetHelp(m_command_options.m_help.GetCurrentValue());484if (m_command_options.m_long_help.OptionWasSet())485alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue());486result.SetStatus(eReturnStatusSuccessFinishNoResult);487} else {488result.AppendError("Unable to create requested alias.\n");489}490return result.Succeeded();491}492493bool HandleAliasingNormalCommand(Args &args, CommandReturnObject &result) {494size_t argc = args.GetArgumentCount();495496if (argc < 2) {497result.AppendError("'command alias' requires at least two arguments");498return false;499}500501// Save these in std::strings since we're going to shift them off.502const std::string alias_command(std::string(args[0].ref()));503const std::string actual_command(std::string(args[1].ref()));504505args.Shift(); // Shift the alias command word off the argument vector.506args.Shift(); // Shift the old command word off the argument vector.507508// Verify that the command is alias'able, and get the appropriate command509// object.510511if (m_interpreter.CommandExists(alias_command)) {512result.AppendErrorWithFormat(513"'%s' is a permanent debugger command and cannot be redefined.\n",514alias_command.c_str());515return false;516}517518if (m_interpreter.UserMultiwordCommandExists(alias_command)) {519result.AppendErrorWithFormat(520"'%s' is user container command and cannot be overwritten.\n"521"Delete it first with 'command container delete'",522alias_command.c_str());523return false;524}525526CommandObjectSP command_obj_sp(527m_interpreter.GetCommandSPExact(actual_command, true));528CommandObjectSP subcommand_obj_sp;529bool use_subcommand = false;530if (!command_obj_sp) {531result.AppendErrorWithFormat("'%s' is not an existing command.\n",532actual_command.c_str());533return false;534}535CommandObject *cmd_obj = command_obj_sp.get();536CommandObject *sub_cmd_obj = nullptr;537OptionArgVectorSP option_arg_vector_sp =538OptionArgVectorSP(new OptionArgVector);539540while (cmd_obj->IsMultiwordObject() && !args.empty()) {541auto sub_command = args[0].ref();542assert(!sub_command.empty());543subcommand_obj_sp = cmd_obj->GetSubcommandSP(sub_command);544if (!subcommand_obj_sp) {545result.AppendErrorWithFormat(546"'%s' is not a valid sub-command of '%s'. "547"Unable to create alias.\n",548args[0].c_str(), actual_command.c_str());549return false;550}551552sub_cmd_obj = subcommand_obj_sp.get();553use_subcommand = true;554args.Shift(); // Shift the sub_command word off the argument vector.555cmd_obj = sub_cmd_obj;556}557558// Verify & handle any options/arguments passed to the alias command559560std::string args_string;561562if (!args.empty()) {563CommandObjectSP tmp_sp =564m_interpreter.GetCommandSPExact(cmd_obj->GetCommandName());565if (use_subcommand)566tmp_sp = m_interpreter.GetCommandSPExact(sub_cmd_obj->GetCommandName());567568args.GetCommandString(args_string);569}570571if (m_interpreter.AliasExists(alias_command) ||572m_interpreter.UserCommandExists(alias_command)) {573result.AppendWarningWithFormat(574"Overwriting existing definition for '%s'.\n", alias_command.c_str());575}576577if (CommandAlias *alias = m_interpreter.AddAlias(578alias_command, use_subcommand ? subcommand_obj_sp : command_obj_sp,579args_string)) {580if (m_command_options.m_help.OptionWasSet())581alias->SetHelp(m_command_options.m_help.GetCurrentValue());582if (m_command_options.m_long_help.OptionWasSet())583alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue());584result.SetStatus(eReturnStatusSuccessFinishNoResult);585} else {586result.AppendError("Unable to create requested alias.\n");587return false;588}589590return result.Succeeded();591}592};593594#pragma mark CommandObjectCommandsUnalias595// CommandObjectCommandsUnalias596597class CommandObjectCommandsUnalias : public CommandObjectParsed {598public:599CommandObjectCommandsUnalias(CommandInterpreter &interpreter)600: CommandObjectParsed(601interpreter, "command unalias",602"Delete one or more custom commands defined by 'command alias'.",603nullptr) {604AddSimpleArgumentList(eArgTypeAliasName);605}606607~CommandObjectCommandsUnalias() override = default;608609void610HandleArgumentCompletion(CompletionRequest &request,611OptionElementVector &opt_element_vector) override {612if (!m_interpreter.HasCommands() || request.GetCursorIndex() != 0)613return;614615for (const auto &ent : m_interpreter.GetAliases()) {616request.TryCompleteCurrentArg(ent.first, ent.second->GetHelp());617}618}619620protected:621void DoExecute(Args &args, CommandReturnObject &result) override {622CommandObject::CommandMap::iterator pos;623CommandObject *cmd_obj;624625if (args.empty()) {626result.AppendError("must call 'unalias' with a valid alias");627return;628}629630auto command_name = args[0].ref();631cmd_obj = m_interpreter.GetCommandObject(command_name);632if (!cmd_obj) {633result.AppendErrorWithFormat(634"'%s' is not a known command.\nTry 'help' to see a "635"current list of commands.\n",636args[0].c_str());637return;638}639640if (m_interpreter.CommandExists(command_name)) {641if (cmd_obj->IsRemovable()) {642result.AppendErrorWithFormat(643"'%s' is not an alias, it is a debugger command which can be "644"removed using the 'command delete' command.\n",645args[0].c_str());646} else {647result.AppendErrorWithFormat(648"'%s' is a permanent debugger command and cannot be removed.\n",649args[0].c_str());650}651return;652}653654if (!m_interpreter.RemoveAlias(command_name)) {655if (m_interpreter.AliasExists(command_name))656result.AppendErrorWithFormat(657"Error occurred while attempting to unalias '%s'.\n",658args[0].c_str());659else660result.AppendErrorWithFormat("'%s' is not an existing alias.\n",661args[0].c_str());662return;663}664665result.SetStatus(eReturnStatusSuccessFinishNoResult);666}667};668669#pragma mark CommandObjectCommandsDelete670// CommandObjectCommandsDelete671672class CommandObjectCommandsDelete : public CommandObjectParsed {673public:674CommandObjectCommandsDelete(CommandInterpreter &interpreter)675: CommandObjectParsed(676interpreter, "command delete",677"Delete one or more custom commands defined by 'command regex'.",678nullptr) {679AddSimpleArgumentList(eArgTypeCommandName);680}681682~CommandObjectCommandsDelete() override = default;683684void685HandleArgumentCompletion(CompletionRequest &request,686OptionElementVector &opt_element_vector) override {687if (!m_interpreter.HasCommands() || request.GetCursorIndex() != 0)688return;689690for (const auto &ent : m_interpreter.GetCommands()) {691if (ent.second->IsRemovable())692request.TryCompleteCurrentArg(ent.first, ent.second->GetHelp());693}694}695696protected:697void DoExecute(Args &args, CommandReturnObject &result) override {698CommandObject::CommandMap::iterator pos;699700if (args.empty()) {701result.AppendErrorWithFormat("must call '%s' with one or more valid user "702"defined regular expression command names",703GetCommandName().str().c_str());704return;705}706707auto command_name = args[0].ref();708if (!m_interpreter.CommandExists(command_name)) {709StreamString error_msg_stream;710const bool generate_upropos = true;711const bool generate_type_lookup = false;712CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage(713&error_msg_stream, command_name, llvm::StringRef(), llvm::StringRef(),714generate_upropos, generate_type_lookup);715result.AppendError(error_msg_stream.GetString());716return;717}718719if (!m_interpreter.RemoveCommand(command_name)) {720result.AppendErrorWithFormat(721"'%s' is a permanent debugger command and cannot be removed.\n",722args[0].c_str());723return;724}725726result.SetStatus(eReturnStatusSuccessFinishNoResult);727}728};729730// CommandObjectCommandsAddRegex731732#define LLDB_OPTIONS_regex733#include "CommandOptions.inc"734735#pragma mark CommandObjectCommandsAddRegex736737class CommandObjectCommandsAddRegex : public CommandObjectParsed,738public IOHandlerDelegateMultiline {739public:740CommandObjectCommandsAddRegex(CommandInterpreter &interpreter)741: CommandObjectParsed(742interpreter, "command regex",743"Define a custom command in terms of "744"existing commands by matching "745"regular expressions.",746"command regex <cmd-name> [s/<regex>/<subst>/ ...]"),747IOHandlerDelegateMultiline("",748IOHandlerDelegate::Completion::LLDBCommand) {749SetHelpLong(750R"(751)"752"This command allows the user to create powerful regular expression commands \753with substitutions. The regular expressions and substitutions are specified \754using the regular expression substitution format of:"755R"(756757s/<regex>/<subst>/758759)"760"<regex> is a regular expression that can use parenthesis to capture regular \761expression input and substitute the captured matches in the output using %1 \762for the first match, %2 for the second, and so on."763R"(764765)"766"The regular expressions can all be specified on the command line if more than \767one argument is provided. If just the command name is provided on the command \768line, then the regular expressions and substitutions can be entered on separate \769lines, followed by an empty line to terminate the command definition."770R"(771772EXAMPLES773774)"775"The following example will define a regular expression command named 'f' that \776will call 'finish' if there are no arguments, or 'frame select <frame-idx>' if \777a number follows 'f':"778R"(779780(lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/')");781AddSimpleArgumentList(eArgTypeSEDStylePair, eArgRepeatOptional);782}783784~CommandObjectCommandsAddRegex() override = default;785786protected:787void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {788StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());789if (output_sp && interactive) {790output_sp->PutCString("Enter one or more sed substitution commands in "791"the form: 's/<regex>/<subst>/'.\nTerminate the "792"substitution list with an empty line.\n");793output_sp->Flush();794}795}796797void IOHandlerInputComplete(IOHandler &io_handler,798std::string &data) override {799io_handler.SetIsDone(true);800if (m_regex_cmd_up) {801StringList lines;802if (lines.SplitIntoLines(data)) {803bool check_only = false;804for (const std::string &line : lines) {805Status error = AppendRegexSubstitution(line, check_only);806if (error.Fail()) {807if (!GetDebugger().GetCommandInterpreter().GetBatchCommandMode()) {808StreamSP out_stream = GetDebugger().GetAsyncOutputStream();809out_stream->Printf("error: %s\n", error.AsCString());810}811}812}813}814if (m_regex_cmd_up->HasRegexEntries()) {815CommandObjectSP cmd_sp(m_regex_cmd_up.release());816m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true);817}818}819}820821void DoExecute(Args &command, CommandReturnObject &result) override {822const size_t argc = command.GetArgumentCount();823if (argc == 0) {824result.AppendError("usage: 'command regex <command-name> "825"[s/<regex1>/<subst1>/ s/<regex2>/<subst2>/ ...]'\n");826return;827}828829Status error;830auto name = command[0].ref();831m_regex_cmd_up = std::make_unique<CommandObjectRegexCommand>(832m_interpreter, name, m_options.GetHelp(), m_options.GetSyntax(), 0,833true);834835if (argc == 1) {836Debugger &debugger = GetDebugger();837bool color_prompt = debugger.GetUseColor();838const bool multiple_lines = true; // Get multiple lines839IOHandlerSP io_handler_sp(new IOHandlerEditline(840debugger, IOHandler::Type::Other,841"lldb-regex", // Name of input reader for history842llvm::StringRef("> "), // Prompt843llvm::StringRef(), // Continuation prompt844multiple_lines, color_prompt,8450, // Don't show line numbers846*this));847848if (io_handler_sp) {849debugger.RunIOHandlerAsync(io_handler_sp);850result.SetStatus(eReturnStatusSuccessFinishNoResult);851}852} else {853for (auto &entry : command.entries().drop_front()) {854bool check_only = false;855error = AppendRegexSubstitution(entry.ref(), check_only);856if (error.Fail())857break;858}859860if (error.Success()) {861AddRegexCommandToInterpreter();862}863}864if (error.Fail()) {865result.AppendError(error.AsCString());866}867}868869Status AppendRegexSubstitution(const llvm::StringRef ®ex_sed,870bool check_only) {871Status error;872873if (!m_regex_cmd_up) {874error.SetErrorStringWithFormat(875"invalid regular expression command object for: '%.*s'",876(int)regex_sed.size(), regex_sed.data());877return error;878}879880size_t regex_sed_size = regex_sed.size();881882if (regex_sed_size <= 1) {883error.SetErrorStringWithFormat(884"regular expression substitution string is too short: '%.*s'",885(int)regex_sed.size(), regex_sed.data());886return error;887}888889if (regex_sed[0] != 's') {890error.SetErrorStringWithFormat("regular expression substitution string "891"doesn't start with 's': '%.*s'",892(int)regex_sed.size(), regex_sed.data());893return error;894}895const size_t first_separator_char_pos = 1;896// use the char that follows 's' as the regex separator character so we can897// have "s/<regex>/<subst>/" or "s|<regex>|<subst>|"898const char separator_char = regex_sed[first_separator_char_pos];899const size_t second_separator_char_pos =900regex_sed.find(separator_char, first_separator_char_pos + 1);901902if (second_separator_char_pos == std::string::npos) {903error.SetErrorStringWithFormat(904"missing second '%c' separator char after '%.*s' in '%.*s'",905separator_char,906(int)(regex_sed.size() - first_separator_char_pos - 1),907regex_sed.data() + (first_separator_char_pos + 1),908(int)regex_sed.size(), regex_sed.data());909return error;910}911912const size_t third_separator_char_pos =913regex_sed.find(separator_char, second_separator_char_pos + 1);914915if (third_separator_char_pos == std::string::npos) {916error.SetErrorStringWithFormat(917"missing third '%c' separator char after '%.*s' in '%.*s'",918separator_char,919(int)(regex_sed.size() - second_separator_char_pos - 1),920regex_sed.data() + (second_separator_char_pos + 1),921(int)regex_sed.size(), regex_sed.data());922return error;923}924925if (third_separator_char_pos != regex_sed_size - 1) {926// Make sure that everything that follows the last regex separator char927if (regex_sed.find_first_not_of("\t\n\v\f\r ",928third_separator_char_pos + 1) !=929std::string::npos) {930error.SetErrorStringWithFormat(931"extra data found after the '%.*s' regular expression substitution "932"string: '%.*s'",933(int)third_separator_char_pos + 1, regex_sed.data(),934(int)(regex_sed.size() - third_separator_char_pos - 1),935regex_sed.data() + (third_separator_char_pos + 1));936return error;937}938} else if (first_separator_char_pos + 1 == second_separator_char_pos) {939error.SetErrorStringWithFormat(940"<regex> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'",941separator_char, separator_char, separator_char, (int)regex_sed.size(),942regex_sed.data());943return error;944} else if (second_separator_char_pos + 1 == third_separator_char_pos) {945error.SetErrorStringWithFormat(946"<subst> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'",947separator_char, separator_char, separator_char, (int)regex_sed.size(),948regex_sed.data());949return error;950}951952if (!check_only) {953std::string regex(std::string(regex_sed.substr(954first_separator_char_pos + 1,955second_separator_char_pos - first_separator_char_pos - 1)));956std::string subst(std::string(regex_sed.substr(957second_separator_char_pos + 1,958third_separator_char_pos - second_separator_char_pos - 1)));959m_regex_cmd_up->AddRegexCommand(regex, subst);960}961return error;962}963964void AddRegexCommandToInterpreter() {965if (m_regex_cmd_up) {966if (m_regex_cmd_up->HasRegexEntries()) {967CommandObjectSP cmd_sp(m_regex_cmd_up.release());968m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true);969}970}971}972973private:974std::unique_ptr<CommandObjectRegexCommand> m_regex_cmd_up;975976class CommandOptions : public Options {977public:978CommandOptions() = default;979980~CommandOptions() override = default;981982Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,983ExecutionContext *execution_context) override {984Status error;985const int short_option = m_getopt_table[option_idx].val;986987switch (short_option) {988case 'h':989m_help.assign(std::string(option_arg));990break;991case 's':992m_syntax.assign(std::string(option_arg));993break;994default:995llvm_unreachable("Unimplemented option");996}997998return error;999}10001001void OptionParsingStarting(ExecutionContext *execution_context) override {1002m_help.clear();1003m_syntax.clear();1004}10051006llvm::ArrayRef<OptionDefinition> GetDefinitions() override {1007return llvm::ArrayRef(g_regex_options);1008}10091010llvm::StringRef GetHelp() { return m_help; }10111012llvm::StringRef GetSyntax() { return m_syntax; }10131014protected:1015// Instance variables to hold the values for command options.10161017std::string m_help;1018std::string m_syntax;1019};10201021Options *GetOptions() override { return &m_options; }10221023CommandOptions m_options;1024};10251026class CommandObjectPythonFunction : public CommandObjectRaw {1027public:1028CommandObjectPythonFunction(CommandInterpreter &interpreter, std::string name,1029std::string funct, std::string help,1030ScriptedCommandSynchronicity synch,1031CompletionType completion_type)1032: CommandObjectRaw(interpreter, name), m_function_name(funct),1033m_synchro(synch), m_completion_type(completion_type) {1034if (!help.empty())1035SetHelp(help);1036else {1037StreamString stream;1038stream.Printf("For more information run 'help %s'", name.c_str());1039SetHelp(stream.GetString());1040}1041}10421043~CommandObjectPythonFunction() override = default;10441045bool IsRemovable() const override { return true; }10461047const std::string &GetFunctionName() { return m_function_name; }10481049ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }10501051llvm::StringRef GetHelpLong() override {1052if (m_fetched_help_long)1053return CommandObjectRaw::GetHelpLong();10541055ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();1056if (!scripter)1057return CommandObjectRaw::GetHelpLong();10581059std::string docstring;1060m_fetched_help_long =1061scripter->GetDocumentationForItem(m_function_name.c_str(), docstring);1062if (!docstring.empty())1063SetHelpLong(docstring);1064return CommandObjectRaw::GetHelpLong();1065}10661067void1068HandleArgumentCompletion(CompletionRequest &request,1069OptionElementVector &opt_element_vector) override {1070lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(1071GetCommandInterpreter(), m_completion_type, request, nullptr);1072}10731074bool WantsCompletion() override { return true; }10751076protected:1077void DoExecute(llvm::StringRef raw_command_line,1078CommandReturnObject &result) override {1079ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();10801081m_interpreter.IncreaseCommandUsage(*this);10821083Status error;10841085result.SetStatus(eReturnStatusInvalid);10861087if (!scripter || !scripter->RunScriptBasedCommand(1088m_function_name.c_str(), raw_command_line, m_synchro,1089result, error, m_exe_ctx)) {1090result.AppendError(error.AsCString());1091} else {1092// Don't change the status if the command already set it...1093if (result.GetStatus() == eReturnStatusInvalid) {1094if (result.GetOutputData().empty())1095result.SetStatus(eReturnStatusSuccessFinishNoResult);1096else1097result.SetStatus(eReturnStatusSuccessFinishResult);1098}1099}1100}11011102private:1103std::string m_function_name;1104ScriptedCommandSynchronicity m_synchro;1105bool m_fetched_help_long = false;1106CompletionType m_completion_type = eNoCompletion;1107};11081109/// This class implements a "raw" scripted command. lldb does no parsing of the1110/// command line, instead passing the line unaltered (except for backtick1111/// substitution).1112class CommandObjectScriptingObjectRaw : public CommandObjectRaw {1113public:1114CommandObjectScriptingObjectRaw(CommandInterpreter &interpreter,1115std::string name,1116StructuredData::GenericSP cmd_obj_sp,1117ScriptedCommandSynchronicity synch,1118CompletionType completion_type)1119: CommandObjectRaw(interpreter, name), m_cmd_obj_sp(cmd_obj_sp),1120m_synchro(synch), m_fetched_help_short(false),1121m_fetched_help_long(false), m_completion_type(completion_type) {1122StreamString stream;1123stream.Printf("For more information run 'help %s'", name.c_str());1124SetHelp(stream.GetString());1125if (ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter())1126GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp));1127}11281129~CommandObjectScriptingObjectRaw() override = default;11301131void1132HandleArgumentCompletion(CompletionRequest &request,1133OptionElementVector &opt_element_vector) override {1134lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(1135GetCommandInterpreter(), m_completion_type, request, nullptr);1136}11371138bool WantsCompletion() override { return true; }11391140bool IsRemovable() const override { return true; }11411142ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }11431144std::optional<std::string> GetRepeatCommand(Args &args,1145uint32_t index) override {1146ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();1147if (!scripter)1148return std::nullopt;11491150return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args);1151}11521153llvm::StringRef GetHelp() override {1154if (m_fetched_help_short)1155return CommandObjectRaw::GetHelp();1156ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();1157if (!scripter)1158return CommandObjectRaw::GetHelp();1159std::string docstring;1160m_fetched_help_short =1161scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring);1162if (!docstring.empty())1163SetHelp(docstring);11641165return CommandObjectRaw::GetHelp();1166}11671168llvm::StringRef GetHelpLong() override {1169if (m_fetched_help_long)1170return CommandObjectRaw::GetHelpLong();11711172ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();1173if (!scripter)1174return CommandObjectRaw::GetHelpLong();11751176std::string docstring;1177m_fetched_help_long =1178scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring);1179if (!docstring.empty())1180SetHelpLong(docstring);1181return CommandObjectRaw::GetHelpLong();1182}11831184protected:1185void DoExecute(llvm::StringRef raw_command_line,1186CommandReturnObject &result) override {1187ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();11881189Status error;11901191result.SetStatus(eReturnStatusInvalid);11921193if (!scripter ||1194!scripter->RunScriptBasedCommand(m_cmd_obj_sp, raw_command_line,1195m_synchro, result, error, m_exe_ctx)) {1196result.AppendError(error.AsCString());1197} else {1198// Don't change the status if the command already set it...1199if (result.GetStatus() == eReturnStatusInvalid) {1200if (result.GetOutputData().empty())1201result.SetStatus(eReturnStatusSuccessFinishNoResult);1202else1203result.SetStatus(eReturnStatusSuccessFinishResult);1204}1205}1206}12071208private:1209StructuredData::GenericSP m_cmd_obj_sp;1210ScriptedCommandSynchronicity m_synchro;1211bool m_fetched_help_short : 1;1212bool m_fetched_help_long : 1;1213CompletionType m_completion_type = eNoCompletion;1214};121512161217/// This command implements a lldb parsed scripted command. The command1218/// provides a definition of the options and arguments, and a option value1219/// setting callback, and then the command's execution function gets passed1220/// just the parsed arguments.1221/// Note, implementing a command in Python using these base interfaces is a bit1222/// of a pain, but it is much easier to export this low level interface, and1223/// then make it nicer on the Python side, than to try to do that in a1224/// script language neutral way.1225/// So I've also added a base class in Python that provides a table-driven1226/// way of defining the options and arguments, which automatically fills the1227/// option values, making them available as properties in Python.1228///1229class CommandObjectScriptingObjectParsed : public CommandObjectParsed {1230private:1231class CommandOptions : public Options {1232public:1233CommandOptions(CommandInterpreter &interpreter,1234StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter),1235m_cmd_obj_sp(cmd_obj_sp) {}12361237~CommandOptions() override = default;12381239Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,1240ExecutionContext *execution_context) override {1241Status error;1242ScriptInterpreter *scripter =1243m_interpreter.GetDebugger().GetScriptInterpreter();1244if (!scripter) {1245error.SetErrorString("No script interpreter for SetOptionValue.");1246return error;1247}1248if (!m_cmd_obj_sp) {1249error.SetErrorString("SetOptionValue called with empty cmd_obj.");1250return error;1251}1252if (!m_options_definition_up) {1253error.SetErrorString("SetOptionValue called before options definitions "1254"were created.");1255return error;1256}1257// Pass the long option, since you aren't actually required to have a1258// short_option, and for those options the index or short option character1259// aren't meaningful on the python side.1260const char * long_option =1261m_options_definition_up.get()[option_idx].long_option;1262bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp,1263execution_context, long_option, option_arg);1264if (!success)1265error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",1266long_option, option_arg);1267return error;1268}12691270void OptionParsingStarting(ExecutionContext *execution_context) override {1271ScriptInterpreter *scripter =1272m_interpreter.GetDebugger().GetScriptInterpreter();1273if (!scripter || !m_cmd_obj_sp)1274return;12751276scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);1277}12781279llvm::ArrayRef<OptionDefinition> GetDefinitions() override {1280if (!m_options_definition_up)1281return {};1282return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);1283}12841285static Status ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp,1286size_t counter, uint32_t &usage_mask) {1287// If the usage entry is not provided, we use LLDB_OPT_SET_ALL.1288// If the usage mask is a UINT, the option belongs to that group.1289// If the usage mask is a vector of UINT's, the option belongs to all the1290// groups listed.1291// If a subelement of the vector is a vector of two ints, then the option1292// belongs to the inclusive range from the first to the second element.1293Status error;1294if (!obj_sp) {1295usage_mask = LLDB_OPT_SET_ALL;1296return error;1297}12981299usage_mask = 0;13001301StructuredData::UnsignedInteger *uint_val =1302obj_sp->GetAsUnsignedInteger();1303if (uint_val) {1304// If this is an integer, then this specifies a single group:1305uint32_t value = uint_val->GetValue();1306if (value == 0) {1307error.SetErrorStringWithFormatv(1308"0 is not a valid group for option {0}", counter);1309return error;1310}1311usage_mask = (1 << (value - 1));1312return error;1313}1314// Otherwise it has to be an array:1315StructuredData::Array *array_val = obj_sp->GetAsArray();1316if (!array_val) {1317error.SetErrorStringWithFormatv(1318"required field is not a array for option {0}", counter);1319return error;1320}1321// This is the array ForEach for accumulating a group usage mask from1322// an array of string descriptions of groups.1323auto groups_accumulator1324= [counter, &usage_mask, &error]1325(StructuredData::Object *obj) -> bool {1326StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();1327if (int_val) {1328uint32_t value = int_val->GetValue();1329if (value == 0) {1330error.SetErrorStringWithFormatv(1331"0 is not a valid group for element {0}", counter);1332return false;1333}1334usage_mask |= (1 << (value - 1));1335return true;1336}1337StructuredData::Array *arr_val = obj->GetAsArray();1338if (!arr_val) {1339error.SetErrorStringWithFormatv(1340"Group element not an int or array of integers for element {0}",1341counter);1342return false;1343}1344size_t num_range_elem = arr_val->GetSize();1345if (num_range_elem != 2) {1346error.SetErrorStringWithFormatv(1347"Subranges of a group not a start and a stop for element {0}",1348counter);1349return false;1350}1351int_val = arr_val->GetItemAtIndex(0)->GetAsUnsignedInteger();1352if (!int_val) {1353error.SetErrorStringWithFormatv("Start element of a subrange of a "1354"group not unsigned int for element {0}", counter);1355return false;1356}1357uint32_t start = int_val->GetValue();1358int_val = arr_val->GetItemAtIndex(1)->GetAsUnsignedInteger();1359if (!int_val) {1360error.SetErrorStringWithFormatv("End element of a subrange of a group"1361" not unsigned int for element {0}", counter);1362return false;1363}1364uint32_t end = int_val->GetValue();1365if (start == 0 || end == 0 || start > end) {1366error.SetErrorStringWithFormatv("Invalid subrange of a group: {0} - "1367"{1} for element {2}", start, end, counter);1368return false;1369}1370for (uint32_t i = start; i <= end; i++) {1371usage_mask |= (1 << (i - 1));1372}1373return true;1374};1375array_val->ForEach(groups_accumulator);1376return error;1377}137813791380Status SetOptionsFromArray(StructuredData::Dictionary &options) {1381Status error;1382m_num_options = options.GetSize();1383m_options_definition_up.reset(new OptionDefinition[m_num_options]);1384// We need to hand out pointers to contents of these vectors; we reserve1385// as much as we'll need up front so they don't get freed on resize...1386m_usage_container.resize(m_num_options);1387m_enum_storage.resize(m_num_options);1388m_enum_vector.resize(m_num_options);13891390size_t counter = 0;1391size_t short_opt_counter = 0;1392// This is the Array::ForEach function for adding option elements:1393auto add_element = [this, &error, &counter, &short_opt_counter]1394(llvm::StringRef long_option, StructuredData::Object *object) -> bool {1395StructuredData::Dictionary *opt_dict = object->GetAsDictionary();1396if (!opt_dict) {1397error.SetErrorString("Value in options dictionary is not a dictionary");1398return false;1399}1400OptionDefinition &option_def = m_options_definition_up.get()[counter];14011402// We aren't exposing the validator yet, set it to null1403option_def.validator = nullptr;1404// We don't require usage masks, so set it to one group by default:1405option_def.usage_mask = 1;14061407// Now set the fields of the OptionDefinition Array from the dictionary:1408//1409// Note that I don't check for unknown fields in the option dictionaries1410// so a scriptor can add extra elements that are helpful when they go to1411// do "set_option_value"14121413// Usage Mask:1414StructuredData::ObjectSP obj_sp = opt_dict->GetValueForKey("groups");1415if (obj_sp) {1416error = ParseUsageMaskFromArray(obj_sp, counter,1417option_def.usage_mask);1418if (error.Fail())1419return false;1420}14211422// Required:1423option_def.required = false;1424obj_sp = opt_dict->GetValueForKey("required");1425if (obj_sp) {1426StructuredData::Boolean *boolean_val = obj_sp->GetAsBoolean();1427if (!boolean_val) {1428error.SetErrorStringWithFormatv("'required' field is not a boolean "1429"for option {0}", counter);1430return false;1431}1432option_def.required = boolean_val->GetValue();1433}14341435// Short Option:1436int short_option;1437obj_sp = opt_dict->GetValueForKey("short_option");1438if (obj_sp) {1439// The value is a string, so pull the1440llvm::StringRef short_str = obj_sp->GetStringValue();1441if (short_str.empty()) {1442error.SetErrorStringWithFormatv("short_option field empty for "1443"option {0}", counter);1444return false;1445} else if (short_str.size() != 1) {1446error.SetErrorStringWithFormatv("short_option field has extra "1447"characters for option {0}", counter);1448return false;1449}1450short_option = (int) short_str[0];1451} else {1452// If the short option is not provided, then we need a unique value1453// less than the lowest printable ASCII character.1454short_option = short_opt_counter++;1455}1456option_def.short_option = short_option;14571458// Long Option is the key from the outer dict:1459if (long_option.empty()) {1460error.SetErrorStringWithFormatv("empty long_option for option {0}",1461counter);1462return false;1463}1464auto inserted = g_string_storer.insert(long_option.str());1465option_def.long_option = ((*(inserted.first)).data());14661467// Value Type:1468obj_sp = opt_dict->GetValueForKey("value_type");1469if (obj_sp) {1470StructuredData::UnsignedInteger *uint_val1471= obj_sp->GetAsUnsignedInteger();1472if (!uint_val) {1473error.SetErrorStringWithFormatv("Value type must be an unsigned "1474"integer");1475return false;1476}1477uint64_t val_type = uint_val->GetValue();1478if (val_type >= eArgTypeLastArg) {1479error.SetErrorStringWithFormatv("Value type {0} beyond the "1480"CommandArgumentType bounds", val_type);1481return false;1482}1483option_def.argument_type = (CommandArgumentType) val_type;1484option_def.option_has_arg = true;1485} else {1486option_def.argument_type = eArgTypeNone;1487option_def.option_has_arg = false;1488}14891490// Completion Type:1491obj_sp = opt_dict->GetValueForKey("completion_type");1492if (obj_sp) {1493StructuredData::UnsignedInteger *uint_val = obj_sp->GetAsUnsignedInteger();1494if (!uint_val) {1495error.SetErrorStringWithFormatv("Completion type must be an "1496"unsigned integer for option {0}", counter);1497return false;1498}1499uint64_t completion_type = uint_val->GetValue();1500if (completion_type > eCustomCompletion) {1501error.SetErrorStringWithFormatv("Completion type for option {0} "1502"beyond the CompletionType bounds", completion_type);1503return false;1504}1505option_def.completion_type = (CommandArgumentType) completion_type;1506} else1507option_def.completion_type = eNoCompletion;15081509// Usage Text:1510std::string usage_text;1511obj_sp = opt_dict->GetValueForKey("help");1512if (!obj_sp) {1513error.SetErrorStringWithFormatv("required usage missing from option "1514"{0}", counter);1515return false;1516}1517llvm::StringRef usage_stref;1518usage_stref = obj_sp->GetStringValue();1519if (usage_stref.empty()) {1520error.SetErrorStringWithFormatv("empty usage text for option {0}",1521counter);1522return false;1523}1524m_usage_container[counter] = usage_stref.str().c_str();1525option_def.usage_text = m_usage_container[counter].data();15261527// Enum Values:15281529obj_sp = opt_dict->GetValueForKey("enum_values");1530if (obj_sp) {1531StructuredData::Array *array = obj_sp->GetAsArray();1532if (!array) {1533error.SetErrorStringWithFormatv("enum values must be an array for "1534"option {0}", counter);1535return false;1536}1537size_t num_elem = array->GetSize();1538size_t enum_ctr = 0;1539m_enum_storage[counter] = std::vector<EnumValueStorage>(num_elem);1540std::vector<EnumValueStorage> &curr_elem = m_enum_storage[counter];15411542// This is the Array::ForEach function for adding enum elements:1543// Since there are only two fields to specify the enum, use a simple1544// two element array with value first, usage second.1545// counter is only used for reporting so I pass it by value here.1546auto add_enum = [&enum_ctr, &curr_elem, counter, &error]1547(StructuredData::Object *object) -> bool {1548StructuredData::Array *enum_arr = object->GetAsArray();1549if (!enum_arr) {1550error.SetErrorStringWithFormatv("Enum values for option {0} not "1551"an array", counter);1552return false;1553}1554size_t num_enum_elements = enum_arr->GetSize();1555if (num_enum_elements != 2) {1556error.SetErrorStringWithFormatv("Wrong number of elements: {0} "1557"for enum {1} in option {2}",1558num_enum_elements, enum_ctr, counter);1559return false;1560}1561// Enum Value:1562StructuredData::ObjectSP obj_sp = enum_arr->GetItemAtIndex(0);1563llvm::StringRef val_stref = obj_sp->GetStringValue();1564std::string value_cstr_str = val_stref.str().c_str();15651566// Enum Usage:1567obj_sp = enum_arr->GetItemAtIndex(1);1568if (!obj_sp) {1569error.SetErrorStringWithFormatv("No usage for enum {0} in option "1570"{1}", enum_ctr, counter);1571return false;1572}1573llvm::StringRef usage_stref = obj_sp->GetStringValue();1574std::string usage_cstr_str = usage_stref.str().c_str();1575curr_elem[enum_ctr] = EnumValueStorage(value_cstr_str,1576usage_cstr_str, enum_ctr);15771578enum_ctr++;1579return true;1580}; // end of add_enum15811582array->ForEach(add_enum);1583if (!error.Success())1584return false;1585// We have to have a vector of elements to set in the options, make1586// that here:1587for (auto &elem : curr_elem)1588m_enum_vector[counter].emplace_back(elem.element);15891590option_def.enum_values = llvm::ArrayRef(m_enum_vector[counter]);1591}1592counter++;1593return true;1594}; // end of add_element15951596options.ForEach(add_element);1597return error;1598}15991600size_t GetNumOptions() { return m_num_options; }16011602private:1603struct EnumValueStorage {1604EnumValueStorage() {1605element.string_value = "value not set";1606element.usage = "usage not set";1607element.value = 0;1608}16091610EnumValueStorage(std::string in_str_val, std::string in_usage,1611size_t in_value) : value(std::move(in_str_val)), usage(std::move(in_usage)) {1612SetElement(in_value);1613}16141615EnumValueStorage(const EnumValueStorage &in) : value(in.value),1616usage(in.usage) {1617SetElement(in.element.value);1618}16191620EnumValueStorage &operator=(const EnumValueStorage &in) {1621value = in.value;1622usage = in.usage;1623SetElement(in.element.value);1624return *this;1625}16261627void SetElement(size_t in_value) {1628element.value = in_value;1629element.string_value = value.data();1630element.usage = usage.data();1631}16321633std::string value;1634std::string usage;1635OptionEnumValueElement element;1636};1637// We have to provide char * values for the long option, usage and enum1638// values, that's what the option definitions hold.1639// The long option strings are quite likely to be reused in other added1640// commands, so those are stored in a global set: g_string_storer.1641// But the usages are much less likely to be reused, so those are stored in1642// a vector in the command instance. It gets resized to the correct size1643// and then filled with null-terminated strings in the std::string, so the1644// are valid C-strings that won't move around.1645// The enum values and descriptions are treated similarly - these aren't1646// all that common so it's not worth the effort to dedup them.1647size_t m_num_options = 0;1648std::unique_ptr<OptionDefinition> m_options_definition_up;1649std::vector<std::vector<EnumValueStorage>> m_enum_storage;1650std::vector<std::vector<OptionEnumValueElement>> m_enum_vector;1651std::vector<std::string> m_usage_container;1652CommandInterpreter &m_interpreter;1653StructuredData::GenericSP m_cmd_obj_sp;1654static std::unordered_set<std::string> g_string_storer;1655};16561657public:1658static CommandObjectSP Create(CommandInterpreter &interpreter,1659std::string name,1660StructuredData::GenericSP cmd_obj_sp,1661ScriptedCommandSynchronicity synch,1662CommandReturnObject &result) {1663CommandObjectSP new_cmd_sp(new CommandObjectScriptingObjectParsed(1664interpreter, name, cmd_obj_sp, synch));16651666CommandObjectScriptingObjectParsed *parsed_cmd1667= static_cast<CommandObjectScriptingObjectParsed *>(new_cmd_sp.get());1668// Now check all the failure modes, and report if found.1669Status opt_error = parsed_cmd->GetOptionsError();1670Status arg_error = parsed_cmd->GetArgsError();16711672if (opt_error.Fail())1673result.AppendErrorWithFormat("failed to parse option definitions: %s",1674opt_error.AsCString());1675if (arg_error.Fail())1676result.AppendErrorWithFormat("%sfailed to parse argument definitions: %s",1677opt_error.Fail() ? ", also " : "",1678arg_error.AsCString());16791680if (!result.Succeeded())1681return {};16821683return new_cmd_sp;1684}16851686CommandObjectScriptingObjectParsed(CommandInterpreter &interpreter,1687std::string name,1688StructuredData::GenericSP cmd_obj_sp,1689ScriptedCommandSynchronicity synch)1690: CommandObjectParsed(interpreter, name.c_str()),1691m_cmd_obj_sp(cmd_obj_sp), m_synchro(synch),1692m_options(interpreter, cmd_obj_sp), m_fetched_help_short(false),1693m_fetched_help_long(false) {1694StreamString stream;1695ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();1696if (!scripter) {1697m_options_error.SetErrorString("No script interpreter");1698return;1699}17001701// Set the flags:1702GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp));17031704// Now set up the options definitions from the options:1705StructuredData::ObjectSP options_object_sp1706= scripter->GetOptionsForCommandObject(cmd_obj_sp);1707// It's okay not to have an options dict.1708if (options_object_sp) {1709// The options come as a dictionary of dictionaries. The key of the1710// outer dict is the long option name (since that's required). The1711// value holds all the other option specification bits.1712StructuredData::Dictionary *options_dict1713= options_object_sp->GetAsDictionary();1714// but if it exists, it has to be an array.1715if (options_dict) {1716m_options_error = m_options.SetOptionsFromArray(*(options_dict));1717// If we got an error don't bother with the arguments...1718if (m_options_error.Fail())1719return;1720} else {1721m_options_error.SetErrorString("Options array not an array");1722return;1723}1724}1725// Then fetch the args. Since the arguments can have usage masks you need1726// an array of arrays.1727StructuredData::ObjectSP args_object_sp1728= scripter->GetArgumentsForCommandObject(cmd_obj_sp);1729if (args_object_sp) {1730StructuredData::Array *args_array = args_object_sp->GetAsArray();1731if (!args_array) {1732m_args_error.SetErrorString("Argument specification is not an array");1733return;1734}1735size_t counter = 0;17361737// This is the Array::ForEach function that handles the1738// CommandArgumentEntry arrays one by one:1739auto arg_array_adder = [this, &counter] (StructuredData::Object *object)1740-> bool {1741// This is the Array::ForEach function to add argument entries:1742CommandArgumentEntry this_entry;1743size_t elem_counter = 0;1744auto args_adder = [this, counter, &elem_counter, &this_entry]1745(StructuredData::Object *object) -> bool {1746// The arguments definition has three fields, the argument type, the1747// repeat and the usage mask.1748CommandArgumentType arg_type = eArgTypeNone;1749ArgumentRepetitionType arg_repetition = eArgRepeatOptional;1750uint32_t arg_opt_set_association;17511752auto report_error = [this, elem_counter, counter]1753(const char *err_txt) -> bool {1754m_args_error.SetErrorStringWithFormatv("Element {0} of arguments "1755"list element {1}: %s.", elem_counter, counter, err_txt);1756return false;1757};17581759StructuredData::Dictionary *arg_dict = object->GetAsDictionary();1760if (!arg_dict) {1761report_error("is not a dictionary.");1762return false;1763}1764// Argument Type:1765StructuredData::ObjectSP obj_sp1766= arg_dict->GetValueForKey("arg_type");1767if (obj_sp) {1768StructuredData::UnsignedInteger *uint_val1769= obj_sp->GetAsUnsignedInteger();1770if (!uint_val) {1771report_error("value type must be an unsigned integer");1772return false;1773}1774uint64_t arg_type_int = uint_val->GetValue();1775if (arg_type_int >= eArgTypeLastArg) {1776report_error("value type beyond ArgumentRepetitionType bounds");1777return false;1778}1779arg_type = (CommandArgumentType) arg_type_int;1780}1781// Repeat Value:1782obj_sp = arg_dict->GetValueForKey("repeat");1783std::optional<ArgumentRepetitionType> repeat;1784if (obj_sp) {1785llvm::StringRef repeat_str = obj_sp->GetStringValue();1786if (repeat_str.empty()) {1787report_error("repeat value is empty");1788return false;1789}1790repeat = ArgRepetitionFromString(repeat_str);1791if (!repeat) {1792report_error("invalid repeat value");1793return false;1794}1795arg_repetition = *repeat;1796}17971798// Usage Mask:1799obj_sp = arg_dict->GetValueForKey("groups");1800m_args_error = CommandOptions::ParseUsageMaskFromArray(obj_sp,1801counter, arg_opt_set_association);1802this_entry.emplace_back(arg_type, arg_repetition,1803arg_opt_set_association);1804elem_counter++;1805return true;1806};1807StructuredData::Array *args_array = object->GetAsArray();1808if (!args_array) {1809m_args_error.SetErrorStringWithFormatv("Argument definition element "1810"{0} is not an array", counter);1811}18121813args_array->ForEach(args_adder);1814if (m_args_error.Fail())1815return false;1816if (this_entry.empty()) {1817m_args_error.SetErrorStringWithFormatv("Argument definition element "1818"{0} is empty", counter);1819return false;1820}1821m_arguments.push_back(this_entry);1822counter++;1823return true;1824}; // end of arg_array_adder1825// Here we actually parse the args definition:1826args_array->ForEach(arg_array_adder);1827}1828}18291830~CommandObjectScriptingObjectParsed() override = default;18311832Status GetOptionsError() { return m_options_error; }1833Status GetArgsError() { return m_args_error; }1834bool WantsCompletion() override { return true; }18351836bool IsRemovable() const override { return true; }18371838ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }18391840std::optional<std::string> GetRepeatCommand(Args &args,1841uint32_t index) override {1842ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();1843if (!scripter)1844return std::nullopt;18451846return scripter->GetRepeatCommandForScriptedCommand(m_cmd_obj_sp, args);1847}18481849llvm::StringRef GetHelp() override {1850if (m_fetched_help_short)1851return CommandObjectParsed::GetHelp();1852ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();1853if (!scripter)1854return CommandObjectParsed::GetHelp();1855std::string docstring;1856m_fetched_help_short =1857scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring);1858if (!docstring.empty())1859SetHelp(docstring);18601861return CommandObjectParsed::GetHelp();1862}18631864llvm::StringRef GetHelpLong() override {1865if (m_fetched_help_long)1866return CommandObjectParsed::GetHelpLong();18671868ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();1869if (!scripter)1870return CommandObjectParsed::GetHelpLong();18711872std::string docstring;1873m_fetched_help_long =1874scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring);1875if (!docstring.empty())1876SetHelpLong(docstring);1877return CommandObjectParsed::GetHelpLong();1878}18791880Options *GetOptions() override {1881// CommandObjectParsed requires that a command with no options return1882// nullptr.1883if (m_options.GetNumOptions() == 0)1884return nullptr;1885return &m_options;1886}18871888protected:1889void DoExecute(Args &args,1890CommandReturnObject &result) override {1891ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();18921893Status error;18941895result.SetStatus(eReturnStatusInvalid);18961897if (!scripter ||1898!scripter->RunScriptBasedParsedCommand(m_cmd_obj_sp, args,1899m_synchro, result, error, m_exe_ctx)) {1900result.AppendError(error.AsCString());1901} else {1902// Don't change the status if the command already set it...1903if (result.GetStatus() == eReturnStatusInvalid) {1904if (result.GetOutputData().empty())1905result.SetStatus(eReturnStatusSuccessFinishNoResult);1906else1907result.SetStatus(eReturnStatusSuccessFinishResult);1908}1909}1910}19111912private:1913StructuredData::GenericSP m_cmd_obj_sp;1914ScriptedCommandSynchronicity m_synchro;1915CommandOptions m_options;1916Status m_options_error;1917Status m_args_error;1918bool m_fetched_help_short : 1;1919bool m_fetched_help_long : 1;1920};19211922std::unordered_set<std::string>1923CommandObjectScriptingObjectParsed::CommandOptions::g_string_storer;19241925// CommandObjectCommandsScriptImport1926#define LLDB_OPTIONS_script_import1927#include "CommandOptions.inc"19281929class CommandObjectCommandsScriptImport : public CommandObjectParsed {1930public:1931CommandObjectCommandsScriptImport(CommandInterpreter &interpreter)1932: CommandObjectParsed(interpreter, "command script import",1933"Import a scripting module in LLDB.", nullptr) {1934AddSimpleArgumentList(eArgTypeFilename, eArgRepeatPlus);1935}19361937~CommandObjectCommandsScriptImport() override = default;19381939Options *GetOptions() override { return &m_options; }19401941protected:1942class CommandOptions : public Options {1943public:1944CommandOptions() = default;19451946~CommandOptions() override = default;19471948Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,1949ExecutionContext *execution_context) override {1950Status error;1951const int short_option = m_getopt_table[option_idx].val;19521953switch (short_option) {1954case 'r':1955// NO-OP1956break;1957case 'c':1958relative_to_command_file = true;1959break;1960case 's':1961silent = true;1962break;1963default:1964llvm_unreachable("Unimplemented option");1965}19661967return error;1968}19691970void OptionParsingStarting(ExecutionContext *execution_context) override {1971relative_to_command_file = false;1972}19731974llvm::ArrayRef<OptionDefinition> GetDefinitions() override {1975return llvm::ArrayRef(g_script_import_options);1976}1977bool relative_to_command_file = false;1978bool silent = false;1979};19801981void DoExecute(Args &command, CommandReturnObject &result) override {1982if (command.empty()) {1983result.AppendError("command script import needs one or more arguments");1984return;1985}19861987FileSpec source_dir = {};1988if (m_options.relative_to_command_file) {1989source_dir = GetDebugger().GetCommandInterpreter().GetCurrentSourceDir();1990if (!source_dir) {1991result.AppendError("command script import -c can only be specified "1992"from a command file");1993return;1994}1995}19961997for (auto &entry : command.entries()) {1998Status error;19992000LoadScriptOptions options;2001options.SetInitSession(true);2002options.SetSilent(m_options.silent);20032004// FIXME: this is necessary because CommandObject::CheckRequirements()2005// assumes that commands won't ever be recursively invoked, but it's2006// actually possible to craft a Python script that does other "command2007// script imports" in __lldb_init_module the real fix is to have2008// recursive commands possible with a CommandInvocation object separate2009// from the CommandObject itself, so that recursive command invocations2010// won't stomp on each other (wrt to execution contents, options, and2011// more)2012m_exe_ctx.Clear();2013if (GetDebugger().GetScriptInterpreter()->LoadScriptingModule(2014entry.c_str(), options, error, /*module_sp=*/nullptr,2015source_dir)) {2016result.SetStatus(eReturnStatusSuccessFinishNoResult);2017} else {2018result.AppendErrorWithFormat("module importing failed: %s",2019error.AsCString());2020}2021}2022}20232024CommandOptions m_options;2025};20262027#define LLDB_OPTIONS_script_add2028#include "CommandOptions.inc"20292030class CommandObjectCommandsScriptAdd : public CommandObjectParsed,2031public IOHandlerDelegateMultiline {2032public:2033CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter)2034: CommandObjectParsed(interpreter, "command script add",2035"Add a scripted function as an LLDB command.",2036"Add a scripted function as an lldb command. "2037"If you provide a single argument, the command "2038"will be added at the root level of the command "2039"hierarchy. If there are more arguments they "2040"must be a path to a user-added container "2041"command, and the last element will be the new "2042"command name."),2043IOHandlerDelegateMultiline("DONE") {2044AddSimpleArgumentList(eArgTypeCommand, eArgRepeatPlus);2045}20462047~CommandObjectCommandsScriptAdd() override = default;20482049Options *GetOptions() override { return &m_options; }20502051void2052HandleArgumentCompletion(CompletionRequest &request,2053OptionElementVector &opt_element_vector) override {2054CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request,2055opt_element_vector);2056}20572058protected:2059class CommandOptions : public Options {2060public:2061CommandOptions() = default;20622063~CommandOptions() override = default;20642065Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,2066ExecutionContext *execution_context) override {2067Status error;2068const int short_option = m_getopt_table[option_idx].val;20692070switch (short_option) {2071case 'f':2072if (!option_arg.empty())2073m_funct_name = std::string(option_arg);2074break;2075case 'c':2076if (!option_arg.empty())2077m_class_name = std::string(option_arg);2078break;2079case 'h':2080if (!option_arg.empty())2081m_short_help = std::string(option_arg);2082break;2083case 'o':2084m_overwrite_lazy = eLazyBoolYes;2085break;2086case 'p':2087m_parsed_command = true;2088break;2089case 's':2090m_synchronicity =2091(ScriptedCommandSynchronicity)OptionArgParser::ToOptionEnum(2092option_arg, GetDefinitions()[option_idx].enum_values, 0, error);2093if (!error.Success())2094error.SetErrorStringWithFormat(2095"unrecognized value for synchronicity '%s'",2096option_arg.str().c_str());2097break;2098case 'C': {2099Status error;2100OptionDefinition definition = GetDefinitions()[option_idx];2101lldb::CompletionType completion_type =2102static_cast<lldb::CompletionType>(OptionArgParser::ToOptionEnum(2103option_arg, definition.enum_values, eNoCompletion, error));2104if (!error.Success())2105error.SetErrorStringWithFormat(2106"unrecognized value for command completion type '%s'",2107option_arg.str().c_str());2108m_completion_type = completion_type;2109} break;2110default:2111llvm_unreachable("Unimplemented option");2112}21132114return error;2115}21162117void OptionParsingStarting(ExecutionContext *execution_context) override {2118m_class_name.clear();2119m_funct_name.clear();2120m_short_help.clear();2121m_completion_type = eNoCompletion;2122m_overwrite_lazy = eLazyBoolCalculate;2123m_synchronicity = eScriptedCommandSynchronicitySynchronous;2124m_parsed_command = false;2125}21262127llvm::ArrayRef<OptionDefinition> GetDefinitions() override {2128return llvm::ArrayRef(g_script_add_options);2129}21302131// Instance variables to hold the values for command options.21322133std::string m_class_name;2134std::string m_funct_name;2135std::string m_short_help;2136LazyBool m_overwrite_lazy = eLazyBoolCalculate;2137ScriptedCommandSynchronicity m_synchronicity =2138eScriptedCommandSynchronicitySynchronous;2139CompletionType m_completion_type = eNoCompletion;2140bool m_parsed_command = false;2141};21422143void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {2144StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());2145if (output_sp && interactive) {2146output_sp->PutCString(g_python_command_instructions);2147output_sp->Flush();2148}2149}21502151void IOHandlerInputComplete(IOHandler &io_handler,2152std::string &data) override {2153StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();21542155ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();2156if (interpreter) {2157StringList lines;2158lines.SplitIntoLines(data);2159if (lines.GetSize() > 0) {2160std::string funct_name_str;2161if (interpreter->GenerateScriptAliasFunction(lines, funct_name_str)) {2162if (funct_name_str.empty()) {2163error_sp->Printf("error: unable to obtain a function name, didn't "2164"add python command.\n");2165error_sp->Flush();2166} else {2167// everything should be fine now, let's add this alias21682169CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(2170m_interpreter, m_cmd_name, funct_name_str, m_short_help,2171m_synchronicity, m_completion_type));2172if (!m_container) {2173Status error = m_interpreter.AddUserCommand(2174m_cmd_name, command_obj_sp, m_overwrite);2175if (error.Fail()) {2176error_sp->Printf("error: unable to add selected command: '%s'",2177error.AsCString());2178error_sp->Flush();2179}2180} else {2181llvm::Error llvm_error = m_container->LoadUserSubcommand(2182m_cmd_name, command_obj_sp, m_overwrite);2183if (llvm_error) {2184error_sp->Printf("error: unable to add selected command: '%s'",2185llvm::toString(std::move(llvm_error)).c_str());2186error_sp->Flush();2187}2188}2189}2190} else {2191error_sp->Printf(2192"error: unable to create function, didn't add python command\n");2193error_sp->Flush();2194}2195} else {2196error_sp->Printf("error: empty function, didn't add python command\n");2197error_sp->Flush();2198}2199} else {2200error_sp->Printf(2201"error: script interpreter missing, didn't add python command\n");2202error_sp->Flush();2203}22042205io_handler.SetIsDone(true);2206}22072208void DoExecute(Args &command, CommandReturnObject &result) override {2209if (GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) {2210result.AppendError("only scripting language supported for scripted "2211"commands is currently Python");2212return;2213}22142215if (command.GetArgumentCount() == 0) {2216result.AppendError("'command script add' requires at least one argument");2217return;2218}2219// Store the options in case we get multi-line input, also figure out the2220// default if not user supplied:2221switch (m_options.m_overwrite_lazy) {2222case eLazyBoolCalculate:2223m_overwrite = !GetDebugger().GetCommandInterpreter().GetRequireCommandOverwrite();2224break;2225case eLazyBoolYes:2226m_overwrite = true;2227break;2228case eLazyBoolNo:2229m_overwrite = false;2230}22312232Status path_error;2233m_container = GetCommandInterpreter().VerifyUserMultiwordCmdPath(2234command, true, path_error);22352236if (path_error.Fail()) {2237result.AppendErrorWithFormat("error in command path: %s",2238path_error.AsCString());2239return;2240}22412242if (!m_container) {2243// This is getting inserted into the root of the interpreter.2244m_cmd_name = std::string(command[0].ref());2245} else {2246size_t num_args = command.GetArgumentCount();2247m_cmd_name = std::string(command[num_args - 1].ref());2248}22492250m_short_help.assign(m_options.m_short_help);2251m_synchronicity = m_options.m_synchronicity;2252m_completion_type = m_options.m_completion_type;22532254// Handle the case where we prompt for the script code first:2255if (m_options.m_class_name.empty() && m_options.m_funct_name.empty()) {2256m_interpreter.GetPythonCommandsFromIOHandler(" ", // Prompt2257*this); // IOHandlerDelegate2258return;2259}22602261CommandObjectSP new_cmd_sp;2262if (m_options.m_class_name.empty()) {2263new_cmd_sp.reset(new CommandObjectPythonFunction(2264m_interpreter, m_cmd_name, m_options.m_funct_name,2265m_options.m_short_help, m_synchronicity, m_completion_type));2266} else {2267ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();2268if (!interpreter) {2269result.AppendError("cannot find ScriptInterpreter");2270return;2271}22722273auto cmd_obj_sp = interpreter->CreateScriptCommandObject(2274m_options.m_class_name.c_str());2275if (!cmd_obj_sp) {2276result.AppendErrorWithFormatv("cannot create helper object for: "2277"'{0}'", m_options.m_class_name);2278return;2279}22802281if (m_options.m_parsed_command) {2282new_cmd_sp = CommandObjectScriptingObjectParsed::Create(m_interpreter,2283m_cmd_name, cmd_obj_sp, m_synchronicity, result);2284if (!result.Succeeded())2285return;2286} else2287new_cmd_sp.reset(new CommandObjectScriptingObjectRaw(2288m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity,2289m_completion_type));2290}22912292// Assume we're going to succeed...2293result.SetStatus(eReturnStatusSuccessFinishNoResult);2294if (!m_container) {2295Status add_error =2296m_interpreter.AddUserCommand(m_cmd_name, new_cmd_sp, m_overwrite);2297if (add_error.Fail())2298result.AppendErrorWithFormat("cannot add command: %s",2299add_error.AsCString());2300} else {2301llvm::Error llvm_error =2302m_container->LoadUserSubcommand(m_cmd_name, new_cmd_sp, m_overwrite);2303if (llvm_error)2304result.AppendErrorWithFormat(2305"cannot add command: %s",2306llvm::toString(std::move(llvm_error)).c_str());2307}2308}23092310CommandOptions m_options;2311std::string m_cmd_name;2312CommandObjectMultiword *m_container = nullptr;2313std::string m_short_help;2314bool m_overwrite = false;2315ScriptedCommandSynchronicity m_synchronicity =2316eScriptedCommandSynchronicitySynchronous;2317CompletionType m_completion_type = eNoCompletion;2318};23192320// CommandObjectCommandsScriptList23212322class CommandObjectCommandsScriptList : public CommandObjectParsed {2323public:2324CommandObjectCommandsScriptList(CommandInterpreter &interpreter)2325: CommandObjectParsed(interpreter, "command script list",2326"List defined top-level scripted commands.",2327nullptr) {}23282329~CommandObjectCommandsScriptList() override = default;23302331void DoExecute(Args &command, CommandReturnObject &result) override {2332m_interpreter.GetHelp(result, CommandInterpreter::eCommandTypesUserDef);23332334result.SetStatus(eReturnStatusSuccessFinishResult);2335}2336};23372338// CommandObjectCommandsScriptClear23392340class CommandObjectCommandsScriptClear : public CommandObjectParsed {2341public:2342CommandObjectCommandsScriptClear(CommandInterpreter &interpreter)2343: CommandObjectParsed(interpreter, "command script clear",2344"Delete all scripted commands.", nullptr) {}23452346~CommandObjectCommandsScriptClear() override = default;23472348protected:2349void DoExecute(Args &command, CommandReturnObject &result) override {2350m_interpreter.RemoveAllUser();23512352result.SetStatus(eReturnStatusSuccessFinishResult);2353}2354};23552356// CommandObjectCommandsScriptDelete23572358class CommandObjectCommandsScriptDelete : public CommandObjectParsed {2359public:2360CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter)2361: CommandObjectParsed(2362interpreter, "command script delete",2363"Delete a scripted command by specifying the path to the command.",2364nullptr) {2365AddSimpleArgumentList(eArgTypeCommand, eArgRepeatPlus);2366}23672368~CommandObjectCommandsScriptDelete() override = default;23692370void2371HandleArgumentCompletion(CompletionRequest &request,2372OptionElementVector &opt_element_vector) override {2373lldb_private::CommandCompletions::CompleteModifiableCmdPathArgs(2374m_interpreter, request, opt_element_vector);2375}23762377protected:2378void DoExecute(Args &command, CommandReturnObject &result) override {23792380llvm::StringRef root_cmd = command[0].ref();2381size_t num_args = command.GetArgumentCount();23822383if (root_cmd.empty()) {2384result.AppendErrorWithFormat("empty root command name");2385return;2386}2387if (!m_interpreter.HasUserCommands() &&2388!m_interpreter.HasUserMultiwordCommands()) {2389result.AppendErrorWithFormat("can only delete user defined commands, "2390"but no user defined commands found");2391return;2392}23932394CommandObjectSP cmd_sp = m_interpreter.GetCommandSPExact(root_cmd);2395if (!cmd_sp) {2396result.AppendErrorWithFormat("command '%s' not found.",2397command[0].c_str());2398return;2399}2400if (!cmd_sp->IsUserCommand()) {2401result.AppendErrorWithFormat("command '%s' is not a user command.",2402command[0].c_str());2403return;2404}2405if (cmd_sp->GetAsMultiwordCommand() && num_args == 1) {2406result.AppendErrorWithFormat("command '%s' is a multi-word command.\n "2407"Delete with \"command container delete\"",2408command[0].c_str());2409return;2410}24112412if (command.GetArgumentCount() == 1) {2413m_interpreter.RemoveUser(root_cmd);2414result.SetStatus(eReturnStatusSuccessFinishResult);2415return;2416}2417// We're deleting a command from a multiword command. Verify the command2418// path:2419Status error;2420CommandObjectMultiword *container =2421GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true,2422error);2423if (error.Fail()) {2424result.AppendErrorWithFormat("could not resolve command path: %s",2425error.AsCString());2426return;2427}2428if (!container) {2429// This means that command only had a leaf command, so the container is2430// the root. That should have been handled above.2431result.AppendErrorWithFormat("could not find a container for '%s'",2432command[0].c_str());2433return;2434}2435const char *leaf_cmd = command[num_args - 1].c_str();2436llvm::Error llvm_error =2437container->RemoveUserSubcommand(leaf_cmd,2438/* multiword not okay */ false);2439if (llvm_error) {2440result.AppendErrorWithFormat(2441"could not delete command '%s': %s", leaf_cmd,2442llvm::toString(std::move(llvm_error)).c_str());2443return;2444}24452446Stream &out_stream = result.GetOutputStream();24472448out_stream << "Deleted command:";2449for (size_t idx = 0; idx < num_args; idx++) {2450out_stream << ' ';2451out_stream << command[idx].c_str();2452}2453out_stream << '\n';2454result.SetStatus(eReturnStatusSuccessFinishResult);2455}2456};24572458#pragma mark CommandObjectMultiwordCommandsScript24592460// CommandObjectMultiwordCommandsScript24612462class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword {2463public:2464CommandObjectMultiwordCommandsScript(CommandInterpreter &interpreter)2465: CommandObjectMultiword(2466interpreter, "command script",2467"Commands for managing custom "2468"commands implemented by "2469"interpreter scripts.",2470"command script <subcommand> [<subcommand-options>]") {2471LoadSubCommand("add", CommandObjectSP(2472new CommandObjectCommandsScriptAdd(interpreter)));2473LoadSubCommand(2474"delete",2475CommandObjectSP(new CommandObjectCommandsScriptDelete(interpreter)));2476LoadSubCommand(2477"clear",2478CommandObjectSP(new CommandObjectCommandsScriptClear(interpreter)));2479LoadSubCommand("list", CommandObjectSP(new CommandObjectCommandsScriptList(2480interpreter)));2481LoadSubCommand(2482"import",2483CommandObjectSP(new CommandObjectCommandsScriptImport(interpreter)));2484}24852486~CommandObjectMultiwordCommandsScript() override = default;2487};24882489#pragma mark CommandObjectCommandContainer2490#define LLDB_OPTIONS_container_add2491#include "CommandOptions.inc"24922493class CommandObjectCommandsContainerAdd : public CommandObjectParsed {2494public:2495CommandObjectCommandsContainerAdd(CommandInterpreter &interpreter)2496: CommandObjectParsed(2497interpreter, "command container add",2498"Add a container command to lldb. Adding to built-"2499"in container commands is not allowed.",2500"command container add [[path1]...] container-name") {2501AddSimpleArgumentList(eArgTypeCommand, eArgRepeatPlus);2502}25032504~CommandObjectCommandsContainerAdd() override = default;25052506Options *GetOptions() override { return &m_options; }25072508void2509HandleArgumentCompletion(CompletionRequest &request,2510OptionElementVector &opt_element_vector) override {2511lldb_private::CommandCompletions::CompleteModifiableCmdPathArgs(2512m_interpreter, request, opt_element_vector);2513}25142515protected:2516class CommandOptions : public Options {2517public:2518CommandOptions() = default;25192520~CommandOptions() override = default;25212522Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,2523ExecutionContext *execution_context) override {2524Status error;2525const int short_option = m_getopt_table[option_idx].val;25262527switch (short_option) {2528case 'h':2529if (!option_arg.empty())2530m_short_help = std::string(option_arg);2531break;2532case 'o':2533m_overwrite = true;2534break;2535case 'H':2536if (!option_arg.empty())2537m_long_help = std::string(option_arg);2538break;2539default:2540llvm_unreachable("Unimplemented option");2541}25422543return error;2544}25452546void OptionParsingStarting(ExecutionContext *execution_context) override {2547m_short_help.clear();2548m_long_help.clear();2549m_overwrite = false;2550}25512552llvm::ArrayRef<OptionDefinition> GetDefinitions() override {2553return llvm::ArrayRef(g_container_add_options);2554}25552556// Instance variables to hold the values for command options.25572558std::string m_short_help;2559std::string m_long_help;2560bool m_overwrite = false;2561};2562void DoExecute(Args &command, CommandReturnObject &result) override {2563size_t num_args = command.GetArgumentCount();25642565if (num_args == 0) {2566result.AppendError("no command was specified");2567return;2568}25692570if (num_args == 1) {2571// We're adding this as a root command, so use the interpreter.2572const char *cmd_name = command.GetArgumentAtIndex(0);2573auto cmd_sp = CommandObjectSP(new CommandObjectMultiword(2574GetCommandInterpreter(), cmd_name, m_options.m_short_help.c_str(),2575m_options.m_long_help.c_str()));2576cmd_sp->GetAsMultiwordCommand()->SetRemovable(true);2577Status add_error = GetCommandInterpreter().AddUserCommand(2578cmd_name, cmd_sp, m_options.m_overwrite);2579if (add_error.Fail()) {2580result.AppendErrorWithFormat("error adding command: %s",2581add_error.AsCString());2582return;2583}2584result.SetStatus(eReturnStatusSuccessFinishNoResult);2585return;2586}25872588// We're adding this to a subcommand, first find the subcommand:2589Status path_error;2590CommandObjectMultiword *add_to_me =2591GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true,2592path_error);25932594if (!add_to_me) {2595result.AppendErrorWithFormat("error adding command: %s",2596path_error.AsCString());2597return;2598}25992600const char *cmd_name = command.GetArgumentAtIndex(num_args - 1);2601auto cmd_sp = CommandObjectSP(new CommandObjectMultiword(2602GetCommandInterpreter(), cmd_name, m_options.m_short_help.c_str(),2603m_options.m_long_help.c_str()));2604llvm::Error llvm_error =2605add_to_me->LoadUserSubcommand(cmd_name, cmd_sp, m_options.m_overwrite);2606if (llvm_error) {2607result.AppendErrorWithFormat("error adding subcommand: %s",2608llvm::toString(std::move(llvm_error)).c_str());2609return;2610}26112612result.SetStatus(eReturnStatusSuccessFinishNoResult);2613}26142615private:2616CommandOptions m_options;2617};26182619#define LLDB_OPTIONS_multiword_delete2620#include "CommandOptions.inc"2621class CommandObjectCommandsContainerDelete : public CommandObjectParsed {2622public:2623CommandObjectCommandsContainerDelete(CommandInterpreter &interpreter)2624: CommandObjectParsed(2625interpreter, "command container delete",2626"Delete a container command previously added to "2627"lldb.",2628"command container delete [[path1] ...] container-cmd") {2629AddSimpleArgumentList(eArgTypeCommand, eArgRepeatPlus);2630}26312632~CommandObjectCommandsContainerDelete() override = default;26332634void2635HandleArgumentCompletion(CompletionRequest &request,2636OptionElementVector &opt_element_vector) override {2637lldb_private::CommandCompletions::CompleteModifiableCmdPathArgs(2638m_interpreter, request, opt_element_vector);2639}26402641protected:2642void DoExecute(Args &command, CommandReturnObject &result) override {2643size_t num_args = command.GetArgumentCount();26442645if (num_args == 0) {2646result.AppendError("No command was specified.");2647return;2648}26492650if (num_args == 1) {2651// We're removing a root command, so we need to delete it from the2652// interpreter.2653const char *cmd_name = command.GetArgumentAtIndex(0);2654// Let's do a little more work here so we can do better error reporting.2655CommandInterpreter &interp = GetCommandInterpreter();2656CommandObjectSP cmd_sp = interp.GetCommandSPExact(cmd_name);2657if (!cmd_sp) {2658result.AppendErrorWithFormat("container command %s doesn't exist.",2659cmd_name);2660return;2661}2662if (!cmd_sp->IsUserCommand()) {2663result.AppendErrorWithFormat(2664"container command %s is not a user command", cmd_name);2665return;2666}2667if (!cmd_sp->GetAsMultiwordCommand()) {2668result.AppendErrorWithFormat("command %s is not a container command",2669cmd_name);2670return;2671}26722673bool did_remove = GetCommandInterpreter().RemoveUserMultiword(cmd_name);2674if (!did_remove) {2675result.AppendErrorWithFormat("error removing command %s.", cmd_name);2676return;2677}26782679result.SetStatus(eReturnStatusSuccessFinishNoResult);2680return;2681}26822683// We're removing a subcommand, first find the subcommand's owner:2684Status path_error;2685CommandObjectMultiword *container =2686GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true,2687path_error);26882689if (!container) {2690result.AppendErrorWithFormat("error removing container command: %s",2691path_error.AsCString());2692return;2693}2694const char *leaf = command.GetArgumentAtIndex(num_args - 1);2695llvm::Error llvm_error =2696container->RemoveUserSubcommand(leaf, /* multiword okay */ true);2697if (llvm_error) {2698result.AppendErrorWithFormat("error removing container command: %s",2699llvm::toString(std::move(llvm_error)).c_str());2700return;2701}2702result.SetStatus(eReturnStatusSuccessFinishNoResult);2703}2704};27052706class CommandObjectCommandContainer : public CommandObjectMultiword {2707public:2708CommandObjectCommandContainer(CommandInterpreter &interpreter)2709: CommandObjectMultiword(2710interpreter, "command container",2711"Commands for adding container commands to lldb. "2712"Container commands are containers for other commands. You can "2713"add nested container commands by specifying a command path, "2714"but you can't add commands into the built-in command hierarchy.",2715"command container <subcommand> [<subcommand-options>]") {2716LoadSubCommand("add", CommandObjectSP(new CommandObjectCommandsContainerAdd(2717interpreter)));2718LoadSubCommand(2719"delete",2720CommandObjectSP(new CommandObjectCommandsContainerDelete(interpreter)));2721}27222723~CommandObjectCommandContainer() override = default;2724};27252726#pragma mark CommandObjectMultiwordCommands27272728// CommandObjectMultiwordCommands27292730CommandObjectMultiwordCommands::CommandObjectMultiwordCommands(2731CommandInterpreter &interpreter)2732: CommandObjectMultiword(interpreter, "command",2733"Commands for managing custom LLDB commands.",2734"command <subcommand> [<subcommand-options>]") {2735LoadSubCommand("source",2736CommandObjectSP(new CommandObjectCommandsSource(interpreter)));2737LoadSubCommand("alias",2738CommandObjectSP(new CommandObjectCommandsAlias(interpreter)));2739LoadSubCommand("unalias", CommandObjectSP(2740new CommandObjectCommandsUnalias(interpreter)));2741LoadSubCommand("delete",2742CommandObjectSP(new CommandObjectCommandsDelete(interpreter)));2743LoadSubCommand("container", CommandObjectSP(new CommandObjectCommandContainer(2744interpreter)));2745LoadSubCommand(2746"regex", CommandObjectSP(new CommandObjectCommandsAddRegex(interpreter)));2747LoadSubCommand(2748"script",2749CommandObjectSP(new CommandObjectMultiwordCommandsScript(interpreter)));2750}27512752CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands() = default;275327542755