aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Commands/CommandObjectCommands.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Commands/CommandObjectCommands.cpp')
-rw-r--r--lldb/source/Commands/CommandObjectCommands.cpp510
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(