| //===-- CommandObjectType.cpp -----------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CommandObjectType.h" |
| |
| // C Includes |
| // C++ Includes |
| #include <algorithm> |
| #include <cctype> |
| #include <functional> |
| |
| // Other libraries and framework includes |
| #include "llvm/ADT/StringRef.h" |
| |
| // Project includes |
| #include "lldb/Core/ConstString.h" |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/IOHandler.h" |
| #include "lldb/Core/RegularExpression.h" |
| #include "lldb/Core/State.h" |
| #include "lldb/Core/StringList.h" |
| #include "lldb/DataFormatters/DataVisualization.h" |
| #include "lldb/Interpreter/CommandInterpreter.h" |
| #include "lldb/Interpreter/CommandObject.h" |
| #include "lldb/Interpreter/CommandReturnObject.h" |
| #include "lldb/Interpreter/Options.h" |
| #include "lldb/Interpreter/OptionGroupFormat.h" |
| #include "lldb/Interpreter/OptionValueBoolean.h" |
| #include "lldb/Interpreter/OptionValueLanguage.h" |
| #include "lldb/Interpreter/OptionValueString.h" |
| #include "lldb/Symbol/Symbol.h" |
| #include "lldb/Target/Language.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/StackFrame.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| #include "lldb/Target/ThreadList.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| class ScriptAddOptions |
| { |
| public: |
| TypeSummaryImpl::Flags m_flags; |
| StringList m_target_types; |
| bool m_regex; |
| ConstString m_name; |
| std::string m_category; |
| |
| ScriptAddOptions(const TypeSummaryImpl::Flags& flags, |
| bool regx, |
| const ConstString& name, |
| std::string catg) : |
| m_flags(flags), |
| m_regex(regx), |
| m_name(name), |
| m_category(catg) |
| { |
| } |
| |
| typedef std::shared_ptr<ScriptAddOptions> SharedPointer; |
| }; |
| |
| class SynthAddOptions |
| { |
| public: |
| bool m_skip_pointers; |
| bool m_skip_references; |
| bool m_cascade; |
| bool m_regex; |
| StringList m_target_types; |
| std::string m_category; |
| |
| SynthAddOptions(bool sptr, |
| bool sref, |
| bool casc, |
| bool regx, |
| std::string catg) : |
| m_skip_pointers(sptr), |
| m_skip_references(sref), |
| m_cascade(casc), |
| m_regex(regx), |
| m_target_types(), |
| m_category(catg) |
| { |
| } |
| |
| typedef std::shared_ptr<SynthAddOptions> SharedPointer; |
| }; |
| |
| static bool |
| WarnOnPotentialUnquotedUnsignedType (Args& command, CommandReturnObject &result) |
| { |
| for (unsigned idx = 0; idx < command.GetArgumentCount(); idx++) |
| { |
| const char* arg = command.GetArgumentAtIndex(idx); |
| if (idx+1 < command.GetArgumentCount()) |
| { |
| if (arg && 0 == strcmp(arg,"unsigned")) |
| { |
| const char* next = command.GetArgumentAtIndex(idx+1); |
| if (next && |
| (0 == strcmp(next, "int") || |
| 0 == strcmp(next, "short") || |
| 0 == strcmp(next, "char") || |
| 0 == strcmp(next, "long"))) |
| { |
| result.AppendWarningWithFormat("%s %s being treated as two types. if you meant the combined type name use quotes, as in \"%s %s\"\n", |
| arg,next,arg,next); |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| class CommandObjectTypeSummaryAdd : |
| public CommandObjectParsed, |
| public IOHandlerDelegateMultiline |
| { |
| private: |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override; |
| |
| void |
| OptionParsingStarting () override; |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| TypeSummaryImpl::Flags m_flags; |
| bool m_regex; |
| std::string m_format_string; |
| ConstString m_name; |
| std::string m_python_script; |
| std::string m_python_function; |
| bool m_is_add_script; |
| std::string m_category; |
| }; |
| |
| CommandOptions m_options; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| bool |
| Execute_ScriptSummary (Args& command, CommandReturnObject &result); |
| |
| bool |
| Execute_StringSummary (Args& command, CommandReturnObject &result); |
| |
| public: |
| enum SummaryFormatType |
| { |
| eRegularSummary, |
| eRegexSummary, |
| eNamedSummary |
| }; |
| |
| CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter); |
| |
| ~CommandObjectTypeSummaryAdd() override = default; |
| |
| void |
| IOHandlerActivated (IOHandler &io_handler) override |
| { |
| static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" |
| "def function (valobj,internal_dict):\n" |
| " \"\"\"valobj: an SBValue which you want to provide a summary for\n" |
| " internal_dict: an LLDB support object not to be used\"\"\"\n"; |
| |
| StreamFileSP output_sp(io_handler.GetOutputStreamFile()); |
| if (output_sp) |
| { |
| output_sp->PutCString(g_summary_addreader_instructions); |
| output_sp->Flush(); |
| } |
| } |
| |
| void |
| IOHandlerInputComplete (IOHandler &io_handler, std::string &data) override |
| { |
| StreamFileSP error_sp = io_handler.GetErrorStreamFile(); |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); |
| if (interpreter) |
| { |
| StringList lines; |
| lines.SplitIntoLines(data); |
| if (lines.GetSize() > 0) |
| { |
| ScriptAddOptions *options_ptr = ((ScriptAddOptions*)io_handler.GetUserData()); |
| if (options_ptr) |
| { |
| ScriptAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope |
| |
| ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); |
| if (interpreter) |
| { |
| std::string funct_name_str; |
| if (interpreter->GenerateTypeScriptFunction (lines, funct_name_str)) |
| { |
| if (funct_name_str.empty()) |
| { |
| error_sp->Printf ("unable to obtain a valid function name from the script interpreter.\n"); |
| error_sp->Flush(); |
| } |
| else |
| { |
| // now I have a valid function name, let's add this as script for every type in the list |
| |
| TypeSummaryImplSP script_format; |
| script_format.reset(new ScriptSummaryFormat(options->m_flags, |
| funct_name_str.c_str(), |
| lines.CopyList(" ").c_str())); |
| |
| Error error; |
| |
| for (size_t i = 0; i < options->m_target_types.GetSize(); i++) |
| { |
| const char *type_name = options->m_target_types.GetStringAtIndex(i); |
| CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), |
| script_format, |
| (options->m_regex ? CommandObjectTypeSummaryAdd::eRegexSummary : CommandObjectTypeSummaryAdd::eRegularSummary), |
| options->m_category, |
| &error); |
| if (error.Fail()) |
| { |
| error_sp->Printf ("error: %s", error.AsCString()); |
| error_sp->Flush(); |
| } |
| } |
| |
| if (options->m_name) |
| { |
| CommandObjectTypeSummaryAdd::AddSummary (options->m_name, |
| script_format, |
| CommandObjectTypeSummaryAdd::eNamedSummary, |
| options->m_category, |
| &error); |
| if (error.Fail()) |
| { |
| CommandObjectTypeSummaryAdd::AddSummary (options->m_name, |
| script_format, |
| CommandObjectTypeSummaryAdd::eNamedSummary, |
| options->m_category, |
| &error); |
| if (error.Fail()) |
| { |
| error_sp->Printf ("error: %s", error.AsCString()); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: %s", error.AsCString()); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| if (error.AsCString()) |
| { |
| error_sp->Printf ("error: %s", error.AsCString()); |
| error_sp->Flush(); |
| } |
| } |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: unable to generate a function.\n"); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: no script interpreter.\n"); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: internal synchronization information missing or invalid.\n"); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| 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_sp->Flush(); |
| } |
| #endif // LLDB_DISABLE_PYTHON |
| io_handler.SetIsDone(true); |
| } |
| |
| static bool |
| AddSummary(ConstString type_name, |
| lldb::TypeSummaryImplSP entry, |
| SummaryFormatType type, |
| std::string category, |
| Error* error = nullptr); |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override; |
| }; |
| |
| static const char *g_synth_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" |
| "You must define a Python class with these methods:\n" |
| " def __init__(self, valobj, dict):\n" |
| " def num_children(self):\n" |
| " def get_child_at_index(self, index):\n" |
| " def get_child_index(self, name):\n" |
| " def update(self):\n" |
| " '''Optional'''\n" |
| "class synthProvider:\n"; |
| |
| class CommandObjectTypeSynthAdd : |
| public CommandObjectParsed, |
| public IOHandlerDelegateMultiline |
| { |
| private: |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| bool success; |
| |
| switch (short_option) |
| { |
| case 'C': |
| m_cascade = Args::StringToBoolean(option_arg, true, &success); |
| if (!success) |
| error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); |
| break; |
| case 'P': |
| handwrite_python = true; |
| break; |
| case 'l': |
| m_class_name = std::string(option_arg); |
| is_class_based = true; |
| break; |
| case 'p': |
| m_skip_pointers = true; |
| break; |
| case 'r': |
| m_skip_references = true; |
| break; |
| case 'w': |
| m_category = std::string(option_arg); |
| break; |
| case 'x': |
| m_regex = true; |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_cascade = true; |
| m_class_name = ""; |
| m_skip_pointers = false; |
| m_skip_references = false; |
| m_category = "default"; |
| is_class_based = false; |
| handwrite_python = false; |
| m_regex = false; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| bool m_cascade; |
| bool m_skip_references; |
| bool m_skip_pointers; |
| std::string m_class_name; |
| bool m_input_python; |
| std::string m_category; |
| bool is_class_based; |
| bool handwrite_python; |
| bool m_regex; |
| }; |
| |
| CommandOptions m_options; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| bool |
| Execute_HandwritePython (Args& command, CommandReturnObject &result); |
| |
| bool |
| Execute_PythonClass (Args& command, CommandReturnObject &result); |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| WarnOnPotentialUnquotedUnsignedType(command, result); |
| |
| if (m_options.handwrite_python) |
| return Execute_HandwritePython(command, result); |
| else if (m_options.is_class_based) |
| return Execute_PythonClass(command, result); |
| else |
| { |
| result.AppendError("must either provide a children list, a Python class name, or use -P and type a Python class line-by-line"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| void |
| IOHandlerActivated (IOHandler &io_handler) override |
| { |
| StreamFileSP output_sp(io_handler.GetOutputStreamFile()); |
| if (output_sp) |
| { |
| output_sp->PutCString(g_synth_addreader_instructions); |
| output_sp->Flush(); |
| } |
| } |
| |
| void |
| IOHandlerInputComplete (IOHandler &io_handler, std::string &data) override |
| { |
| StreamFileSP error_sp = io_handler.GetErrorStreamFile(); |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); |
| if (interpreter) |
| { |
| StringList lines; |
| lines.SplitIntoLines(data); |
| if (lines.GetSize() > 0) |
| { |
| SynthAddOptions *options_ptr = ((SynthAddOptions*)io_handler.GetUserData()); |
| if (options_ptr) |
| { |
| SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope |
| |
| ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); |
| if (interpreter) |
| { |
| std::string class_name_str; |
| if (interpreter->GenerateTypeSynthClass (lines, class_name_str)) |
| { |
| if (class_name_str.empty()) |
| { |
| error_sp->Printf ("error: unable to obtain a proper name for the class.\n"); |
| error_sp->Flush(); |
| } |
| else |
| { |
| // everything should be fine now, let's add the synth provider class |
| |
| SyntheticChildrenSP synth_provider; |
| synth_provider.reset(new ScriptedSyntheticChildren(SyntheticChildren::Flags().SetCascades(options->m_cascade). |
| SetSkipPointers(options->m_skip_pointers). |
| SetSkipReferences(options->m_skip_references), |
| class_name_str.c_str())); |
| |
| |
| lldb::TypeCategoryImplSP category; |
| DataVisualization::Categories::GetCategory(ConstString(options->m_category.c_str()), category); |
| |
| Error error; |
| |
| for (size_t i = 0; i < options->m_target_types.GetSize(); i++) |
| { |
| const char *type_name = options->m_target_types.GetStringAtIndex(i); |
| ConstString const_type_name(type_name); |
| if (const_type_name) |
| { |
| if (!CommandObjectTypeSynthAdd::AddSynth(const_type_name, |
| synth_provider, |
| options->m_regex ? CommandObjectTypeSynthAdd::eRegexSynth : CommandObjectTypeSynthAdd::eRegularSynth, |
| options->m_category, |
| &error)) |
| { |
| error_sp->Printf("error: %s\n", error.AsCString()); |
| error_sp->Flush(); |
| break; |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: invalid type name.\n"); |
| error_sp->Flush(); |
| break; |
| } |
| } |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: unable to generate a class.\n"); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: no script interpreter.\n"); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: internal synchronization data missing.\n"); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| 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_sp->Flush(); |
| } |
| |
| #endif // LLDB_DISABLE_PYTHON |
| io_handler.SetIsDone(true); |
| } |
| |
| public: |
| enum SynthFormatType |
| { |
| eRegularSynth, |
| eRegexSynth |
| }; |
| |
| CommandObjectTypeSynthAdd (CommandInterpreter &interpreter); |
| |
| ~CommandObjectTypeSynthAdd() override = default; |
| |
| static bool |
| AddSynth(ConstString type_name, |
| lldb::SyntheticChildrenSP entry, |
| SynthFormatType type, |
| std::string category_name, |
| Error* error); |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeFormatAdd |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeFormatAdd : public CommandObjectParsed |
| { |
| private: |
| class CommandOptions : public OptionGroup |
| { |
| public: |
| CommandOptions () : |
| OptionGroup() |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| uint32_t |
| GetNumDefinitions () override; |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| void |
| OptionParsingStarting (CommandInterpreter &interpreter) override |
| { |
| m_cascade = true; |
| m_skip_pointers = false; |
| m_skip_references = false; |
| m_regex = false; |
| m_category.assign("default"); |
| m_custom_type_name.clear(); |
| } |
| |
| Error |
| SetOptionValue (CommandInterpreter &interpreter, |
| uint32_t option_idx, |
| const char *option_value) override |
| { |
| Error error; |
| const int short_option = g_option_table[option_idx].short_option; |
| bool success; |
| |
| switch (short_option) |
| { |
| case 'C': |
| m_cascade = Args::StringToBoolean(option_value, true, &success); |
| if (!success) |
| error.SetErrorStringWithFormat("invalid value for cascade: %s", option_value); |
| break; |
| case 'p': |
| m_skip_pointers = true; |
| break; |
| case 'w': |
| m_category.assign(option_value); |
| break; |
| case 'r': |
| m_skip_references = true; |
| break; |
| case 'x': |
| m_regex = true; |
| break; |
| case 't': |
| m_custom_type_name.assign(option_value); |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| bool m_cascade; |
| bool m_skip_references; |
| bool m_skip_pointers; |
| bool m_regex; |
| std::string m_category; |
| std::string m_custom_type_name; |
| }; |
| |
| OptionGroupOptions m_option_group; |
| OptionGroupFormat m_format_options; |
| CommandOptions m_command_options; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_option_group; |
| } |
| |
| public: |
| CommandObjectTypeFormatAdd (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type format add", |
| "Add a new formatting style for a type.", |
| nullptr), |
| m_option_group (interpreter), |
| m_format_options (eFormatInvalid), |
| m_command_options () |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlus; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| |
| SetHelpLong( |
| R"( |
| The following examples of 'type format add' refer to this code snippet for context: |
| |
| typedef int Aint; |
| typedef float Afloat; |
| typedef Aint Bint; |
| typedef Afloat Bfloat; |
| |
| Aint ix = 5; |
| Bint iy = 5; |
| |
| Afloat fx = 3.14; |
| BFloat fy = 3.14; |
| |
| Adding default formatting: |
| |
| (lldb) type format add -f hex AInt |
| (lldb) frame variable iy |
| |
| )" " Produces hexidecimal display of iy, because no formatter is available for Bint and \ |
| the one for Aint is used instead." R"( |
| |
| To prevent this use the cascade option '-C no' to prevent evaluation of typedef chains: |
| |
| |
| (lldb) type format add -f hex -C no AInt |
| |
| Similar reasoning applies to this: |
| |
| (lldb) type format add -f hex -C no float -p |
| |
| )" " All float values and float references are now formatted as hexadecimal, but not \ |
| pointers to floats. Nor will it change the default display for Afloat and Bfloat objects." |
| ); |
| |
| // Add the "--format" to all options groups |
| m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_1); |
| m_option_group.Append (&m_command_options); |
| m_option_group.Finalize(); |
| } |
| |
| ~CommandObjectTypeFormatAdd() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1) |
| { |
| result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| const Format format = m_format_options.GetFormat(); |
| if (format == eFormatInvalid && m_command_options.m_custom_type_name.empty()) |
| { |
| result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| TypeFormatImplSP entry; |
| |
| if (m_command_options.m_custom_type_name.empty()) |
| entry.reset(new TypeFormatImpl_Format(format, |
| TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). |
| SetSkipPointers(m_command_options.m_skip_pointers). |
| SetSkipReferences(m_command_options.m_skip_references))); |
| else |
| entry.reset(new TypeFormatImpl_EnumType(ConstString(m_command_options.m_custom_type_name.c_str()), |
| TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). |
| SetSkipPointers(m_command_options.m_skip_pointers). |
| SetSkipReferences(m_command_options.m_skip_references))); |
| |
| // now I have a valid format, let's add it to every type |
| |
| TypeCategoryImplSP category_sp; |
| DataVisualization::Categories::GetCategory(ConstString(m_command_options.m_category), category_sp); |
| if (!category_sp) |
| return false; |
| |
| WarnOnPotentialUnquotedUnsignedType(command, result); |
| |
| for (size_t i = 0; i < argc; i++) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| ConstString typeCS(typeA); |
| if (typeCS) |
| { |
| if (m_command_options.m_regex) |
| { |
| RegularExpressionSP typeRX(new RegularExpression()); |
| if (!typeRX->Compile(typeCS.GetCString())) |
| { |
| result.AppendError("regex format error (maybe this is not really a regex?)"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| category_sp->GetRegexTypeSummariesContainer()->Delete(typeCS); |
| category_sp->GetRegexTypeFormatsContainer()->Add(typeRX, entry); |
| } |
| else |
| category_sp->GetTypeFormatsContainer()->Add(typeCS, entry); |
| } |
| else |
| { |
| result.AppendError("empty typenames not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| return result.Succeeded(); |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "Add this to the given category instead of the default one."}, |
| { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, |
| { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, |
| { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, |
| { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Type names are actually regular expressions."}, |
| { LLDB_OPT_SET_2, false, "type", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "Format variables as if they were of this type."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| uint32_t |
| CommandObjectTypeFormatAdd::CommandOptions::GetNumDefinitions () |
| { |
| return sizeof(g_option_table) / sizeof (OptionDefinition); |
| } |
| |
| class CommandObjectTypeFormatterDelete : public CommandObjectParsed |
| { |
| protected: |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'a': |
| m_delete_all = true; |
| break; |
| case 'w': |
| m_category = std::string(option_arg); |
| break; |
| case 'l': |
| m_language = Language::GetLanguageTypeFromString(option_arg); |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_delete_all = false; |
| m_category = "default"; |
| m_language = lldb::eLanguageTypeUnknown; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| bool m_delete_all; |
| std::string m_category; |
| lldb::LanguageType m_language; |
| }; |
| |
| CommandOptions m_options; |
| uint32_t m_formatter_kind_mask; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| public: |
| CommandObjectTypeFormatterDelete (CommandInterpreter &interpreter, |
| uint32_t formatter_kind_mask, |
| const char* name, |
| const char* help) : |
| CommandObjectParsed(interpreter, |
| name, |
| help, |
| nullptr), |
| m_options(interpreter), |
| m_formatter_kind_mask(formatter_kind_mask) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlain; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| } |
| |
| ~CommandObjectTypeFormatterDelete() override = default; |
| |
| protected: |
| virtual bool |
| FormatterSpecificDeletion (ConstString typeCS) |
| { |
| return false; |
| } |
| |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc != 1) |
| { |
| result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| const char* typeA = command.GetArgumentAtIndex(0); |
| ConstString typeCS(typeA); |
| |
| if (!typeCS) |
| { |
| result.AppendError("empty typenames not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| if (m_options.m_delete_all) |
| { |
| DataVisualization::Categories::ForEach( [this, typeCS] (const lldb::TypeCategoryImplSP& category_sp) -> bool { |
| category_sp->Delete(typeCS, m_formatter_kind_mask); |
| return true; |
| }); |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| return result.Succeeded(); |
| } |
| |
| bool delete_category = false; |
| bool extra_deletion = false; |
| |
| if (m_options.m_language != lldb::eLanguageTypeUnknown) |
| { |
| lldb::TypeCategoryImplSP category; |
| DataVisualization::Categories::GetCategory(m_options.m_language, category); |
| if (category) |
| delete_category = category->Delete(typeCS, m_formatter_kind_mask); |
| extra_deletion = FormatterSpecificDeletion(typeCS); |
| } |
| else |
| { |
| lldb::TypeCategoryImplSP category; |
| DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); |
| if (category) |
| delete_category = category->Delete(typeCS, m_formatter_kind_mask); |
| extra_deletion = FormatterSpecificDeletion(typeCS); |
| } |
| |
| if (delete_category || extra_deletion) |
| { |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| return result.Succeeded(); |
| } |
| else |
| { |
| result.AppendErrorWithFormat ("no custom formatter for %s.\n", typeA); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectTypeFormatterDelete::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_1, false, "all", 'a', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Delete from every category."}, |
| { LLDB_OPT_SET_2, false, "category", 'w', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "Delete from given category."}, |
| { LLDB_OPT_SET_3, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Delete from given language's category."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| class CommandObjectTypeFormatterClear : public CommandObjectParsed |
| { |
| private: |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'a': |
| m_delete_all = true; |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_delete_all = false; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| bool m_delete_all; |
| }; |
| |
| CommandOptions m_options; |
| uint32_t m_formatter_kind_mask; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| public: |
| CommandObjectTypeFormatterClear (CommandInterpreter &interpreter, |
| uint32_t formatter_kind_mask, |
| const char* name, |
| const char* help) : |
| CommandObjectParsed(interpreter, |
| name, |
| help, |
| nullptr), |
| m_options(interpreter), |
| m_formatter_kind_mask(formatter_kind_mask) |
| { |
| } |
| |
| ~CommandObjectTypeFormatterClear() override = default; |
| |
| protected: |
| virtual void |
| FormatterSpecificDeletion () |
| { |
| } |
| |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| if (m_options.m_delete_all) |
| { |
| DataVisualization::Categories::ForEach( [this] (const TypeCategoryImplSP& category_sp) -> bool { |
| category_sp->Clear(m_formatter_kind_mask); |
| return true; |
| }); |
| } |
| else |
| { |
| lldb::TypeCategoryImplSP category; |
| if (command.GetArgumentCount() > 0) |
| { |
| const char* cat_name = command.GetArgumentAtIndex(0); |
| ConstString cat_nameCS(cat_name); |
| DataVisualization::Categories::GetCategory(cat_nameCS, category); |
| } |
| else |
| { |
| DataVisualization::Categories::GetCategory(ConstString(nullptr), category); |
| } |
| category->Clear(m_formatter_kind_mask); |
| } |
| |
| FormatterSpecificDeletion(); |
| |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| return result.Succeeded(); |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectTypeFormatterClear::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "all", 'a', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Clear every category."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeFormatDelete |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeFormatDelete : public CommandObjectTypeFormatterDelete |
| { |
| public: |
| CommandObjectTypeFormatDelete (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterDelete (interpreter, |
| eFormatCategoryItemValue | eFormatCategoryItemRegexValue, |
| "type format delete", |
| "Delete an existing formatting style for a type.") |
| { |
| } |
| |
| ~CommandObjectTypeFormatDelete() override = default; |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeFormatClear |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeFormatClear : public CommandObjectTypeFormatterClear |
| { |
| public: |
| CommandObjectTypeFormatClear (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterClear (interpreter, |
| eFormatCategoryItemValue | eFormatCategoryItemRegexValue, |
| "type format clear", |
| "Delete all existing format styles.") |
| { |
| } |
| }; |
| |
| template <typename FormatterType> |
| class CommandObjectTypeFormatterList : public CommandObjectParsed |
| { |
| typedef typename FormatterType::SharedPointer FormatterSharedPointer; |
| |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter), |
| m_category_regex("",""), |
| m_category_language(lldb::eLanguageTypeUnknown, lldb::eLanguageTypeUnknown) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'w': |
| m_category_regex.SetCurrentValue(option_arg); |
| m_category_regex.SetOptionWasSet(); |
| break; |
| case 'l': |
| error = m_category_language.SetValueFromString(option_arg); |
| if (error.Success()) |
| m_category_language.SetOptionWasSet(); |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_category_regex.Clear(); |
| m_category_language.Clear(); |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| static OptionDefinition g_option_table[] = |
| { |
| { LLDB_OPT_SET_1, false, "category-regex", 'w', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "Only show categories matching this filter."}, |
| { LLDB_OPT_SET_2, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Only show the category for a specific language."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| OptionValueString m_category_regex; |
| OptionValueLanguage m_category_language; |
| }; |
| |
| CommandOptions m_options; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| public: |
| CommandObjectTypeFormatterList (CommandInterpreter &interpreter, |
| const char* name, |
| const char* help) : |
| CommandObjectParsed(interpreter, |
| name, |
| help, |
| nullptr), |
| m_options(interpreter) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatOptional; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| } |
| |
| ~CommandObjectTypeFormatterList() override = default; |
| |
| protected: |
| virtual bool |
| FormatterSpecificList (CommandReturnObject &result) |
| { |
| return false; |
| } |
| |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| std::unique_ptr<RegularExpression> category_regex; |
| std::unique_ptr<RegularExpression> formatter_regex; |
| |
| if (m_options.m_category_regex.OptionWasSet()) |
| { |
| category_regex.reset(new RegularExpression()); |
| if (!category_regex->Compile(m_options.m_category_regex.GetCurrentValue())) |
| { |
| result.AppendErrorWithFormat("syntax error in category regular expression '%s'", m_options.m_category_regex.GetCurrentValue()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| if (argc == 1) |
| { |
| const char* arg = command.GetArgumentAtIndex(0); |
| formatter_regex.reset(new RegularExpression()); |
| if (!formatter_regex->Compile(arg)) |
| { |
| result.AppendErrorWithFormat("syntax error in regular expression '%s'", arg); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| bool any_printed = false; |
| |
| auto category_closure = [&result, &formatter_regex, &any_printed] (const lldb::TypeCategoryImplSP& category) -> void { |
| result.GetOutputStream().Printf("-----------------------\nCategory: %s%s\n-----------------------\n", |
| category->GetName(), |
| category->IsEnabled() ? "" : " (disabled)"); |
| |
| TypeCategoryImpl::ForEachCallbacks<FormatterType> foreach; |
| foreach.SetExact([&result, &formatter_regex, &any_printed] (ConstString name, const FormatterSharedPointer& format_sp) -> bool { |
| if (formatter_regex) |
| { |
| bool escape = true; |
| if (0 == strcmp(name.AsCString(), formatter_regex->GetText())) |
| { |
| escape = false; |
| } |
| else if (formatter_regex->Execute(name.AsCString())) |
| { |
| escape = false; |
| } |
| |
| if (escape) |
| return true; |
| } |
| |
| any_printed = true; |
| result.GetOutputStream().Printf ("%s: %s\n", name.AsCString(), format_sp->GetDescription().c_str()); |
| return true; |
| }); |
| |
| foreach.SetWithRegex([&result, &formatter_regex, &any_printed] (RegularExpressionSP regex_sp, const FormatterSharedPointer& format_sp) -> bool { |
| if (formatter_regex) |
| { |
| bool escape = true; |
| if (0 == strcmp(regex_sp->GetText(), formatter_regex->GetText())) |
| { |
| escape = false; |
| } |
| else if (formatter_regex->Execute(regex_sp->GetText())) |
| { |
| escape = false; |
| } |
| |
| if (escape) |
| return true; |
| } |
| |
| any_printed = true; |
| result.GetOutputStream().Printf ("%s: %s\n", regex_sp->GetText(), format_sp->GetDescription().c_str()); |
| return true; |
| }); |
| |
| category->ForEach(foreach); |
| }; |
| |
| if (m_options.m_category_language.OptionWasSet()) |
| { |
| lldb::TypeCategoryImplSP category_sp; |
| DataVisualization::Categories::GetCategory(m_options.m_category_language.GetCurrentValue(), category_sp); |
| if (category_sp) |
| category_closure(category_sp); |
| } |
| else |
| { |
| DataVisualization::Categories::ForEach( [this, &command, &result, &category_regex, &formatter_regex, &category_closure] (const lldb::TypeCategoryImplSP& category) -> bool { |
| if (category_regex) |
| { |
| bool escape = true; |
| if (0 == strcmp(category->GetName(), category_regex->GetText())) |
| { |
| escape = false; |
| } |
| else if (category_regex->Execute(category->GetName())) |
| { |
| escape = false; |
| } |
| |
| if (escape) |
| return true; |
| } |
| |
| category_closure(category); |
| |
| return true; |
| }); |
| |
| any_printed = FormatterSpecificList(result) | any_printed; |
| } |
| |
| if (any_printed) |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| else |
| { |
| result.GetOutputStream().PutCString("no matching results found.\n"); |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| } |
| return result.Succeeded(); |
| } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeFormatList |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeFormatList : public CommandObjectTypeFormatterList<TypeFormatImpl> |
| { |
| public: |
| |
| CommandObjectTypeFormatList (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterList(interpreter, |
| "type format list", |
| "Show a list of current formats.") |
| { |
| } |
| }; |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeSummaryAdd |
| //------------------------------------------------------------------------- |
| |
| #endif // LLDB_DISABLE_PYTHON |
| |
| Error |
| CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| bool success; |
| |
| switch (short_option) |
| { |
| case 'C': |
| m_flags.SetCascades(Args::StringToBoolean(option_arg, true, &success)); |
| if (!success) |
| error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); |
| break; |
| case 'e': |
| m_flags.SetDontShowChildren(false); |
| break; |
| case 'h': |
| m_flags.SetHideEmptyAggregates(true); |
| break; |
| case 'v': |
| m_flags.SetDontShowValue(true); |
| break; |
| case 'c': |
| m_flags.SetShowMembersOneLiner(true); |
| break; |
| case 's': |
| m_format_string = std::string(option_arg); |
| break; |
| case 'p': |
| m_flags.SetSkipPointers(true); |
| break; |
| case 'r': |
| m_flags.SetSkipReferences(true); |
| break; |
| case 'x': |
| m_regex = true; |
| break; |
| case 'n': |
| m_name.SetCString(option_arg); |
| break; |
| case 'o': |
| m_python_script = std::string(option_arg); |
| m_is_add_script = true; |
| break; |
| case 'F': |
| m_python_function = std::string(option_arg); |
| m_is_add_script = true; |
| break; |
| case 'P': |
| m_is_add_script = true; |
| break; |
| case 'w': |
| m_category = std::string(option_arg); |
| break; |
| case 'O': |
| m_flags.SetHideItemNames(true); |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting () |
| { |
| m_flags.Clear().SetCascades().SetDontShowChildren().SetDontShowValue(false); |
| m_flags.SetShowMembersOneLiner(false).SetSkipPointers(false).SetSkipReferences(false).SetHideItemNames(false); |
| |
| m_regex = false; |
| m_name.Clear(); |
| m_python_script = ""; |
| m_python_function = ""; |
| m_format_string = ""; |
| m_is_add_script = false; |
| m_category = "default"; |
| } |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| |
| bool |
| CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturnObject &result) |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1 && !m_options.m_name) |
| { |
| result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| TypeSummaryImplSP script_format; |
| |
| if (!m_options.m_python_function.empty()) // we have a Python function ready to use |
| { |
| const char *funct_name = m_options.m_python_function.c_str(); |
| if (!funct_name || !funct_name[0]) |
| { |
| result.AppendError ("function name empty.\n"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| std::string code = (" " + m_options.m_python_function + "(valobj,internal_dict)"); |
| |
| script_format.reset(new ScriptSummaryFormat(m_options.m_flags, |
| funct_name, |
| code.c_str())); |
| |
| ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); |
| |
| if (interpreter && !interpreter->CheckObjectExists(funct_name)) |
| result.AppendWarningWithFormat("The provided function \"%s\" does not exist - " |
| "please define it before attempting to use this summary.\n", |
| funct_name); |
| } |
| else if (!m_options.m_python_script.empty()) // we have a quick 1-line script, just use it |
| { |
| ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); |
| if (!interpreter) |
| { |
| result.AppendError ("script interpreter missing - unable to generate function wrapper.\n"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| StringList funct_sl; |
| funct_sl << m_options.m_python_script.c_str(); |
| std::string funct_name_str; |
| if (!interpreter->GenerateTypeScriptFunction (funct_sl, |
| funct_name_str)) |
| { |
| result.AppendError ("unable to generate function wrapper.\n"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| if (funct_name_str.empty()) |
| { |
| result.AppendError ("script interpreter failed to generate a valid function name.\n"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| std::string code = " " + m_options.m_python_script; |
| |
| script_format.reset(new ScriptSummaryFormat(m_options.m_flags, |
| funct_name_str.c_str(), |
| code.c_str())); |
| } |
| else |
| { |
| // Use an IOHandler to grab Python code from the user |
| ScriptAddOptions *options = new ScriptAddOptions(m_options.m_flags, |
| m_options.m_regex, |
| m_options.m_name, |
| m_options.m_category); |
| |
| for (size_t i = 0; i < argc; i++) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| if (typeA && *typeA) |
| options->m_target_types << typeA; |
| else |
| { |
| result.AppendError("empty typenames not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt |
| *this, // IOHandlerDelegate |
| true, // Run IOHandler in async mode |
| options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| |
| return result.Succeeded(); |
| } |
| |
| // if I am here, script_format must point to something good, so I can add that |
| // as a script summary to all interested parties |
| |
| Error error; |
| |
| for (size_t i = 0; i < command.GetArgumentCount(); i++) |
| { |
| const char *type_name = command.GetArgumentAtIndex(i); |
| CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), |
| script_format, |
| (m_options.m_regex ? eRegexSummary : eRegularSummary), |
| m_options.m_category, |
| &error); |
| if (error.Fail()) |
| { |
| result.AppendError(error.AsCString()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| if (m_options.m_name) |
| { |
| AddSummary(m_options.m_name, script_format, eNamedSummary, m_options.m_category, &error); |
| if (error.Fail()) |
| { |
| result.AppendError(error.AsCString()); |
| result.AppendError("added to types, but not given a name"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| return result.Succeeded(); |
| } |
| |
| #endif // LLDB_DISABLE_PYTHON |
| |
| bool |
| CommandObjectTypeSummaryAdd::Execute_StringSummary (Args& command, CommandReturnObject &result) |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1 && !m_options.m_name) |
| { |
| result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| if (!m_options.m_flags.GetShowMembersOneLiner() && m_options.m_format_string.empty()) |
| { |
| result.AppendError("empty summary strings not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| const char* format_cstr = (m_options.m_flags.GetShowMembersOneLiner() ? "" : m_options.m_format_string.c_str()); |
| |
| // ${var%S} is an endless recursion, prevent it |
| if (strcmp(format_cstr, "${var%S}") == 0) |
| { |
| result.AppendError("recursive summary not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| std::unique_ptr<StringSummaryFormat> string_format(new StringSummaryFormat(m_options.m_flags, format_cstr)); |
| if (!string_format) |
| { |
| result.AppendError("summary creation failed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| if (string_format->m_error.Fail()) |
| { |
| result.AppendErrorWithFormat("syntax error: %s", string_format->m_error.AsCString("<unknown>")); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| lldb::TypeSummaryImplSP entry(string_format.release()); |
| |
| // now I have a valid format, let's add it to every type |
| Error error; |
| for (size_t i = 0; i < argc; i++) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| if (!typeA || typeA[0] == '\0') |
| { |
| result.AppendError("empty typenames not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| ConstString typeCS(typeA); |
| |
| AddSummary(typeCS, |
| entry, |
| (m_options.m_regex ? eRegexSummary : eRegularSummary), |
| m_options.m_category, |
| &error); |
| |
| if (error.Fail()) |
| { |
| result.AppendError(error.AsCString()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| if (m_options.m_name) |
| { |
| AddSummary(m_options.m_name, entry, eNamedSummary, m_options.m_category, &error); |
| if (error.Fail()) |
| { |
| result.AppendError(error.AsCString()); |
| result.AppendError("added to types, but not given a name"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| return result.Succeeded(); |
| } |
| |
| CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type summary add", |
| "Add a new summary style for a type.", |
| nullptr), |
| IOHandlerDelegateMultiline ("DONE"), |
| m_options (interpreter) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlus; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| |
| SetHelpLong( |
| R"( |
| The following examples of 'type summary add' refer to this code snippet for context: |
| |
| struct JustADemo |
| { |
| int* ptr; |
| float value; |
| JustADemo(int p = 1, float v = 0.1) : ptr(new int(p)), value(v) {} |
| }; |
| JustADemo demo_instance(42, 3.14); |
| |
| typedef JustADemo NewDemo; |
| NewDemo new_demo_instance(42, 3.14); |
| |
| (lldb) type summary add --summary-string "the answer is ${*var.ptr}" JustADemo |
| |
| Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42" |
| |
| (lldb) type summary add --summary-string "the answer is ${*var.ptr}, and the question is ${var.value}" JustADemo |
| |
| Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42 and the question is 3.14" |
| |
| )" "Alternatively, you could define formatting for all pointers to integers and \ |
| rely on that when formatting JustADemo to obtain the same result:" R"( |
| |
| (lldb) type summary add --summary-string "${var%V} -> ${*var}" "int *" |
| (lldb) type summary add --summary-string "the answer is ${var.ptr}, and the question is ${var.value}" JustADemo |
| |
| )" "Type summaries are automatically applied to derived typedefs, so the examples \ |
| above apply to both JustADemo and NewDemo. The cascade option can be used to \ |
| suppress this behavior:" R"( |
| |
| (lldb) type summary add --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo -C no |
| |
| The summary will now be used for values of JustADemo but not NewDemo. |
| |
| )" "By default summaries are shown for pointers and references to values of the \ |
| specified type. To suppress formatting for pointers use the -p option, or apply \ |
| the corresponding -r option to suppress formatting for references:" R"( |
| |
| (lldb) type summary add -p -r --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo |
| |
| )" "One-line summaries including all fields in a type can be inferred without supplying an \ |
| explicit summary string by passing the -c option:" R"( |
| |
| (lldb) type summary add -c JustADemo |
| (lldb) frame variable demo_instance |
| (ptr=<address>, value=3.14) |
| |
| )" "Type summaries normally suppress the nested display of individual fields. To \ |
| supply a summary to supplement the default structure add the -e option:" R"( |
| |
| (lldb) type summary add -e --summary-string "*ptr = ${*var.ptr}" JustADemo |
| |
| )" "Now when displaying JustADemo values the int* is displayed, followed by the \ |
| standard LLDB sequence of children, one per line:" R"( |
| |
| *ptr = 42 { |
| ptr = <address> |
| value = 3.14 |
| } |
| |
| )" "You can also add summaries written in Python. These scripts use lldb public API to \ |
| gather information from your variables and produce a meaningful summary. To start a \ |
| multi-line script use the -P option. The function declaration will be displayed along with \ |
| a comment describing the two arguments. End your script with the word 'DONE' on a line by \ |
| itself:" R"( |
| |
| (lldb) type summary add JustADemo -P |
| def function (valobj,internal_dict): |
| """valobj: an SBValue which you want to provide a summary for |
| internal_dict: an LLDB support object not to be used""" |
| value = valobj.GetChildMemberWithName('value'); |
| return 'My value is ' + value.GetValue(); |
| DONE |
| |
| Alternatively, the -o option can be used when providing a simple one-line Python script: |
| |
| (lldb) type summary add JustADemo -o "value = valobj.GetChildMemberWithName('value'); return 'My value is ' + value.GetValue();")" |
| ); |
| } |
| |
| bool |
| CommandObjectTypeSummaryAdd::DoExecute (Args& command, CommandReturnObject &result) |
| { |
| WarnOnPotentialUnquotedUnsignedType(command, result); |
| |
| if (m_options.m_is_add_script) |
| { |
| #ifndef LLDB_DISABLE_PYTHON |
| return Execute_ScriptSummary(command, result); |
| #else |
| result.AppendError ("python is disabled"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| #endif // LLDB_DISABLE_PYTHON |
| } |
| |
| return Execute_StringSummary(command, result); |
| } |
| |
| static bool |
| FixArrayTypeNameWithRegex (ConstString &type_name) |
| { |
| llvm::StringRef type_name_ref(type_name.GetStringRef()); |
| |
| if (type_name_ref.endswith("[]")) |
| { |
| std::string type_name_str(type_name.GetCString()); |
| type_name_str.resize(type_name_str.length()-2); |
| if (type_name_str.back() != ' ') |
| type_name_str.append(" \\[[0-9]+\\]"); |
| else |
| type_name_str.append("\\[[0-9]+\\]"); |
| type_name.SetCString(type_name_str.c_str()); |
| return true; |
| } |
| return false; |
| } |
| |
| bool |
| CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, |
| TypeSummaryImplSP entry, |
| SummaryFormatType type, |
| std::string category_name, |
| Error* error) |
| { |
| lldb::TypeCategoryImplSP category; |
| DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); |
| |
| if (type == eRegularSummary) |
| { |
| if (FixArrayTypeNameWithRegex (type_name)) |
| type = eRegexSummary; |
| } |
| |
| if (type == eRegexSummary) |
| { |
| RegularExpressionSP typeRX(new RegularExpression()); |
| if (!typeRX->Compile(type_name.GetCString())) |
| { |
| if (error) |
| error->SetErrorString("regex format error (maybe this is not really a regex?)"); |
| return false; |
| } |
| |
| category->GetRegexTypeSummariesContainer()->Delete(type_name); |
| category->GetRegexTypeSummariesContainer()->Add(typeRX, entry); |
| |
| return true; |
| } |
| else if (type == eNamedSummary) |
| { |
| // system named summaries do not exist (yet?) |
| DataVisualization::NamedSummaryFormats::Add(type_name,entry); |
| return true; |
| } |
| else |
| { |
| category->GetTypeSummariesContainer()->Add(type_name, entry); |
| return true; |
| } |
| } |
| |
| OptionDefinition |
| CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "Add this to the given category instead of the default one."}, |
| { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, |
| { LLDB_OPT_SET_ALL, false, "no-value", 'v', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't show the value, just show the summary, for this type."}, |
| { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, |
| { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, |
| { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Type names are actually regular expressions."}, |
| { LLDB_OPT_SET_1 , true, "inline-children", 'c', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "If true, inline all child values into summary string."}, |
| { LLDB_OPT_SET_1 , false, "omit-names", 'O', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "If true, omit value names in the summary display."}, |
| { LLDB_OPT_SET_2 , true, "summary-string", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeSummaryString, "Summary string used to display text and object contents."}, |
| { LLDB_OPT_SET_3, false, "python-script", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonScript, "Give a one-liner Python script as part of the command."}, |
| { LLDB_OPT_SET_3, false, "python-function", 'F', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonFunction, "Give the name of a Python function to use for this type."}, |
| { LLDB_OPT_SET_3, false, "input-python", 'P', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Input Python code to use for this type manually."}, |
| { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "expand", 'e', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Expand aggregate data types to show children on separate lines."}, |
| { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "hide-empty", 'h', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Do not expand aggregate data types with no children."}, |
| { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "A name for this summary string."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeSummaryDelete |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeSummaryDelete : public CommandObjectTypeFormatterDelete |
| { |
| public: |
| CommandObjectTypeSummaryDelete (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterDelete (interpreter, |
| eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary, |
| "type summary delete", |
| "Delete an existing summary for a type.") |
| { |
| } |
| |
| ~CommandObjectTypeSummaryDelete() override = default; |
| |
| protected: |
| bool |
| FormatterSpecificDeletion (ConstString typeCS) override |
| { |
| if (m_options.m_language != lldb::eLanguageTypeUnknown) |
| return false; |
| return DataVisualization::NamedSummaryFormats::Delete(typeCS); |
| } |
| }; |
| |
| class CommandObjectTypeSummaryClear : public CommandObjectTypeFormatterClear |
| { |
| public: |
| CommandObjectTypeSummaryClear (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterClear (interpreter, |
| eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary, |
| "type summary clear", |
| "Delete all existing summaries.") |
| { |
| } |
| |
| protected: |
| void |
| FormatterSpecificDeletion () override |
| { |
| DataVisualization::NamedSummaryFormats::Clear(); |
| } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeSummaryList |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeSummaryList : public CommandObjectTypeFormatterList<TypeSummaryImpl> |
| { |
| public: |
| CommandObjectTypeSummaryList (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterList(interpreter, |
| "type summary list", |
| "Show a list of current summaries.") |
| { |
| } |
| |
| protected: |
| bool |
| FormatterSpecificList (CommandReturnObject &result) override |
| { |
| if (DataVisualization::NamedSummaryFormats::GetCount() > 0) |
| { |
| result.GetOutputStream().Printf("Named summaries:\n"); |
| DataVisualization::NamedSummaryFormats::ForEach( [&result] (ConstString name, const TypeSummaryImplSP& summary_sp) -> bool { |
| result.GetOutputStream().Printf ("%s: %s\n", name.AsCString(), summary_sp->GetDescription().c_str()); |
| return true; |
| }); |
| return true; |
| } |
| return false; |
| } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeCategoryDefine |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeCategoryDefine : public CommandObjectParsed |
| { |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter), |
| m_define_enabled(false,false), |
| m_cate_language(eLanguageTypeUnknown,eLanguageTypeUnknown) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'e': |
| m_define_enabled.SetValueFromString("true"); |
| break; |
| case 'l': |
| error = m_cate_language.SetValueFromString(option_arg); |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_define_enabled.Clear(); |
| m_cate_language.Clear(); |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| OptionValueBoolean m_define_enabled; |
| OptionValueLanguage m_cate_language; |
| }; |
| |
| CommandOptions m_options; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| public: |
| CommandObjectTypeCategoryDefine (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type category define", |
| "Define a new category as a source of formatters.", |
| nullptr), |
| m_options(interpreter) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlus; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| } |
| |
| ~CommandObjectTypeCategoryDefine() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1) |
| { |
| result.AppendErrorWithFormat ("%s takes 1 or more args.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| for (size_t i = 0; i < argc; i++) |
| { |
| const char* cateName = command.GetArgumentAtIndex(i); |
| TypeCategoryImplSP category_sp; |
| if (DataVisualization::Categories::GetCategory(ConstString(cateName), category_sp) && category_sp) |
| { |
| category_sp->AddLanguage(m_options.m_cate_language.GetCurrentValue()); |
| if (m_options.m_define_enabled.GetCurrentValue()) |
| DataVisualization::Categories::Enable(category_sp, TypeCategoryMap::Default); |
| } |
| } |
| |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| return result.Succeeded(); |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectTypeCategoryDefine::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "enabled", 'e', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "If specified, this category will be created enabled."}, |
| { LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Specify the language that this category is supported for."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeCategoryEnable |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeCategoryEnable : public CommandObjectParsed |
| { |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'l': |
| if (option_arg) |
| { |
| m_language = Language::GetLanguageTypeFromString(option_arg); |
| if (m_language == lldb::eLanguageTypeUnknown) |
| error.SetErrorStringWithFormat ("unrecognized language '%s'", option_arg); |
| } |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_language = lldb::eLanguageTypeUnknown; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| lldb::LanguageType m_language; |
| |
| }; |
| |
| CommandOptions m_options; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| public: |
| CommandObjectTypeCategoryEnable (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type category enable", |
| "Enable a category as a source of formatters.", |
| nullptr), |
| m_options(interpreter) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlus; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| |
| } |
| |
| ~CommandObjectTypeCategoryEnable() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1 && |
| m_options.m_language == lldb::eLanguageTypeUnknown) |
| { |
| result.AppendErrorWithFormat ("%s takes arguments and/or a language", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) |
| { |
| DataVisualization::Categories::EnableStar(); |
| } |
| else if (argc > 0) |
| { |
| for (int i = argc - 1; i >= 0; i--) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| ConstString typeCS(typeA); |
| |
| if (!typeCS) |
| { |
| result.AppendError("empty category name not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| DataVisualization::Categories::Enable(typeCS); |
| lldb::TypeCategoryImplSP cate; |
| if (DataVisualization::Categories::GetCategory(typeCS, cate) && cate) |
| { |
| if (cate->GetCount() == 0) |
| { |
| result.AppendWarning("empty category enabled (typo?)"); |
| } |
| } |
| } |
| } |
| |
| if (m_options.m_language != lldb::eLanguageTypeUnknown) |
| DataVisualization::Categories::Enable(m_options.m_language); |
| |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| return result.Succeeded(); |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectTypeCategoryEnable::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Enable the category for this language."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeCategoryDelete |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeCategoryDelete : public CommandObjectParsed |
| { |
| public: |
| CommandObjectTypeCategoryDelete (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type category delete", |
| "Delete a category and all associated formatters.", |
| nullptr) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlus; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| } |
| |
| ~CommandObjectTypeCategoryDelete() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1) |
| { |
| result.AppendErrorWithFormat ("%s takes 1 or more arg.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| bool success = true; |
| |
| // the order is not relevant here |
| for (int i = argc - 1; i >= 0; i--) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| ConstString typeCS(typeA); |
| |
| if (!typeCS) |
| { |
| result.AppendError("empty category name not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| if (!DataVisualization::Categories::Delete(typeCS)) |
| success = false; // keep deleting even if we hit an error |
| } |
| if (success) |
| { |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| return result.Succeeded(); |
| } |
| else |
| { |
| result.AppendError("cannot delete one or more categories\n"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeCategoryDisable |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeCategoryDisable : public CommandObjectParsed |
| { |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'l': |
| if (option_arg) |
| { |
| m_language = Language::GetLanguageTypeFromString(option_arg); |
| if (m_language == lldb::eLanguageTypeUnknown) |
| error.SetErrorStringWithFormat ("unrecognized language '%s'", option_arg); |
| } |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_language = lldb::eLanguageTypeUnknown; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| lldb::LanguageType m_language; |
| }; |
| |
| CommandOptions m_options; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| public: |
| CommandObjectTypeCategoryDisable (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type category disable", |
| "Disable a category as a source of formatters.", |
| nullptr), |
| m_options(interpreter) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlus; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| } |
| |
| ~CommandObjectTypeCategoryDisable() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1 && |
| m_options.m_language == lldb::eLanguageTypeUnknown) |
| { |
| result.AppendErrorWithFormat ("%s takes arguments and/or a language", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) |
| { |
| DataVisualization::Categories::DisableStar(); |
| } |
| else if (argc > 0) |
| { |
| // the order is not relevant here |
| for (int i = argc - 1; i >= 0; i--) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| ConstString typeCS(typeA); |
| |
| if (!typeCS) |
| { |
| result.AppendError("empty category name not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| DataVisualization::Categories::Disable(typeCS); |
| } |
| } |
| |
| if (m_options.m_language != lldb::eLanguageTypeUnknown) |
| DataVisualization::Categories::Disable(m_options.m_language); |
| |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| return result.Succeeded(); |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectTypeCategoryDisable::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Enable the category for this language."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeCategoryList |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeCategoryList : public CommandObjectParsed |
| { |
| public: |
| CommandObjectTypeCategoryList (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type category list", |
| "Provide a list of all existing categories.", |
| nullptr) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatOptional; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| } |
| |
| ~CommandObjectTypeCategoryList() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| std::unique_ptr<RegularExpression> regex; |
| |
| if (argc == 1) |
| { |
| regex.reset(new RegularExpression()); |
| const char* arg = command.GetArgumentAtIndex(0); |
| if (!regex->Compile(arg)) |
| { |
| result.AppendErrorWithFormat("syntax error in category regular expression '%s'", arg); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| else if (argc != 0) |
| { |
| result.AppendErrorWithFormat ("%s takes 0 or one arg.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| DataVisualization::Categories::ForEach( [®ex, &result] (const lldb::TypeCategoryImplSP& category_sp) -> bool { |
| if (regex) |
| { |
| bool escape = true; |
| if (0 == strcmp(category_sp->GetName(), regex->GetText())) |
| { |
| escape = false; |
| } |
| else if (regex->Execute(category_sp->GetName())) |
| { |
| escape = false; |
| } |
| |
| if (escape) |
| return true; |
| } |
| |
| result.GetOutputStream().Printf("Category: %s\n", category_sp->GetDescription().c_str()); |
| |
| return true; |
| }); |
| |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| return result.Succeeded(); |
| } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeFilterList |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeFilterList : public CommandObjectTypeFormatterList<TypeFilterImpl> |
| { |
| public: |
| CommandObjectTypeFilterList (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterList(interpreter, |
| "type filter list", |
| "Show a list of current filters.") |
| { |
| } |
| }; |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeSynthList |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeSynthList : public CommandObjectTypeFormatterList<SyntheticChildren> |
| { |
| public: |
| CommandObjectTypeSynthList (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterList(interpreter, |
| "type synthetic list", |
| "Show a list of current synthetic providers.") |
| { |
| } |
| }; |
| |
| #endif // LLDB_DISABLE_PYTHON |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeFilterDelete |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeFilterDelete : public CommandObjectTypeFormatterDelete |
| { |
| public: |
| CommandObjectTypeFilterDelete (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterDelete (interpreter, |
| eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter, |
| "type filter delete", |
| "Delete an existing filter for a type.") |
| { |
| } |
| |
| ~CommandObjectTypeFilterDelete() override = default; |
| }; |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeSynthDelete |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeSynthDelete : public CommandObjectTypeFormatterDelete |
| { |
| public: |
| CommandObjectTypeSynthDelete (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterDelete (interpreter, |
| eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth, |
| "type synthetic delete", |
| "Delete an existing synthetic provider for a type.") |
| { |
| } |
| |
| ~CommandObjectTypeSynthDelete() override = default; |
| }; |
| |
| #endif // LLDB_DISABLE_PYTHON |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeFilterClear |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeFilterClear : public CommandObjectTypeFormatterClear |
| { |
| public: |
| CommandObjectTypeFilterClear (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterClear (interpreter, |
| eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter, |
| "type filter clear", |
| "Delete all existing filter.") |
| { |
| } |
| }; |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| //------------------------------------------------------------------------- |
| // CommandObjectTypeSynthClear |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectTypeSynthClear : public CommandObjectTypeFormatterClear |
| { |
| public: |
| CommandObjectTypeSynthClear (CommandInterpreter &interpreter) : |
| CommandObjectTypeFormatterClear (interpreter, |
| eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth, |
| "type synthetic clear", |
| "Delete all existing synthetic providers.") |
| { |
| } |
| }; |
| |
| bool |
| CommandObjectTypeSynthAdd::Execute_HandwritePython (Args& command, CommandReturnObject &result) |
| { |
| SynthAddOptions *options = new SynthAddOptions ( m_options.m_skip_pointers, |
| m_options.m_skip_references, |
| m_options.m_cascade, |
| m_options.m_regex, |
| m_options.m_category); |
| |
| const size_t argc = command.GetArgumentCount(); |
| |
| for (size_t i = 0; i < argc; i++) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| if (typeA && *typeA) |
| options->m_target_types << typeA; |
| else |
| { |
| result.AppendError("empty typenames not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt |
| *this, // IOHandlerDelegate |
| true, // Run IOHandler in async mode |
| options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| return result.Succeeded(); |
| } |
| |
| bool |
| CommandObjectTypeSynthAdd::Execute_PythonClass (Args& command, CommandReturnObject &result) |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1) |
| { |
| result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| if (m_options.m_class_name.empty() && !m_options.m_input_python) |
| { |
| result.AppendErrorWithFormat ("%s needs either a Python class name or -P to directly input Python code.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| SyntheticChildrenSP entry; |
| |
| ScriptedSyntheticChildren* impl = new ScriptedSyntheticChildren(SyntheticChildren::Flags(). |
| SetCascades(m_options.m_cascade). |
| SetSkipPointers(m_options.m_skip_pointers). |
| SetSkipReferences(m_options.m_skip_references), |
| m_options.m_class_name.c_str()); |
| |
| entry.reset(impl); |
| |
| ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); |
| |
| if (interpreter && !interpreter->CheckObjectExists(impl->GetPythonClassName())) |
| result.AppendWarning("The provided class does not exist - please define it before attempting to use this synthetic provider"); |
| |
| // now I have a valid provider, let's add it to every type |
| |
| lldb::TypeCategoryImplSP category; |
| DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); |
| |
| Error error; |
| |
| for (size_t i = 0; i < argc; i++) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| ConstString typeCS(typeA); |
| if (typeCS) |
| { |
| if (!AddSynth(typeCS, |
| entry, |
| m_options.m_regex ? eRegexSynth : eRegularSynth, |
| m_options.m_category, |
| &error)) |
| { |
| result.AppendError(error.AsCString()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| else |
| { |
| result.AppendError("empty typenames not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| return result.Succeeded(); |
| } |
| |
| CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type synthetic add", |
| "Add a new synthetic provider for a type.", |
| nullptr), |
| IOHandlerDelegateMultiline ("DONE"), |
| m_options (interpreter) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlus; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| } |
| |
| bool |
| CommandObjectTypeSynthAdd::AddSynth(ConstString type_name, |
| SyntheticChildrenSP entry, |
| SynthFormatType type, |
| std::string category_name, |
| Error* error) |
| { |
| lldb::TypeCategoryImplSP category; |
| DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); |
| |
| if (type == eRegularSynth) |
| { |
| if (FixArrayTypeNameWithRegex (type_name)) |
| type = eRegexSynth; |
| } |
| |
| if (category->AnyMatches(type_name, |
| eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter, |
| false)) |
| { |
| if (error) |
| error->SetErrorStringWithFormat("cannot add synthetic for type %s when filter is defined in same category!", type_name.AsCString()); |
| return false; |
| } |
| |
| if (type == eRegexSynth) |
| { |
| RegularExpressionSP typeRX(new RegularExpression()); |
| if (!typeRX->Compile(type_name.GetCString())) |
| { |
| if (error) |
| error->SetErrorString("regex format error (maybe this is not really a regex?)"); |
| return false; |
| } |
| |
| category->GetRegexTypeSyntheticsContainer()->Delete(type_name); |
| category->GetRegexTypeSyntheticsContainer()->Add(typeRX, entry); |
| |
| return true; |
| } |
| else |
| { |
| category->GetTypeSyntheticsContainer()->Add(type_name, entry); |
| return true; |
| } |
| } |
| |
| OptionDefinition |
| CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, |
| { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, |
| { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, |
| { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "Add this to the given category instead of the default one."}, |
| { LLDB_OPT_SET_2, false, "python-class", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonClass, "Use this Python class to produce synthetic children."}, |
| { LLDB_OPT_SET_3, false, "input-python", 'P', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Type Python code to generate a class that provides synthetic children."}, |
| { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Type names are actually regular expressions."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| #endif // LLDB_DISABLE_PYTHON |
| |
| class CommandObjectTypeFilterAdd : public CommandObjectParsed |
| { |
| private: |
| class CommandOptions : public Options |
| { |
| typedef std::vector<std::string> option_vector; |
| |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| bool success; |
| |
| switch (short_option) |
| { |
| case 'C': |
| m_cascade = Args::StringToBoolean(option_arg, true, &success); |
| if (!success) |
| error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); |
| break; |
| case 'c': |
| m_expr_paths.push_back(option_arg); |
| has_child_list = true; |
| break; |
| case 'p': |
| m_skip_pointers = true; |
| break; |
| case 'r': |
| m_skip_references = true; |
| break; |
| case 'w': |
| m_category = std::string(option_arg); |
| break; |
| case 'x': |
| m_regex = true; |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_cascade = true; |
| m_skip_pointers = false; |
| m_skip_references = false; |
| m_category = "default"; |
| m_expr_paths.clear(); |
| has_child_list = false; |
| m_regex = false; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| bool m_cascade; |
| bool m_skip_references; |
| bool m_skip_pointers; |
| bool m_input_python; |
| option_vector m_expr_paths; |
| std::string m_category; |
| bool has_child_list; |
| bool m_regex; |
| |
| typedef option_vector::iterator ExpressionPathsIterator; |
| }; |
| |
| CommandOptions m_options; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| enum FilterFormatType |
| { |
| eRegularFilter, |
| eRegexFilter |
| }; |
| |
| bool |
| AddFilter(ConstString type_name, |
| TypeFilterImplSP entry, |
| FilterFormatType type, |
| std::string category_name, |
| Error* error) |
| { |
| lldb::TypeCategoryImplSP category; |
| DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); |
| |
| if (type == eRegularFilter) |
| { |
| if (FixArrayTypeNameWithRegex (type_name)) |
| type = eRegexFilter; |
| } |
| |
| if (category->AnyMatches(type_name, |
| eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth, |
| false)) |
| { |
| if (error) |
| error->SetErrorStringWithFormat("cannot add filter for type %s when synthetic is defined in same category!", type_name.AsCString()); |
| return false; |
| } |
| |
| if (type == eRegexFilter) |
| { |
| RegularExpressionSP typeRX(new RegularExpression()); |
| if (!typeRX->Compile(type_name.GetCString())) |
| { |
| if (error) |
| error->SetErrorString("regex format error (maybe this is not really a regex?)"); |
| return false; |
| } |
| |
| category->GetRegexTypeFiltersContainer()->Delete(type_name); |
| category->GetRegexTypeFiltersContainer()->Add(typeRX, entry); |
| |
| return true; |
| } |
| else |
| { |
| category->GetTypeFiltersContainer()->Add(type_name, entry); |
| return true; |
| } |
| } |
| |
| public: |
| CommandObjectTypeFilterAdd (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "type filter add", |
| "Add a new filter for a type.", |
| nullptr), |
| m_options (interpreter) |
| { |
| CommandArgumentEntry type_arg; |
| CommandArgumentData type_style_arg; |
| |
| type_style_arg.arg_type = eArgTypeName; |
| type_style_arg.arg_repetition = eArgRepeatPlus; |
| |
| type_arg.push_back (type_style_arg); |
| |
| m_arguments.push_back (type_arg); |
| |
| SetHelpLong( |
| R"( |
| The following examples of 'type filter add' refer to this code snippet for context: |
| |
| class Foo { |
| int a; |
| int b; |
| int c; |
| int d; |
| int e; |
| int f; |
| int g; |
| int h; |
| int i; |
| } |
| Foo my_foo; |
| |
| Adding a simple filter: |
| |
| (lldb) type filter add --child a --child g Foo |
| (lldb) frame variable my_foo |
| |
| )" "Produces output where only a and g are displayed. Other children of my_foo \ |
| (b, c, d, e, f, h and i) are available by asking for them explicitly:" R"( |
| |
| (lldb) frame variable my_foo.b my_foo.c my_foo.i |
| |
| )" "The formatting option --raw on frame variable bypasses the filter, showing \ |
| all children of my_foo as if no filter was defined:" R"( |
| |
| (lldb) frame variable my_foo --raw)" |
| ); |
| } |
| |
| ~CommandObjectTypeFilterAdd() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| |
| if (argc < 1) |
| { |
| result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| if (m_options.m_expr_paths.empty()) |
| { |
| result.AppendErrorWithFormat ("%s needs one or more children.\n", m_cmd_name.c_str()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| TypeFilterImplSP entry(new TypeFilterImpl(SyntheticChildren::Flags().SetCascades(m_options.m_cascade). |
| SetSkipPointers(m_options.m_skip_pointers). |
| SetSkipReferences(m_options.m_skip_references))); |
| |
| // go through the expression paths |
| CommandOptions::ExpressionPathsIterator begin, end = m_options.m_expr_paths.end(); |
| |
| for (begin = m_options.m_expr_paths.begin(); begin != end; begin++) |
| entry->AddExpressionPath(*begin); |
| |
| |
| // now I have a valid provider, let's add it to every type |
| |
| lldb::TypeCategoryImplSP category; |
| DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); |
| |
| Error error; |
| |
| WarnOnPotentialUnquotedUnsignedType(command, result); |
| |
| for (size_t i = 0; i < argc; i++) |
| { |
| const char* typeA = command.GetArgumentAtIndex(i); |
| ConstString typeCS(typeA); |
| if (typeCS) |
| { |
| if (!AddFilter(typeCS, |
| entry, |
| m_options.m_regex ? eRegexFilter : eRegularFilter, |
| m_options.m_category, |
| &error)) |
| { |
| result.AppendError(error.AsCString()); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| else |
| { |
| result.AppendError("empty typenames not allowed"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| return result.Succeeded(); |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectTypeFilterAdd::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, |
| { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, |
| { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, |
| { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "Add this to the given category instead of the default one."}, |
| { LLDB_OPT_SET_ALL, false, "child", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeExpressionPath, "Include this expression path in the synthetic view."}, |
| { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Type names are actually regular expressions."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //---------------------------------------------------------------------- |
| // "type lookup" |
| //---------------------------------------------------------------------- |
| class CommandObjectTypeLookup : public CommandObjectRaw |
| { |
| protected: |
| // this function is allowed to do a more aggressive job at guessing languages than the expression parser |
| // is comfortable with - so leave the original call alone and add one that is specific to type lookup |
| lldb::LanguageType |
| GuessLanguage (StackFrame *frame) |
| { |
| lldb::LanguageType lang_type = lldb::eLanguageTypeUnknown; |
| |
| if (!frame) |
| return lang_type; |
| |
| lang_type = frame->GuessLanguage(); |
| if (lang_type != lldb::eLanguageTypeUnknown) |
| return lang_type; |
| |
| Symbol *s = frame->GetSymbolContext(eSymbolContextSymbol).symbol; |
| if (s) |
| lang_type = s->GetMangled().GuessLanguage(); |
| |
| return lang_type; |
| } |
| |
| class CommandOptions : public OptionGroup |
| { |
| public: |
| CommandOptions () : |
| OptionGroup(), |
| m_show_help(false), |
| m_language(eLanguageTypeUnknown) |
| {} |
| |
| ~CommandOptions() override = default; |
| |
| uint32_t |
| GetNumDefinitions () override |
| { |
| return 3; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| Error |
| SetOptionValue (CommandInterpreter &interpreter, |
| uint32_t option_idx, |
| const char *option_value) override |
| { |
| Error error; |
| |
| const int short_option = g_option_table[option_idx].short_option; |
| |
| switch (short_option) |
| { |
| case 'h': |
| m_show_help = true; |
| break; |
| |
| case 'l': |
| m_language = Language::GetLanguageTypeFromString(option_value); |
| break; |
| |
| default: |
| error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting (CommandInterpreter &interpreter) override |
| { |
| m_show_help = false; |
| m_language = eLanguageTypeUnknown; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| bool m_show_help; |
| lldb::LanguageType m_language; |
| }; |
| |
| OptionGroupOptions m_option_group; |
| CommandOptions m_command_options; |
| |
| public: |
| CommandObjectTypeLookup (CommandInterpreter &interpreter) : |
| CommandObjectRaw (interpreter, |
| "type lookup", |
| "Lookup types and declarations in the current target, following language-specific naming conventions.", |
| "type lookup <type-specifier>", |
| eCommandRequiresTarget), |
| m_option_group(interpreter), |
| m_command_options() |
| { |
| m_option_group.Append(&m_command_options); |
| m_option_group.Finalize(); |
| } |
| |
| ~CommandObjectTypeLookup() override = default; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_option_group; |
| } |
| |
| const char* |
| GetHelpLong () override |
| { |
| if (m_cmd_help_long.empty()) |
| { |
| StreamString stream; |
| // FIXME: hardcoding languages is not good |
| lldb::LanguageType languages[] = {eLanguageTypeObjC,eLanguageTypeC_plus_plus}; |
| |
| for(const auto lang_type : languages) |
| { |
| if (auto language = Language::FindPlugin(lang_type)) |
| { |
| if (const char* help = language->GetLanguageSpecificTypeLookupHelp()) |
| { |
| stream.Printf("%s\n", help); |
| } |
| } |
| } |
| |
| if (stream.GetData()) |
| m_cmd_help_long.assign(stream.GetString()); |
| } |
| return this->CommandObject::GetHelpLong(); |
| } |
| |
| bool |
| DoExecute (const char *raw_command_line, CommandReturnObject &result) override |
| { |
| if (!raw_command_line || !raw_command_line[0]) |
| { |
| result.SetError("type lookup cannot be invoked without a type name as argument"); |
| return false; |
| } |
| |
| m_option_group.NotifyOptionParsingStarting(); |
| |
| const char * name_of_type = nullptr; |
| |
| if (raw_command_line[0] == '-') |
| { |
| // We have some options and these options MUST end with --. |
| const char *end_options = nullptr; |
| const char *s = raw_command_line; |
| while (s && s[0]) |
| { |
| end_options = ::strstr (s, "--"); |
| if (end_options) |
| { |
| end_options += 2; // Get past the "--" |
| if (::isspace (end_options[0])) |
| { |
| name_of_type = end_options; |
| while (::isspace (*name_of_type)) |
| ++name_of_type; |
| break; |
| } |
| } |
| s = end_options; |
| } |
| |
| if (end_options) |
| { |
| Args args (llvm::StringRef(raw_command_line, end_options - raw_command_line)); |
| if (!ParseOptions (args, result)) |
| return false; |
| |
| Error error (m_option_group.NotifyOptionParsingFinished()); |
| if (error.Fail()) |
| { |
| result.AppendError (error.AsCString()); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| } |
| } |
| if (nullptr == name_of_type) |
| name_of_type = raw_command_line; |
| |
| TargetSP target_sp(GetCommandInterpreter().GetDebugger().GetSelectedTarget()); |
| const bool fill_all_in = true; |
| ExecutionContext exe_ctx(target_sp.get(), fill_all_in); |
| ExecutionContextScope *best_scope = exe_ctx.GetBestExecutionContextScope(); |
| |
| bool any_found = false; |
| |
| std::vector<Language*> languages; |
| |
| bool is_global_search = false; |
| |
| if ( (is_global_search = (m_command_options.m_language == eLanguageTypeUnknown)) ) |
| { |
| // FIXME: hardcoding languages is not good |
| languages.push_back(Language::FindPlugin(eLanguageTypeObjC)); |
| languages.push_back(Language::FindPlugin(eLanguageTypeC_plus_plus)); |
| } |
| else |
| { |
| languages.push_back(Language::FindPlugin(m_command_options.m_language)); |
| } |
| |
| // This is not the most efficient way to do this, but we support very few languages |
| // so the cost of the sort is going to be dwarfed by the actual lookup anyway |
| if (StackFrame* frame = m_exe_ctx.GetFramePtr()) |
| { |
| LanguageType lang = GuessLanguage(frame); |
| if (lang != eLanguageTypeUnknown) |
| { |
| std::sort(languages.begin(), |
| languages.end(), |
| [lang] (Language* lang1, |
| Language* lang2) -> bool { |
| if (!lang1 || !lang2) return false; |
| LanguageType lt1 = lang1->GetLanguageType(); |
| LanguageType lt2 = lang2->GetLanguageType(); |
| if (lt1 == lang) return true; // make the selected frame's language come first |
| if (lt2 == lang) return false; // make the selected frame's language come first |
| return (lt1 < lt2); // normal comparison otherwise |
| }); |
| } |
| } |
| |
| for (Language* language : languages) |
| { |
| if (!language) |
| continue; |
| |
| if (auto scavenger = language->GetTypeScavenger()) |
| { |
| Language::TypeScavenger::ResultSet search_results; |
| if (scavenger->Find(best_scope, name_of_type, search_results) > 0) |
| { |
| for (const auto& search_result : search_results) |
| { |
| if (search_result && search_result->IsValid()) |
| { |
| any_found = true; |
| search_result->DumpToStream(result.GetOutputStream(), this->m_command_options.m_show_help); |
| } |
| } |
| } |
| // this is "type lookup SomeName" and we did find a match, so get out |
| if (any_found && is_global_search) |
| break; |
| } |
| } |
| |
| if (!any_found) |
| result.AppendMessageWithFormat("no type was found matching '%s'\n", name_of_type); |
| |
| result.SetStatus (any_found ? lldb::eReturnStatusSuccessFinishResult : lldb::eReturnStatusSuccessFinishNoResult); |
| return true; |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectTypeLookup::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "show-help", 'h', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display available help for types"}, |
| { LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Which language's types should the search scope be"}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| template <typename FormatterType> |
| class CommandObjectFormatterInfo : public CommandObjectRaw |
| { |
| public: |
| typedef std::function<typename FormatterType::SharedPointer(ValueObject&)> DiscoveryFunction; |
| CommandObjectFormatterInfo (CommandInterpreter &interpreter, |
| const char* formatter_name, |
| DiscoveryFunction discovery_func) : |
| CommandObjectRaw(interpreter, |
| nullptr, |
| nullptr, |
| nullptr, |
| eCommandRequiresFrame), |
| m_formatter_name(formatter_name ? formatter_name : ""), |
| m_discovery_function(discovery_func) |
| { |
| StreamString name; |
| name.Printf("type %s info", formatter_name); |
| SetCommandName(name.GetData()); |
| StreamString help; |
| help.Printf("This command evaluates the provided expression and shows which %s is applied to the resulting value (if any).", formatter_name); |
| SetHelp(help.GetData()); |
| StreamString syntax; |
| syntax.Printf("type %s info <expr>", formatter_name); |
| SetSyntax(syntax.GetData()); |
| } |
| |
| ~CommandObjectFormatterInfo() override = default; |
| |
| protected: |
| bool |
| DoExecute (const char *command, CommandReturnObject &result) override |
| { |
| TargetSP target_sp = m_interpreter.GetDebugger().GetSelectedTarget(); |
| Thread *thread = GetDefaultThread(); |
| if (!thread) |
| { |
| result.AppendError("no default thread"); |
| result.SetStatus(lldb::eReturnStatusFailed); |
| return false; |
| } |
| |
| StackFrameSP frame_sp = thread->GetSelectedFrame(); |
| ValueObjectSP result_valobj_sp; |
| EvaluateExpressionOptions options; |
| lldb::ExpressionResults expr_result = target_sp->EvaluateExpression(command, frame_sp.get(), result_valobj_sp, options); |
| if (expr_result == eExpressionCompleted && result_valobj_sp) |
| { |
| result_valobj_sp = result_valobj_sp->GetQualifiedRepresentationIfAvailable(target_sp->GetPreferDynamicValue(), target_sp->GetEnableSyntheticValue()); |
| typename FormatterType::SharedPointer formatter_sp = m_discovery_function(*result_valobj_sp); |
| if (formatter_sp) |
| { |
| std::string description(formatter_sp->GetDescription()); |
| result.AppendMessageWithFormat("%s applied to (%s) %s is: %s\n", |
| m_formatter_name.c_str(), |
| result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>"), |
| command, |
| description.c_str()); |
| result.SetStatus(lldb::eReturnStatusSuccessFinishResult); |
| } |
| else |
| { |
| result.AppendMessageWithFormat("no %s applies to (%s) %s\n", |
| m_formatter_name.c_str(), |
| result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>"), |
| command); |
| result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); |
| } |
| return true; |
| } |
| else |
| { |
| result.AppendError("failed to evaluate expression"); |
| result.SetStatus(lldb::eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| private: |
| std::string m_formatter_name; |
| DiscoveryFunction m_discovery_function; |
| }; |
| |
| class CommandObjectTypeFormat : public CommandObjectMultiword |
| { |
| public: |
| CommandObjectTypeFormat(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "type format", "Commands for customizing value display formats.", |
| "type format [<sub-command-options>] ") |
| { |
| LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFormatAdd (interpreter))); |
| LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFormatClear (interpreter))); |
| LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFormatDelete (interpreter))); |
| LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFormatList (interpreter))); |
| LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo<TypeFormatImpl>(interpreter, |
| "format", |
| [](ValueObject& valobj) -> TypeFormatImpl::SharedPointer { |
| return valobj.GetValueFormat(); |
| }))); |
| } |
| |
| ~CommandObjectTypeFormat() override = default; |
| }; |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| |
| class CommandObjectTypeSynth : public CommandObjectMultiword |
| { |
| public: |
| CommandObjectTypeSynth(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "type synthetic", |
| "Commands for operating on synthetic type representations.", |
| "type synthetic [<sub-command-options>] ") |
| { |
| LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSynthAdd (interpreter))); |
| LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSynthClear (interpreter))); |
| LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSynthDelete (interpreter))); |
| LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSynthList (interpreter))); |
| LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo<SyntheticChildren>(interpreter, |
| "synthetic", |
| [](ValueObject& valobj) -> SyntheticChildren::SharedPointer { |
| return valobj.GetSyntheticChildren(); |
| }))); |
| } |
| |
| ~CommandObjectTypeSynth() override = default; |
| }; |
| |
| #endif // LLDB_DISABLE_PYTHON |
| |
| class CommandObjectTypeFilter : public CommandObjectMultiword |
| { |
| public: |
| CommandObjectTypeFilter(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "type filter", "Commands for operating on type filters.", |
| "type synthetic [<sub-command-options>] ") |
| { |
| LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFilterAdd (interpreter))); |
| LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFilterClear (interpreter))); |
| LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFilterDelete (interpreter))); |
| LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFilterList (interpreter))); |
| } |
| |
| ~CommandObjectTypeFilter() override = default; |
| }; |
| |
| class CommandObjectTypeCategory : public CommandObjectMultiword |
| { |
| public: |
| CommandObjectTypeCategory(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "type category", "Commands for operating on type categories.", |
| "type category [<sub-command-options>] ") |
| { |
| LoadSubCommand ("define", CommandObjectSP (new CommandObjectTypeCategoryDefine (interpreter))); |
| LoadSubCommand ("enable", CommandObjectSP (new CommandObjectTypeCategoryEnable (interpreter))); |
| LoadSubCommand ("disable", CommandObjectSP (new CommandObjectTypeCategoryDisable (interpreter))); |
| LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeCategoryDelete (interpreter))); |
| LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeCategoryList (interpreter))); |
| } |
| |
| ~CommandObjectTypeCategory() override = default; |
| }; |
| |
| class CommandObjectTypeSummary : public CommandObjectMultiword |
| { |
| public: |
| CommandObjectTypeSummary(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "type summary", "Commands for editing variable summary display options.", |
| "type summary [<sub-command-options>] ") |
| { |
| LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSummaryAdd (interpreter))); |
| LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSummaryClear (interpreter))); |
| LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSummaryDelete (interpreter))); |
| LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSummaryList (interpreter))); |
| LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo<TypeSummaryImpl>(interpreter, |
| "summary", |
| [](ValueObject& valobj) -> TypeSummaryImpl::SharedPointer { |
| return valobj.GetSummaryFormat(); |
| }))); |
| } |
| |
| ~CommandObjectTypeSummary() override = default; |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectType |
| //------------------------------------------------------------------------- |
| |
| CommandObjectType::CommandObjectType(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "type", "Commands for operating on the type system.", |
| "type [<sub-command-options>]") |
| { |
| LoadSubCommand ("category", CommandObjectSP (new CommandObjectTypeCategory (interpreter))); |
| LoadSubCommand ("filter", CommandObjectSP (new CommandObjectTypeFilter (interpreter))); |
| LoadSubCommand ("format", CommandObjectSP (new CommandObjectTypeFormat (interpreter))); |
| LoadSubCommand ("summary", CommandObjectSP (new CommandObjectTypeSummary (interpreter))); |
| #ifndef LLDB_DISABLE_PYTHON |
| LoadSubCommand ("synthetic", CommandObjectSP (new CommandObjectTypeSynth (interpreter))); |
| #endif // LLDB_DISABLE_PYTHON |
| LoadSubCommand ("lookup", CommandObjectSP (new CommandObjectTypeLookup (interpreter))); |
| } |
| |
| CommandObjectType::~CommandObjectType() = default; |