diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectCommands.cpp')
-rw-r--r-- | lldb/source/Commands/CommandObjectCommands.cpp | 510 |
1 files changed, 454 insertions, 56 deletions
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp index 63927987..1ec54cf 100644 --- a/lldb/source/Commands/CommandObjectCommands.cpp +++ b/lldb/source/Commands/CommandObjectCommands.cpp @@ -443,6 +443,14 @@ protected: return false; } + if (m_interpreter.UserMultiwordCommandExists(alias_command)) { + result.AppendErrorWithFormat( + "'%s' is a user container command and cannot be overwritten.\n" + "Delete it first with 'command container delete'\n", + args[0].c_str()); + return false; + } + // Get CommandObject that is being aliased. The command name is read from // the front of raw_command_string. raw_command_string is returned with the // name of the command object stripped off the front. @@ -528,6 +536,14 @@ protected: return false; } + if (m_interpreter.UserMultiwordCommandExists(alias_command)) { + result.AppendErrorWithFormat( + "'%s' is user container command and cannot be overwritten.\n" + "Delete it first with 'command container delete'", + alias_command.c_str()); + return false; + } + CommandObjectSP command_obj_sp( m_interpreter.GetCommandSPExact(actual_command, true)); CommandObjectSP subcommand_obj_sp; @@ -1371,14 +1387,21 @@ public: CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "command script add", "Add a scripted function as an LLDB command.", - nullptr), + "Add a scripted function as an lldb command. " + "If you provide a single argument, the command " + "will be added at the root level of the command " + "hierarchy. If there are more arguments they " + "must be a path to a user-added container " + "command, and the last element will be the new " + "command name."), IOHandlerDelegateMultiline("DONE"), m_options() { CommandArgumentEntry arg1; CommandArgumentData cmd_arg; - // Define the first (and only) variant of this arg. - cmd_arg.arg_type = eArgTypeCommandName; - cmd_arg.arg_repetition = eArgRepeatPlain; + // This is one or more command names, which form the path to the command + // you want to add. + cmd_arg.arg_type = eArgTypeCommand; + cmd_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the // argument entry. @@ -1392,6 +1415,13 @@ public: Options *GetOptions() override { return &m_options; } + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request, + opt_element_vector); + } + protected: class CommandOptions : public Options { public: @@ -1418,6 +1448,9 @@ protected: if (!option_arg.empty()) m_short_help = std::string(option_arg); break; + case 'o': + m_overwrite = true; + break; case 's': m_synchronicity = (ScriptedCommandSynchronicity)OptionArgParser::ToOptionEnum( @@ -1438,6 +1471,7 @@ protected: m_class_name.clear(); m_funct_name.clear(); m_short_help.clear(); + m_overwrite = false; m_synchronicity = eScriptedCommandSynchronicitySynchronous; } @@ -1450,6 +1484,7 @@ protected: std::string m_class_name; std::string m_funct_name; std::string m_short_help; + bool m_overwrite; ScriptedCommandSynchronicity m_synchronicity = eScriptedCommandSynchronicitySynchronous; }; @@ -1484,26 +1519,36 @@ protected: CommandObjectSP command_obj_sp(new CommandObjectPythonFunction( m_interpreter, m_cmd_name, funct_name_str, m_short_help, m_synchronicity)); - - if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, - true)) { - error_sp->Printf("error: unable to add selected command, didn't " - "add python command.\n"); - error_sp->Flush(); + if (!m_container) { + Status error = m_interpreter.AddUserCommand( + m_cmd_name, command_obj_sp, m_overwrite); + if (error.Fail()) { + error_sp->Printf("error: unable to add selected command: '%s'", + error.AsCString()); + error_sp->Flush(); + } + } else { + llvm::Error llvm_error = m_container->LoadUserSubcommand( + m_cmd_name, command_obj_sp, m_overwrite); + if (llvm_error) { + error_sp->Printf("error: unable to add selected command: '%s'", + llvm::toString(std::move(llvm_error)).c_str()); + error_sp->Flush(); + } } } } else { error_sp->Printf( - "error: unable to create function, didn't add python command.\n"); + "error: unable to create function, didn't add python command\n"); error_sp->Flush(); } } else { - error_sp->Printf("error: empty function, didn't add python command.\n"); + error_sp->Printf("error: empty function, didn't add python command\n"); error_sp->Flush(); } } else { error_sp->Printf( - "error: script interpreter missing, didn't add python command.\n"); + "error: script interpreter missing, didn't add python command\n"); error_sp->Flush(); } @@ -1517,31 +1562,45 @@ protected: return false; } - if (command.GetArgumentCount() != 1) { - result.AppendError("'command script add' requires one argument"); + if (command.GetArgumentCount() == 0) { + result.AppendError("'command script add' requires at least one argument"); return false; } - // Store the options in case we get multi-line input - m_cmd_name = std::string(command[0].ref()); + m_overwrite = m_options.m_overwrite; + Status path_error; + m_container = GetCommandInterpreter().VerifyUserMultiwordCmdPath( + command, true, path_error); + + if (path_error.Fail()) { + result.AppendErrorWithFormat("error in command path: %s", + path_error.AsCString()); + return false; + } + + if (!m_container) { + // This is getting inserted into the root of the interpreter. + m_cmd_name = std::string(command[0].ref()); + } else { + size_t num_args = command.GetArgumentCount(); + m_cmd_name = std::string(command[num_args - 1].ref()); + } + m_short_help.assign(m_options.m_short_help); m_synchronicity = m_options.m_synchronicity; + // Handle the case where we prompt for the script code first: + if (m_options.m_class_name.empty() && m_options.m_funct_name.empty()) { + m_interpreter.GetPythonCommandsFromIOHandler(" ", // Prompt + *this); // IOHandlerDelegate + return result.Succeeded(); + } + + CommandObjectSP new_cmd_sp; if (m_options.m_class_name.empty()) { - if (m_options.m_funct_name.empty()) { - m_interpreter.GetPythonCommandsFromIOHandler( - " ", // Prompt - *this); // IOHandlerDelegate - } else { - CommandObjectSP new_cmd(new CommandObjectPythonFunction( - m_interpreter, m_cmd_name, m_options.m_funct_name, - m_options.m_short_help, m_synchronicity)); - if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { - result.SetStatus(eReturnStatusSuccessFinishNoResult); - } else { - result.AppendError("cannot add command"); - } - } + new_cmd_sp.reset(new CommandObjectPythonFunction( + m_interpreter, m_cmd_name, m_options.m_funct_name, + m_options.m_short_help, m_synchronicity)); } else { ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter(); if (!interpreter) { @@ -1556,21 +1615,33 @@ protected: return false; } - CommandObjectSP new_cmd(new CommandObjectScriptingObject( + new_cmd_sp.reset(new CommandObjectScriptingObject( m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity)); - if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { - result.SetStatus(eReturnStatusSuccessFinishNoResult); - } else { - result.AppendError("cannot add command"); - } } - + + // Assume we're going to succeed... + result.SetStatus(eReturnStatusSuccessFinishNoResult); + if (!m_container) { + Status add_error = + m_interpreter.AddUserCommand(m_cmd_name, new_cmd_sp, m_overwrite); + if (add_error.Fail()) + result.AppendErrorWithFormat("cannot add command: %s", + add_error.AsCString()); + } else { + llvm::Error llvm_error = + m_container->LoadUserSubcommand(m_cmd_name, new_cmd_sp, m_overwrite); + if (llvm_error) + result.AppendErrorWithFormat("cannot add command: %s", + llvm::toString(std::move(llvm_error)).c_str()); + } return result.Succeeded(); } CommandOptions m_options; std::string m_cmd_name; + CommandObjectMultiword *m_container = nullptr; std::string m_short_help; + bool m_overwrite; ScriptedCommandSynchronicity m_synchronicity; }; @@ -1580,7 +1651,8 @@ class CommandObjectCommandsScriptList : public CommandObjectParsed { public: CommandObjectCommandsScriptList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "command script list", - "List defined scripted commands.", nullptr) {} + "List defined top-level scripted commands.", + nullptr) {} ~CommandObjectCommandsScriptList() override = default; @@ -1628,14 +1700,17 @@ protected: class CommandObjectCommandsScriptDelete : public CommandObjectParsed { public: CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter) - : CommandObjectParsed(interpreter, "command script delete", - "Delete a scripted command.", nullptr) { + : CommandObjectParsed( + interpreter, "command script delete", + "Delete a scripted command by specifying the path to the command.", + nullptr) { CommandArgumentEntry arg1; CommandArgumentData cmd_arg; - // Define the first (and only) variant of this arg. - cmd_arg.arg_type = eArgTypeCommandName; - cmd_arg.arg_repetition = eArgRepeatPlain; + // This is a list of command names forming the path to the command + // to be deleted. + cmd_arg.arg_type = eArgTypeCommand; + cmd_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the // argument entry. @@ -1650,30 +1725,86 @@ public: void HandleArgumentCompletion(CompletionRequest &request, OptionElementVector &opt_element_vector) override { - if (!m_interpreter.HasCommands() || request.GetCursorIndex() != 0) - return; - - for (const auto &c : m_interpreter.GetUserCommands()) - request.TryCompleteCurrentArg(c.first, c.second->GetHelp()); + CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request, + opt_element_vector); } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { - if (command.GetArgumentCount() != 1) { - result.AppendError("'command script delete' requires one argument"); + llvm::StringRef root_cmd = command[0].ref(); + size_t num_args = command.GetArgumentCount(); + + if (root_cmd.empty()) { + result.AppendErrorWithFormat("empty root command name"); + return false; + } + if (!m_interpreter.HasUserCommands() && + !m_interpreter.HasUserMultiwordCommands()) { + result.AppendErrorWithFormat("can only delete user defined commands, " + "but no user defined commands found"); return false; } - auto cmd_name = command[0].ref(); + CommandObjectSP cmd_sp = m_interpreter.GetCommandSPExact(root_cmd); + if (!cmd_sp) { + result.AppendErrorWithFormat("command '%s' not found.", + command[0].c_str()); + return false; + } + if (!cmd_sp->IsUserCommand()) { + result.AppendErrorWithFormat("command '%s' is not a user command.", + command[0].c_str()); + return false; + } + if (cmd_sp->GetAsMultiwordCommand() && num_args == 1) { + result.AppendErrorWithFormat("command '%s' is a multi-word command.\n " + "Delete with \"command container delete\"", + command[0].c_str()); + return false; + } - if (cmd_name.empty() || !m_interpreter.HasUserCommands() || - !m_interpreter.UserCommandExists(cmd_name)) { - result.AppendErrorWithFormat("command %s not found", command[0].c_str()); + if (command.GetArgumentCount() == 1) { + m_interpreter.RemoveUser(root_cmd); + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + // We're deleting a command from a multiword command. Verify the command + // path: + Status error; + CommandObjectMultiword *container = + GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true, + error); + if (error.Fail()) { + result.AppendErrorWithFormat("could not resolve command path: %s", + error.AsCString()); + return false; + } + if (!container) { + // This means that command only had a leaf command, so the container is + // the root. That should have been handled above. + result.AppendErrorWithFormat("could not find a container for '%s'", + command[0].c_str()); + return false; + } + const char *leaf_cmd = command[num_args - 1].c_str(); + llvm::Error llvm_error = container->RemoveUserSubcommand(leaf_cmd, + /* multiword not okay */ false); + if (llvm_error) { + result.AppendErrorWithFormat("could not delete command '%s': %s", + leaf_cmd, + llvm::toString(std::move(llvm_error)).c_str()); return false; } - m_interpreter.RemoveUser(cmd_name); + Stream &out_stream = result.GetOutputStream(); + + out_stream << "Deleted command:"; + for (size_t idx = 0; idx < num_args; idx++) { + out_stream << ' '; + out_stream << command[idx].c_str(); + } + out_stream << '\n'; result.SetStatus(eReturnStatusSuccessFinishResult); return true; } @@ -1710,6 +1841,271 @@ public: ~CommandObjectMultiwordCommandsScript() override = default; }; +#pragma mark CommandObjectCommandContainer +#define LLDB_OPTIONS_container_add +#include "CommandOptions.inc" + +class CommandObjectCommandsContainerAdd : public CommandObjectParsed { +public: + CommandObjectCommandsContainerAdd(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "command container add", + "Add a container command to lldb. Adding to built-" + "in container commands is not allowed.", + "command container add [[path1]...] container-name") { + CommandArgumentEntry arg1; + CommandArgumentData cmd_arg; + + // This is one or more command names, which form the path to the command + // you want to add. + cmd_arg.arg_type = eArgTypeCommand; + cmd_arg.arg_repetition = eArgRepeatPlus; + + // There is only one variant this argument could be; put it into the + // argument entry. + arg1.push_back(cmd_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back(arg1); + } + + ~CommandObjectCommandsContainerAdd() override = default; + + Options *GetOptions() override { return &m_options; } + + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request, + opt_element_vector); + } + +protected: + class CommandOptions : public Options { + public: + CommandOptions() : Options(), m_short_help(), m_long_help() {} + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'h': + if (!option_arg.empty()) + m_short_help = std::string(option_arg); + break; + case 'o': + m_overwrite = true; + break; + case 'H': + if (!option_arg.empty()) + m_long_help = std::string(option_arg); + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_short_help.clear(); + m_long_help.clear(); + m_overwrite = false; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_container_add_options); + } + + // Instance variables to hold the values for command options. + + std::string m_short_help; + std::string m_long_help; + bool m_overwrite = false; + }; + bool DoExecute(Args &command, CommandReturnObject &result) override { + size_t num_args = command.GetArgumentCount(); + + if (num_args == 0) { + result.AppendError("no command was specified"); + return false; + } + + if (num_args == 1) { + // We're adding this as a root command, so use the interpreter. + const char *cmd_name = command.GetArgumentAtIndex(0); + auto cmd_sp = CommandObjectSP(new CommandObjectMultiword( + GetCommandInterpreter(), cmd_name, m_options.m_short_help.c_str(), + m_options.m_long_help.c_str())); + cmd_sp->GetAsMultiwordCommand()->SetRemovable(true); + Status add_error = GetCommandInterpreter().AddUserCommand( + cmd_name, cmd_sp, m_options.m_overwrite); + if (add_error.Fail()) { + result.AppendErrorWithFormat("error adding command: %s", + add_error.AsCString()); + return false; + } + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } + + // We're adding this to a subcommand, first find the subcommand: + Status path_error; + CommandObjectMultiword *add_to_me = + GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true, + path_error); + + if (!add_to_me) { + result.AppendErrorWithFormat("error adding command: %s", + path_error.AsCString()); + return false; + } + + const char *cmd_name = command.GetArgumentAtIndex(num_args - 1); + auto cmd_sp = CommandObjectSP(new CommandObjectMultiword( + GetCommandInterpreter(), cmd_name, m_options.m_short_help.c_str(), + m_options.m_long_help.c_str())); + llvm::Error llvm_error = + add_to_me->LoadUserSubcommand(cmd_name, cmd_sp, m_options.m_overwrite); + if (llvm_error) { + result.AppendErrorWithFormat("error adding subcommand: %s", + llvm::toString(std::move(llvm_error)).c_str()); + return false; + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } + +private: + CommandOptions m_options; +}; + +#define LLDB_OPTIONS_multiword_delete +#include "CommandOptions.inc" +class CommandObjectCommandsContainerDelete : public CommandObjectParsed { +public: + CommandObjectCommandsContainerDelete(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "command container delete", + "Delete a container command previously added to " + "lldb.", + "command container delete [[path1] ...] container-cmd") { + CommandArgumentEntry arg1; + CommandArgumentData cmd_arg; + + // This is one or more command names, which form the path to the command + // you want to add. + cmd_arg.arg_type = eArgTypeCommand; + cmd_arg.arg_repetition = eArgRepeatPlus; + + // There is only one variant this argument could be; put it into the + // argument entry. + arg1.push_back(cmd_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back(arg1); + } + + ~CommandObjectCommandsContainerDelete() override = default; + + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::CompleteModifiableCmdPathArgs(m_interpreter, request, + opt_element_vector); + } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + size_t num_args = command.GetArgumentCount(); + + if (num_args == 0) { + result.AppendError("No command was specified."); + return false; + } + + if (num_args == 1) { + // We're removing a root command, so we need to delete it from the + // interpreter. + const char *cmd_name = command.GetArgumentAtIndex(0); + // Let's do a little more work here so we can do better error reporting. + CommandInterpreter &interp = GetCommandInterpreter(); + CommandObjectSP cmd_sp = interp.GetCommandSPExact(cmd_name); + if (!cmd_sp) { + result.AppendErrorWithFormat("container command %s doesn't exist.", + cmd_name); + return false; + } + if (!cmd_sp->IsUserCommand()) { + result.AppendErrorWithFormat( + "container command %s is not a user command", cmd_name); + return false; + } + if (!cmd_sp->GetAsMultiwordCommand()) { + result.AppendErrorWithFormat("command %s is not a container command", + cmd_name); + return false; + } + + bool did_remove = GetCommandInterpreter().RemoveUserMultiword(cmd_name); + if (!did_remove) { + result.AppendErrorWithFormat("error removing command %s.", cmd_name); + return false; + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } + + // We're removing a subcommand, first find the subcommand's owner: + Status path_error; + CommandObjectMultiword *container = + GetCommandInterpreter().VerifyUserMultiwordCmdPath(command, true, + path_error); + + if (!container) { + result.AppendErrorWithFormat("error removing container command: %s", + path_error.AsCString()); + return false; + } + const char *leaf = command.GetArgumentAtIndex(num_args - 1); + llvm::Error llvm_error = + container->RemoveUserSubcommand(leaf, /* multiword okay */ true); + if (llvm_error) { + result.AppendErrorWithFormat("error removing container command: %s", + llvm::toString(std::move(llvm_error)).c_str()); + return false; + } + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } +}; + +class CommandObjectCommandContainer : public CommandObjectMultiword { +public: + CommandObjectCommandContainer(CommandInterpreter &interpreter) + : CommandObjectMultiword( + interpreter, "command container", + "Commands for adding container commands to lldb. " + "Container commands are containers for other commands. You can" + "add nested container commands by specifying a command path, but " + "but you can't add commands into the built-in command hierarchy.", + "command container <subcommand> [<subcommand-options>]") { + LoadSubCommand("add", CommandObjectSP(new CommandObjectCommandsContainerAdd( + interpreter))); + LoadSubCommand( + "delete", + CommandObjectSP(new CommandObjectCommandsContainerDelete(interpreter))); + } + + ~CommandObjectCommandContainer() override = default; +}; + #pragma mark CommandObjectMultiwordCommands // CommandObjectMultiwordCommands @@ -1727,6 +2123,8 @@ CommandObjectMultiwordCommands::CommandObjectMultiwordCommands( new CommandObjectCommandsUnalias(interpreter))); LoadSubCommand("delete", CommandObjectSP(new CommandObjectCommandsDelete(interpreter))); + LoadSubCommand("container", CommandObjectSP(new CommandObjectCommandContainer( + interpreter))); LoadSubCommand( "regex", CommandObjectSP(new CommandObjectCommandsAddRegex(interpreter))); LoadSubCommand( |