aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Commands/CommandObjectCommands.cpp
diff options
context:
space:
mode:
authorjimingham <jingham@apple.com>2024-02-13 11:09:47 -0800
committerGitHub <noreply@github.com>2024-02-13 11:09:47 -0800
commita69ecb2420f644e31f18fcc61a07b3ca627e8939 (patch)
tree3ef79076597be772ad71ff8b1d9c2fff4e333bcd /lldb/source/Commands/CommandObjectCommands.cpp
parenta04c6366b156f508cdf84a32ef4484b53a6dabee (diff)
downloadllvm-a69ecb2420f644e31f18fcc61a07b3ca627e8939.zip
llvm-a69ecb2420f644e31f18fcc61a07b3ca627e8939.tar.gz
llvm-a69ecb2420f644e31f18fcc61a07b3ca627e8939.tar.bz2
Add the ability to define a Python based command that uses CommandObjectParsed (#70734)
This allows you to specify options and arguments and their definitions and then have lldb handle the completions, help, etc. in the same way that lldb does for its parsed commands internally. This feature has some design considerations as well as the code, so I've also set up an RFC, but I did this one first and will put the RFC address in here once I've pushed it... Note, the lldb "ParsedCommand interface" doesn't actually do all the work that it should. For instance, saying the type of an option that has a completer doesn't automatically hook up the completer, and ditto for argument values. We also do almost no work to verify that the arguments match their definition, or do auto-completion for them. This patch allows you to make a command that's bug-for-bug compatible with built-in ones, but I didn't want to stall it on getting the auto-command checking to work all the way correctly. As an overall design note, my primary goal here was to make an interface that worked well in the script language. For that I needed, for instance, to have a property-based way to get all the option values that were specified. It was much more convenient to do that by making a fairly bare-bones C interface to define the options and arguments of a command, and set their values, and then wrap that in a Python class (installed along with the other bits of the lldb python module) which you can then derive from to make your new command. This approach will also make it easier to experiment. See the file test_commands.py in the test case for examples of how this works.
Diffstat (limited to 'lldb/source/Commands/CommandObjectCommands.cpp')
-rw-r--r--lldb/source/Commands/CommandObjectCommands.cpp729
1 files changed, 718 insertions, 11 deletions
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index a51e5ab..3dfd452 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1151,13 +1151,16 @@ private:
CompletionType m_completion_type = eNoCompletion;
};
-class CommandObjectScriptingObject : public CommandObjectRaw {
+/// This class implements a "raw" scripted command. lldb does no parsing of the
+/// command line, instead passing the line unaltered (except for backtick
+/// substitution).
+class CommandObjectScriptingObjectRaw : public CommandObjectRaw {
public:
- CommandObjectScriptingObject(CommandInterpreter &interpreter,
- std::string name,
- StructuredData::GenericSP cmd_obj_sp,
- ScriptedCommandSynchronicity synch,
- CompletionType completion_type)
+ CommandObjectScriptingObjectRaw(CommandInterpreter &interpreter,
+ std::string name,
+ StructuredData::GenericSP cmd_obj_sp,
+ ScriptedCommandSynchronicity synch,
+ CompletionType completion_type)
: CommandObjectRaw(interpreter, name), m_cmd_obj_sp(cmd_obj_sp),
m_synchro(synch), m_fetched_help_short(false),
m_fetched_help_long(false), m_completion_type(completion_type) {
@@ -1168,7 +1171,7 @@ public:
GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp));
}
- ~CommandObjectScriptingObject() override = default;
+ ~CommandObjectScriptingObjectRaw() override = default;
void
HandleArgumentCompletion(CompletionRequest &request,
@@ -1246,6 +1249,699 @@ private:
CompletionType m_completion_type = eNoCompletion;
};
+
+/// This command implements a lldb parsed scripted command. The command
+/// provides a definition of the options and arguments, and a option value
+/// setting callback, and then the command's execution function gets passed
+/// just the parsed arguments.
+/// Note, implementing a command in Python using these base interfaces is a bit
+/// of a pain, but it is much easier to export this low level interface, and
+/// then make it nicer on the Python side, than to try to do that in a
+/// script language neutral way.
+/// So I've also added a base class in Python that provides a table-driven
+/// way of defining the options and arguments, which automatically fills the
+/// option values, making them available as properties in Python.
+///
+class CommandObjectScriptingObjectParsed : public CommandObjectParsed {
+private:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions(CommandInterpreter &interpreter,
+ StructuredData::GenericSP cmd_obj_sp) : m_interpreter(interpreter),
+ m_cmd_obj_sp(cmd_obj_sp) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ ScriptInterpreter *scripter =
+ m_interpreter.GetDebugger().GetScriptInterpreter();
+ if (!scripter) {
+ error.SetErrorString("No script interpreter for SetOptionValue.");
+ return error;
+ }
+ if (!m_cmd_obj_sp) {
+ error.SetErrorString("SetOptionValue called with empty cmd_obj.");
+ return error;
+ }
+ if (!m_options_definition_up) {
+ error.SetErrorString("SetOptionValue called before options definitions "
+ "were created.");
+ return error;
+ }
+ // Pass the long option, since you aren't actually required to have a
+ // short_option, and for those options the index or short option character
+ // aren't meaningful on the python side.
+ const char * long_option =
+ m_options_definition_up.get()[option_idx].long_option;
+ bool success = scripter->SetOptionValueForCommandObject(m_cmd_obj_sp,
+ execution_context, long_option, option_arg);
+ if (!success)
+ error.SetErrorStringWithFormatv("Error setting option: {0} to {1}",
+ long_option, option_arg);
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ ScriptInterpreter *scripter =
+ m_interpreter.GetDebugger().GetScriptInterpreter();
+ if (!scripter || !m_cmd_obj_sp)
+ return;
+
+ scripter->OptionParsingStartedForCommandObject(m_cmd_obj_sp);
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ if (!m_options_definition_up)
+ return {};
+ return llvm::ArrayRef(m_options_definition_up.get(), m_num_options);
+ }
+
+ static Status ParseUsageMaskFromArray(StructuredData::ObjectSP obj_sp,
+ size_t counter, uint32_t &usage_mask) {
+ // If the usage entry is not provided, we use LLDB_OPT_SET_ALL.
+ // If the usage mask is a UINT, the option belongs to that group.
+ // If the usage mask is a vector of UINT's, the option belongs to all the
+ // groups listed.
+ // If a subelement of the vector is a vector of two ints, then the option
+ // belongs to the inclusive range from the first to the second element.
+ Status error;
+ if (!obj_sp) {
+ usage_mask = LLDB_OPT_SET_ALL;
+ return error;
+ }
+
+ usage_mask = 0;
+
+ StructuredData::UnsignedInteger *uint_val =
+ obj_sp->GetAsUnsignedInteger();
+ if (uint_val) {
+ // If this is an integer, then this specifies a single group:
+ uint32_t value = uint_val->GetValue();
+ if (value == 0) {
+ error.SetErrorStringWithFormatv(
+ "0 is not a valid group for option {0}", counter);
+ return error;
+ }
+ usage_mask = (1 << (value - 1));
+ return error;
+ }
+ // Otherwise it has to be an array:
+ StructuredData::Array *array_val = obj_sp->GetAsArray();
+ if (!array_val) {
+ error.SetErrorStringWithFormatv(
+ "required field is not a array for option {0}", counter);
+ return error;
+ }
+ // This is the array ForEach for accumulating a group usage mask from
+ // an array of string descriptions of groups.
+ auto groups_accumulator
+ = [counter, &usage_mask, &error]
+ (StructuredData::Object *obj) -> bool {
+ StructuredData::UnsignedInteger *int_val = obj->GetAsUnsignedInteger();
+ if (int_val) {
+ uint32_t value = int_val->GetValue();
+ if (value == 0) {
+ error.SetErrorStringWithFormatv(
+ "0 is not a valid group for element {0}", counter);
+ return false;
+ }
+ usage_mask |= (1 << (value - 1));
+ return true;
+ }
+ StructuredData::Array *arr_val = obj->GetAsArray();
+ if (!arr_val) {
+ error.SetErrorStringWithFormatv(
+ "Group element not an int or array of integers for element {0}",
+ counter);
+ return false;
+ }
+ size_t num_range_elem = arr_val->GetSize();
+ if (num_range_elem != 2) {
+ error.SetErrorStringWithFormatv(
+ "Subranges of a group not a start and a stop for element {0}",
+ counter);
+ return false;
+ }
+ int_val = arr_val->GetItemAtIndex(0)->GetAsUnsignedInteger();
+ if (!int_val) {
+ error.SetErrorStringWithFormatv("Start element of a subrange of a "
+ "group not unsigned int for element {0}", counter);
+ return false;
+ }
+ uint32_t start = int_val->GetValue();
+ int_val = arr_val->GetItemAtIndex(1)->GetAsUnsignedInteger();
+ if (!int_val) {
+ error.SetErrorStringWithFormatv("End element of a subrange of a group"
+ " not unsigned int for element {0}", counter);
+ return false;
+ }
+ uint32_t end = int_val->GetValue();
+ if (start == 0 || end == 0 || start > end) {
+ error.SetErrorStringWithFormatv("Invalid subrange of a group: {0} - "
+ "{1} for element {2}", start, end, counter);
+ return false;
+ }
+ for (uint32_t i = start; i <= end; i++) {
+ usage_mask |= (1 << (i - 1));
+ }
+ return true;
+ };
+ array_val->ForEach(groups_accumulator);
+ return error;
+ }
+
+
+ Status SetOptionsFromArray(StructuredData::Dictionary &options) {
+ Status error;
+ m_num_options = options.GetSize();
+ m_options_definition_up.reset(new OptionDefinition[m_num_options]);
+ // We need to hand out pointers to contents of these vectors; we reserve
+ // as much as we'll need up front so they don't get freed on resize...
+ m_usage_container.reserve(m_num_options);
+ m_enum_storage.reserve(m_num_options);
+ m_enum_vector.reserve(m_num_options);
+
+ size_t counter = 0;
+ size_t short_opt_counter = 0;
+ // This is the Array::ForEach function for adding option elements:
+ auto add_element = [this, &error, &counter, &short_opt_counter]
+ (llvm::StringRef long_option, StructuredData::Object *object) -> bool {
+ StructuredData::Dictionary *opt_dict = object->GetAsDictionary();
+ if (!opt_dict) {
+ error.SetErrorString("Value in options dictionary is not a dictionary");
+ return false;
+ }
+ OptionDefinition &option_def = m_options_definition_up.get()[counter];
+
+ // We aren't exposing the validator yet, set it to null
+ option_def.validator = nullptr;
+ // We don't require usage masks, so set it to one group by default:
+ option_def.usage_mask = 1;
+
+ // Now set the fields of the OptionDefinition Array from the dictionary:
+ //
+ // Note that I don't check for unknown fields in the option dictionaries
+ // so a scriptor can add extra elements that are helpful when they go to
+ // do "set_option_value"
+
+ // Usage Mask:
+ StructuredData::ObjectSP obj_sp = opt_dict->GetValueForKey("groups");
+ if (obj_sp) {
+ error = ParseUsageMaskFromArray(obj_sp, counter,
+ option_def.usage_mask);
+ if (error.Fail())
+ return false;
+ }
+
+ // Required:
+ option_def.required = false;
+ obj_sp = opt_dict->GetValueForKey("required");
+ if (obj_sp) {
+ StructuredData::Boolean *boolean_val = obj_sp->GetAsBoolean();
+ if (!boolean_val) {
+ error.SetErrorStringWithFormatv("'required' field is not a boolean "
+ "for option {0}", counter);
+ return false;
+ }
+ option_def.required = boolean_val->GetValue();
+ }
+
+ // Short Option:
+ int short_option;
+ obj_sp = opt_dict->GetValueForKey("short_option");
+ if (obj_sp) {
+ // The value is a string, so pull the
+ llvm::StringRef short_str = obj_sp->GetStringValue();
+ if (short_str.empty()) {
+ error.SetErrorStringWithFormatv("short_option field empty for "
+ "option {0}", counter);
+ return false;
+ } else if (short_str.size() != 1) {
+ error.SetErrorStringWithFormatv("short_option field has extra "
+ "characters for option {0}", counter);
+ return false;
+ }
+ short_option = (int) short_str[0];
+ } else {
+ // If the short option is not provided, then we need a unique value
+ // less than the lowest printable ASCII character.
+ short_option = short_opt_counter++;
+ }
+ option_def.short_option = short_option;
+
+ // Long Option is the key from the outer dict:
+ if (long_option.empty()) {
+ error.SetErrorStringWithFormatv("empty long_option for option {0}",
+ counter);
+ return false;
+ }
+ auto inserted = g_string_storer.insert(long_option.str());
+ option_def.long_option = ((*(inserted.first)).data());
+
+ // Value Type:
+ obj_sp = opt_dict->GetValueForKey("value_type");
+ if (obj_sp) {
+ StructuredData::UnsignedInteger *uint_val
+ = obj_sp->GetAsUnsignedInteger();
+ if (!uint_val) {
+ error.SetErrorStringWithFormatv("Value type must be an unsigned "
+ "integer");
+ return false;
+ }
+ uint64_t val_type = uint_val->GetValue();
+ if (val_type >= eArgTypeLastArg) {
+ error.SetErrorStringWithFormatv("Value type {0} beyond the "
+ "CommandArgumentType bounds", val_type);
+ return false;
+ }
+ option_def.argument_type = (CommandArgumentType) val_type;
+ option_def.option_has_arg = true;
+ } else {
+ option_def.argument_type = eArgTypeNone;
+ option_def.option_has_arg = false;
+ }
+
+ // Completion Type:
+ obj_sp = opt_dict->GetValueForKey("completion_type");
+ if (obj_sp) {
+ StructuredData::UnsignedInteger *uint_val = obj_sp->GetAsUnsignedInteger();
+ if (!uint_val) {
+ error.SetErrorStringWithFormatv("Completion type must be an "
+ "unsigned integer for option {0}", counter);
+ return false;
+ }
+ uint64_t completion_type = uint_val->GetValue();
+ if (completion_type > eCustomCompletion) {
+ error.SetErrorStringWithFormatv("Completion type for option {0} "
+ "beyond the CompletionType bounds", completion_type);
+ return false;
+ }
+ option_def.completion_type = (CommandArgumentType) completion_type;
+ } else
+ option_def.completion_type = eNoCompletion;
+
+ // Usage Text:
+ std::string usage_text;
+ obj_sp = opt_dict->GetValueForKey("help");
+ if (!obj_sp) {
+ error.SetErrorStringWithFormatv("required usage missing from option "
+ "{0}", counter);
+ return false;
+ }
+ llvm::StringRef usage_stref;
+ usage_stref = obj_sp->GetStringValue();
+ if (usage_stref.empty()) {
+ error.SetErrorStringWithFormatv("empty usage text for option {0}",
+ counter);
+ return false;
+ }
+ m_usage_container[counter] = usage_stref.str().c_str();
+ option_def.usage_text = m_usage_container[counter].data();
+
+ // Enum Values:
+
+ obj_sp = opt_dict->GetValueForKey("enum_values");
+ if (obj_sp) {
+ StructuredData::Array *array = obj_sp->GetAsArray();
+ if (!array) {
+ error.SetErrorStringWithFormatv("enum values must be an array for "
+ "option {0}", counter);
+ return false;
+ }
+ size_t num_elem = array->GetSize();
+ size_t enum_ctr = 0;
+ m_enum_storage[counter] = std::vector<EnumValueStorage>(num_elem);
+ std::vector<EnumValueStorage> &curr_elem = m_enum_storage[counter];
+
+ // This is the Array::ForEach function for adding enum elements:
+ // Since there are only two fields to specify the enum, use a simple
+ // two element array with value first, usage second.
+ // counter is only used for reporting so I pass it by value here.
+ auto add_enum = [&enum_ctr, &curr_elem, counter, &error]
+ (StructuredData::Object *object) -> bool {
+ StructuredData::Array *enum_arr = object->GetAsArray();
+ if (!enum_arr) {
+ error.SetErrorStringWithFormatv("Enum values for option {0} not "
+ "an array", counter);
+ return false;
+ }
+ size_t num_enum_elements = enum_arr->GetSize();
+ if (num_enum_elements != 2) {
+ error.SetErrorStringWithFormatv("Wrong number of elements: {0} "
+ "for enum {1} in option {2}",
+ num_enum_elements, enum_ctr, counter);
+ return false;
+ }
+ // Enum Value:
+ StructuredData::ObjectSP obj_sp = enum_arr->GetItemAtIndex(0);
+ llvm::StringRef val_stref = obj_sp->GetStringValue();
+ std::string value_cstr_str = val_stref.str().c_str();
+
+ // Enum Usage:
+ obj_sp = enum_arr->GetItemAtIndex(1);
+ if (!obj_sp) {
+ error.SetErrorStringWithFormatv("No usage for enum {0} in option "
+ "{1}", enum_ctr, counter);
+ return false;
+ }
+ llvm::StringRef usage_stref = obj_sp->GetStringValue();
+ std::string usage_cstr_str = usage_stref.str().c_str();
+ curr_elem[enum_ctr] = EnumValueStorage(value_cstr_str,
+ usage_cstr_str, enum_ctr);
+
+ enum_ctr++;
+ return true;
+ }; // end of add_enum
+
+ array->ForEach(add_enum);
+ if (!error.Success())
+ return false;
+ // We have to have a vector of elements to set in the options, make
+ // that here:
+ for (auto &elem : curr_elem)
+ m_enum_vector[counter].emplace_back(elem.element);
+
+ option_def.enum_values = llvm::ArrayRef(m_enum_vector[counter]);
+ }
+ counter++;
+ return true;
+ }; // end of add_element
+
+ options.ForEach(add_element);
+ return error;
+ }
+
+ private:
+ struct EnumValueStorage {
+ EnumValueStorage() {
+ element.string_value = "value not set";
+ element.usage = "usage not set";
+ element.value = 0;
+ }
+
+ EnumValueStorage(std::string in_str_val, std::string in_usage,
+ size_t in_value) : value(std::move(in_str_val)), usage(std::move(in_usage)) {
+ SetElement(in_value);
+ }
+
+ EnumValueStorage(const EnumValueStorage &in) : value(in.value),
+ usage(in.usage) {
+ SetElement(in.element.value);
+ }
+
+ EnumValueStorage &operator=(const EnumValueStorage &in) {
+ value = in.value;
+ usage = in.usage;
+ SetElement(in.element.value);
+ return *this;
+ }
+
+ void SetElement(size_t in_value) {
+ element.value = in_value;
+ element.string_value = value.data();
+ element.usage = usage.data();
+ }
+
+ std::string value;
+ std::string usage;
+ OptionEnumValueElement element;
+ };
+ // We have to provide char * values for the long option, usage and enum
+ // values, that's what the option definitions hold.
+ // The long option strings are quite likely to be reused in other added
+ // commands, so those are stored in a global set: g_string_storer.
+ // But the usages are much less likely to be reused, so those are stored in
+ // a vector in the command instance. It gets resized to the correct size
+ // and then filled with null-terminated strings in the std::string, so the
+ // are valid C-strings that won't move around.
+ // The enum values and descriptions are treated similarly - these aren't
+ // all that common so it's not worth the effort to dedup them.
+ size_t m_num_options = 0;
+ std::unique_ptr<OptionDefinition> m_options_definition_up;
+ std::vector<std::vector<EnumValueStorage>> m_enum_storage;
+ std::vector<std::vector<OptionEnumValueElement>> m_enum_vector;
+ std::vector<std::string> m_usage_container;
+ CommandInterpreter &m_interpreter;
+ StructuredData::GenericSP m_cmd_obj_sp;
+ static std::unordered_set<std::string> g_string_storer;
+ };
+
+public:
+ static CommandObjectSP Create(CommandInterpreter &interpreter,
+ std::string name,
+ StructuredData::GenericSP cmd_obj_sp,
+ ScriptedCommandSynchronicity synch,
+ CommandReturnObject &result) {
+ CommandObjectSP new_cmd_sp(new CommandObjectScriptingObjectParsed(
+ interpreter, name, cmd_obj_sp, synch));
+
+ CommandObjectScriptingObjectParsed *parsed_cmd
+ = static_cast<CommandObjectScriptingObjectParsed *>(new_cmd_sp.get());
+ // Now check all the failure modes, and report if found.
+ Status opt_error = parsed_cmd->GetOptionsError();
+ Status arg_error = parsed_cmd->GetArgsError();
+
+ if (opt_error.Fail())
+ result.AppendErrorWithFormat("failed to parse option definitions: %s",
+ opt_error.AsCString());
+ if (arg_error.Fail())
+ result.AppendErrorWithFormat("%sfailed to parse argument definitions: %s",
+ opt_error.Fail() ? ", also " : "",
+ arg_error.AsCString());
+
+ if (!result.Succeeded())
+ return {};
+
+ return new_cmd_sp;
+ }
+
+ CommandObjectScriptingObjectParsed(CommandInterpreter &interpreter,
+ std::string name,
+ StructuredData::GenericSP cmd_obj_sp,
+ ScriptedCommandSynchronicity synch)
+ : CommandObjectParsed(interpreter, name.c_str()),
+ m_cmd_obj_sp(cmd_obj_sp), m_synchro(synch),
+ m_options(interpreter, cmd_obj_sp), m_fetched_help_short(false),
+ m_fetched_help_long(false) {
+ StreamString stream;
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter) {
+ m_options_error.SetErrorString("No script interpreter");
+ return;
+ }
+
+ // Set the flags:
+ GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp));
+
+ // Now set up the options definitions from the options:
+ StructuredData::ObjectSP options_object_sp
+ = scripter->GetOptionsForCommandObject(cmd_obj_sp);
+ // It's okay not to have an options dict.
+ if (options_object_sp) {
+ // The options come as a dictionary of dictionaries. The key of the
+ // outer dict is the long option name (since that's required). The
+ // value holds all the other option specification bits.
+ StructuredData::Dictionary *options_dict
+ = options_object_sp->GetAsDictionary();
+ // but if it exists, it has to be an array.
+ if (options_dict) {
+ m_options_error = m_options.SetOptionsFromArray(*(options_dict));
+ // If we got an error don't bother with the arguments...
+ if (m_options_error.Fail())
+ return;
+ } else {
+ m_options_error.SetErrorString("Options array not an array");
+ return;
+ }
+ }
+ // Then fetch the args. Since the arguments can have usage masks you need
+ // an array of arrays.
+ StructuredData::ObjectSP args_object_sp
+ = scripter->GetArgumentsForCommandObject(cmd_obj_sp);
+ if (args_object_sp) {
+ StructuredData::Array *args_array = args_object_sp->GetAsArray();
+ if (!args_array) {
+ m_args_error.SetErrorString("Argument specification is not an array");
+ return;
+ }
+ size_t counter = 0;
+
+ // This is the Array::ForEach function that handles the
+ // CommandArgumentEntry arrays one by one:
+ auto arg_array_adder = [this, &counter] (StructuredData::Object *object)
+ -> bool {
+ // This is the Array::ForEach function to add argument entries:
+ CommandArgumentEntry this_entry;
+ size_t elem_counter = 0;
+ auto args_adder = [this, counter, &elem_counter, &this_entry]
+ (StructuredData::Object *object) -> bool {
+ // The arguments definition has three fields, the argument type, the
+ // repeat and the usage mask.
+ CommandArgumentType arg_type = eArgTypeNone;
+ ArgumentRepetitionType arg_repetition = eArgRepeatOptional;
+ uint32_t arg_opt_set_association;
+
+ auto report_error = [this, elem_counter, counter]
+ (const char *err_txt) -> bool {
+ m_args_error.SetErrorStringWithFormatv("Element {0} of arguments "
+ "list element {1}: %s.", elem_counter, counter, err_txt);
+ return false;
+ };
+
+ StructuredData::Dictionary *arg_dict = object->GetAsDictionary();
+ if (!arg_dict) {
+ report_error("is not a dictionary.");
+ return false;
+ }
+ // Argument Type:
+ StructuredData::ObjectSP obj_sp
+ = arg_dict->GetValueForKey("arg_type");
+ if (obj_sp) {
+ StructuredData::UnsignedInteger *uint_val
+ = obj_sp->GetAsUnsignedInteger();
+ if (!uint_val) {
+ report_error("value type must be an unsigned integer");
+ return false;
+ }
+ uint64_t arg_type_int = uint_val->GetValue();
+ if (arg_type_int >= eArgTypeLastArg) {
+ report_error("value type beyond ArgumentRepetitionType bounds");
+ return false;
+ }
+ arg_type = (CommandArgumentType) arg_type_int;
+ }
+ // Repeat Value:
+ obj_sp = arg_dict->GetValueForKey("repeat");
+ std::optional<ArgumentRepetitionType> repeat;
+ if (obj_sp) {
+ llvm::StringRef repeat_str = obj_sp->GetStringValue();
+ if (repeat_str.empty()) {
+ report_error("repeat value is empty");
+ return false;
+ }
+ repeat = ArgRepetitionFromString(repeat_str);
+ if (!repeat) {
+ report_error("invalid repeat value");
+ return false;
+ }
+ arg_repetition = *repeat;
+ }
+
+ // Usage Mask:
+ obj_sp = arg_dict->GetValueForKey("groups");
+ m_args_error = CommandOptions::ParseUsageMaskFromArray(obj_sp,
+ counter, arg_opt_set_association);
+ this_entry.emplace_back(arg_type, arg_repetition,
+ arg_opt_set_association);
+ elem_counter++;
+ return true;
+ };
+ StructuredData::Array *args_array = object->GetAsArray();
+ if (!args_array) {
+ m_args_error.SetErrorStringWithFormatv("Argument definition element "
+ "{0} is not an array", counter);
+ }
+
+ args_array->ForEach(args_adder);
+ if (m_args_error.Fail())
+ return false;
+ if (this_entry.empty()) {
+ m_args_error.SetErrorStringWithFormatv("Argument definition element "
+ "{0} is empty", counter);
+ return false;
+ }
+ m_arguments.push_back(this_entry);
+ counter++;
+ return true;
+ }; // end of arg_array_adder
+ // Here we actually parse the args definition:
+ args_array->ForEach(arg_array_adder);
+ }
+ }
+
+ ~CommandObjectScriptingObjectParsed() override = default;
+
+ Status GetOptionsError() { return m_options_error; }
+ Status GetArgsError() { return m_args_error; }
+ bool WantsCompletion() override { return true; }
+
+ bool IsRemovable() const override { return true; }
+
+ ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }
+
+ llvm::StringRef GetHelp() override {
+ if (m_fetched_help_short)
+ return CommandObjectParsed::GetHelp();
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectParsed::GetHelp();
+ std::string docstring;
+ m_fetched_help_short =
+ scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring);
+ if (!docstring.empty())
+ SetHelp(docstring);
+
+ return CommandObjectParsed::GetHelp();
+ }
+
+ llvm::StringRef GetHelpLong() override {
+ if (m_fetched_help_long)
+ return CommandObjectParsed::GetHelpLong();
+
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ if (!scripter)
+ return CommandObjectParsed::GetHelpLong();
+
+ std::string docstring;
+ m_fetched_help_long =
+ scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring);
+ if (!docstring.empty())
+ SetHelpLong(docstring);
+ return CommandObjectParsed::GetHelpLong();
+ }
+
+ Options *GetOptions() override { return &m_options; }
+
+
+protected:
+ void DoExecute(Args &args,
+ CommandReturnObject &result) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ Status error;
+
+ result.SetStatus(eReturnStatusInvalid);
+
+ if (!scripter ||
+ !scripter->RunScriptBasedParsedCommand(m_cmd_obj_sp, args,
+ m_synchro, result, error, m_exe_ctx)) {
+ result.AppendError(error.AsCString());
+ } else {
+ // Don't change the status if the command already set it...
+ if (result.GetStatus() == eReturnStatusInvalid) {
+ if (result.GetOutputData().empty())
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
+ }
+ }
+
+private:
+ StructuredData::GenericSP m_cmd_obj_sp;
+ ScriptedCommandSynchronicity m_synchro;
+ CommandOptions m_options;
+ Status m_options_error;
+ Status m_args_error;
+ bool m_fetched_help_short : 1;
+ bool m_fetched_help_long : 1;
+};
+
+std::unordered_set<std::string>
+ CommandObjectScriptingObjectParsed::CommandOptions::g_string_storer;
+
// CommandObjectCommandsScriptImport
#define LLDB_OPTIONS_script_import
#include "CommandOptions.inc"
@@ -1439,6 +2135,9 @@ protected:
case 'o':
m_overwrite_lazy = eLazyBoolYes;
break;
+ case 'p':
+ m_parsed_command = true;
+ break;
case 's':
m_synchronicity =
(ScriptedCommandSynchronicity)OptionArgParser::ToOptionEnum(
@@ -1474,6 +2173,7 @@ protected:
m_completion_type = eNoCompletion;
m_overwrite_lazy = eLazyBoolCalculate;
m_synchronicity = eScriptedCommandSynchronicitySynchronous;
+ m_parsed_command = false;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -1489,6 +2189,7 @@ protected:
ScriptedCommandSynchronicity m_synchronicity =
eScriptedCommandSynchronicitySynchronous;
CompletionType m_completion_type = eNoCompletion;
+ bool m_parsed_command = false;
};
void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
@@ -1628,10 +2329,16 @@ protected:
"'{0}'", m_options.m_class_name);
return;
}
-
- new_cmd_sp.reset(new CommandObjectScriptingObject(
- m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity,
- m_completion_type));
+
+ if (m_options.m_parsed_command) {
+ new_cmd_sp = CommandObjectScriptingObjectParsed::Create(m_interpreter,
+ m_cmd_name, cmd_obj_sp, m_synchronicity, result);
+ if (!result.Succeeded())
+ return;
+ } else
+ new_cmd_sp.reset(new CommandObjectScriptingObjectRaw(
+ m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity,
+ m_completion_type));
}
// Assume we're going to succeed...