3227 lines
113 KiB
C++
3227 lines
113 KiB
C++
//===-- 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>
|
|
|
|
// Project includes
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Core/IOHandler.h"
|
|
#include "lldb/Core/State.h"
|
|
#include "lldb/DataFormatters/DataVisualization.h"
|
|
#include "lldb/Host/OptionParser.h"
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
|
#include "lldb/Interpreter/CommandObject.h"
|
|
#include "lldb/Interpreter/CommandReturnObject.h"
|
|
#include "lldb/Interpreter/OptionGroupFormat.h"
|
|
#include "lldb/Interpreter/OptionValueBoolean.h"
|
|
#include "lldb/Interpreter/OptionValueLanguage.h"
|
|
#include "lldb/Interpreter/OptionValueString.h"
|
|
#include "lldb/Interpreter/Options.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"
|
|
#include "lldb/Utility/ConstString.h"
|
|
#include "lldb/Utility/RegularExpression.h"
|
|
#include "lldb/Utility/StringList.h"
|
|
|
|
// Other libraries and framework includes
|
|
#include "llvm/ADT/STLExtras.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) {
|
|
if (command.empty())
|
|
return false;
|
|
|
|
for (auto entry : llvm::enumerate(command.entries().drop_back())) {
|
|
if (entry.value().ref != "unsigned")
|
|
continue;
|
|
auto next = command.entries()[entry.index() + 1].ref;
|
|
if (next == "int" || next == "short" || next == "char" || next == "long") {
|
|
result.AppendWarningWithFormat(
|
|
"unsigned %s being treated as two types. if you meant the combined "
|
|
"type "
|
|
"name use quotes, as in \"unsigned %s\"\n",
|
|
next.str().c_str(), next.str().c_str());
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static OptionDefinition g_type_summary_add_options[] = {
|
|
// clang-format off
|
|
{ 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." }
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
|
|
public IOHandlerDelegateMultiline {
|
|
private:
|
|
class CommandOptions : public Options {
|
|
public:
|
|
CommandOptions(CommandInterpreter &interpreter) : Options() {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) override;
|
|
|
|
void OptionParsingStarting(ExecutionContext *execution_context) override;
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_summary_add_options);
|
|
}
|
|
|
|
// 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";
|
|
|
|
static OptionDefinition g_type_synth_add_options[] = {
|
|
// clang-format off
|
|
{ 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." }
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeSynthAdd : public CommandObjectParsed,
|
|
public IOHandlerDelegateMultiline {
|
|
private:
|
|
class CommandOptions : public Options {
|
|
public:
|
|
CommandOptions() : Options() {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) 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.str().c_str());
|
|
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(ExecutionContext *execution_context) 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;
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_synth_add_options);
|
|
}
|
|
|
|
// 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
|
|
//-------------------------------------------------------------------------
|
|
|
|
static OptionDefinition g_type_format_add_options[] = {
|
|
// clang-format off
|
|
{ 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." }
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeFormatAdd : public CommandObjectParsed {
|
|
private:
|
|
class CommandOptions : public OptionGroup {
|
|
public:
|
|
CommandOptions() : OptionGroup() {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_format_add_options);
|
|
}
|
|
|
|
void OptionParsingStarting(ExecutionContext *execution_context) 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(uint32_t option_idx, llvm::StringRef option_value,
|
|
ExecutionContext *execution_context) override {
|
|
Error error;
|
|
const int short_option =
|
|
g_type_format_add_options[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.str().c_str());
|
|
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;
|
|
}
|
|
|
|
// 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(), 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 hexadecimal 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 (auto &arg_entry : command.entries()) {
|
|
if (arg_entry.ref.empty()) {
|
|
result.AppendError("empty typenames not allowed");
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
ConstString typeCS(arg_entry.ref);
|
|
if (m_command_options.m_regex) {
|
|
RegularExpressionSP typeRX(new RegularExpression());
|
|
if (!typeRX->Compile(arg_entry.ref)) {
|
|
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);
|
|
}
|
|
|
|
result.SetStatus(eReturnStatusSuccessFinishNoResult);
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
static OptionDefinition g_type_formatter_delete_options[] = {
|
|
// clang-format off
|
|
{ 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." }
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeFormatterDelete : public CommandObjectParsed {
|
|
protected:
|
|
class CommandOptions : public Options {
|
|
public:
|
|
CommandOptions() : Options() {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) 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(ExecutionContext *execution_context) override {
|
|
m_delete_all = false;
|
|
m_category = "default";
|
|
m_language = lldb::eLanguageTypeUnknown;
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_formatter_delete_options);
|
|
}
|
|
|
|
// 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(),
|
|
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;
|
|
}
|
|
}
|
|
};
|
|
|
|
static OptionDefinition g_type_formatter_clear_options[] = {
|
|
// clang-format off
|
|
{ LLDB_OPT_SET_ALL, false, "all", 'a', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Clear every category." }
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeFormatterClear : public CommandObjectParsed {
|
|
private:
|
|
class CommandOptions : public Options {
|
|
public:
|
|
CommandOptions() : Options() {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) 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(ExecutionContext *execution_context) override {
|
|
m_delete_all = false;
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_formatter_clear_options);
|
|
}
|
|
|
|
// 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(),
|
|
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();
|
|
}
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 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()
|
|
: Options(), m_category_regex("", ""),
|
|
m_category_language(lldb::eLanguageTypeUnknown,
|
|
lldb::eLanguageTypeUnknown) {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) 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(ExecutionContext *execution_context) override {
|
|
m_category_regex.Clear();
|
|
m_category_language.Clear();
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
static OptionDefinition g_option_table[] = {
|
|
// clang-format off
|
|
{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."}
|
|
// clang-format on
|
|
};
|
|
return llvm::ArrayRef<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() {
|
|
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.GetCurrentValueAsRef())) {
|
|
result.AppendErrorWithFormat(
|
|
"syntax error in category regular expression '%s'",
|
|
m_options.m_category_regex.GetCurrentValueAsRef().str().c_str());
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (argc == 1) {
|
|
const char *arg = command.GetArgumentAtIndex(0);
|
|
formatter_regex.reset(new RegularExpression());
|
|
if (!formatter_regex->Compile(llvm::StringRef::withNullAsEmpty(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 (name.GetStringRef() == formatter_regex->GetText()) {
|
|
escape = false;
|
|
} else if (formatter_regex->Execute(name.GetStringRef())) {
|
|
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 (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().str().c_str(),
|
|
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(
|
|
[&category_regex, &category_closure](
|
|
const lldb::TypeCategoryImplSP &category) -> bool {
|
|
if (category_regex) {
|
|
bool escape = true;
|
|
if (category->GetName() == category_regex->GetText()) {
|
|
escape = false;
|
|
} else if (category_regex->Execute(
|
|
llvm::StringRef::withNullAsEmpty(
|
|
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, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) {
|
|
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.str().c_str());
|
|
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.SetString(option_arg);
|
|
break;
|
|
case 'o':
|
|
m_python_script = option_arg;
|
|
m_is_add_script = true;
|
|
break;
|
|
case 'F':
|
|
m_python_function = 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(
|
|
ExecutionContext *execution_context) {
|
|
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 (auto &entry : command.entries()) {
|
|
if (entry.ref.empty()) {
|
|
result.AppendError("empty typenames not allowed");
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
options->m_target_types << entry.ref;
|
|
}
|
|
|
|
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 (auto &entry : command.entries()) {
|
|
CommandObjectTypeSummaryAdd::AddSummary(
|
|
ConstString(entry.ref), 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 (auto &arg_entry : command.entries()) {
|
|
if (arg_entry.ref.empty()) {
|
|
result.AppendError("empty typenames not allowed");
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
ConstString typeCS(arg_entry.ref);
|
|
|
|
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.GetStringRef())) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 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
|
|
//-------------------------------------------------------------------------
|
|
|
|
static OptionDefinition g_type_category_define_options[] = {
|
|
// clang-format off
|
|
{ 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." }
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeCategoryDefine : public CommandObjectParsed {
|
|
class CommandOptions : public Options {
|
|
public:
|
|
CommandOptions()
|
|
: Options(), m_define_enabled(false, false),
|
|
m_cate_language(eLanguageTypeUnknown, eLanguageTypeUnknown) {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) override {
|
|
Error error;
|
|
const int short_option = m_getopt_table[option_idx].val;
|
|
|
|
switch (short_option) {
|
|
case 'e':
|
|
m_define_enabled.SetValueFromString(llvm::StringRef("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(ExecutionContext *execution_context) override {
|
|
m_define_enabled.Clear();
|
|
m_cate_language.Clear();
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_category_define_options);
|
|
}
|
|
|
|
// 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() {
|
|
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 (auto &entry : command.entries()) {
|
|
TypeCategoryImplSP category_sp;
|
|
if (DataVisualization::Categories::GetCategory(ConstString(entry.ref),
|
|
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();
|
|
}
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
// CommandObjectTypeCategoryEnable
|
|
//-------------------------------------------------------------------------
|
|
|
|
static OptionDefinition g_type_category_enable_options[] = {
|
|
// clang-format off
|
|
{ LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Enable the category for this language." },
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeCategoryEnable : public CommandObjectParsed {
|
|
class CommandOptions : public Options {
|
|
public:
|
|
CommandOptions() : Options() {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) override {
|
|
Error error;
|
|
const int short_option = m_getopt_table[option_idx].val;
|
|
|
|
switch (short_option) {
|
|
case 'l':
|
|
if (!option_arg.empty()) {
|
|
m_language = Language::GetLanguageTypeFromString(option_arg);
|
|
if (m_language == lldb::eLanguageTypeUnknown)
|
|
error.SetErrorStringWithFormat("unrecognized language '%s'",
|
|
option_arg.str().c_str());
|
|
}
|
|
break;
|
|
default:
|
|
error.SetErrorStringWithFormat("unrecognized option '%c'",
|
|
short_option);
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void OptionParsingStarting(ExecutionContext *execution_context) override {
|
|
m_language = lldb::eLanguageTypeUnknown;
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_category_enable_options);
|
|
}
|
|
|
|
// 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() {
|
|
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();
|
|
}
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 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
|
|
//-------------------------------------------------------------------------
|
|
|
|
OptionDefinition g_type_category_disable_options[] = {
|
|
// clang-format off
|
|
{ LLDB_OPT_SET_ALL, false, "language", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage, "Enable the category for this language." }
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeCategoryDisable : public CommandObjectParsed {
|
|
class CommandOptions : public Options {
|
|
public:
|
|
CommandOptions() : Options() {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) override {
|
|
Error error;
|
|
const int short_option = m_getopt_table[option_idx].val;
|
|
|
|
switch (short_option) {
|
|
case 'l':
|
|
if (!option_arg.empty()) {
|
|
m_language = Language::GetLanguageTypeFromString(option_arg);
|
|
if (m_language == lldb::eLanguageTypeUnknown)
|
|
error.SetErrorStringWithFormat("unrecognized language '%s'",
|
|
option_arg.str().c_str());
|
|
}
|
|
break;
|
|
default:
|
|
error.SetErrorStringWithFormat("unrecognized option '%c'",
|
|
short_option);
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void OptionParsingStarting(ExecutionContext *execution_context) override {
|
|
m_language = lldb::eLanguageTypeUnknown;
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_category_disable_options);
|
|
}
|
|
|
|
// 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() {
|
|
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();
|
|
}
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
// 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(llvm::StringRef::withNullAsEmpty(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 (regex->GetText() == category_sp->GetName()) {
|
|
escape = false;
|
|
} else if (regex->Execute(llvm::StringRef::withNullAsEmpty(
|
|
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);
|
|
|
|
for (auto &entry : command.entries()) {
|
|
if (entry.ref.empty()) {
|
|
result.AppendError("empty typenames not allowed");
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
options->m_target_types << entry.ref;
|
|
}
|
|
|
|
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 (auto &arg_entry : command.entries()) {
|
|
if (arg_entry.ref.empty()) {
|
|
result.AppendError("empty typenames not allowed");
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
ConstString typeCS(arg_entry.ref);
|
|
if (!AddSynth(typeCS, entry,
|
|
m_options.m_regex ? eRegexSynth : eRegularSynth,
|
|
m_options.m_category, &error)) {
|
|
result.AppendError(error.AsCString());
|
|
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() {
|
|
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.GetStringRef())) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
#endif // LLDB_DISABLE_PYTHON
|
|
|
|
static OptionDefinition g_type_filter_add_options[] = {
|
|
// clang-format off
|
|
{ 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." }
|
|
// clang-format on
|
|
};
|
|
|
|
class CommandObjectTypeFilterAdd : public CommandObjectParsed {
|
|
private:
|
|
class CommandOptions : public Options {
|
|
typedef std::vector<std::string> option_vector;
|
|
|
|
public:
|
|
CommandOptions() : Options() {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) 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.str().c_str());
|
|
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(ExecutionContext *execution_context) 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;
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_filter_add_options);
|
|
}
|
|
|
|
// 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.GetStringRef())) {
|
|
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() {
|
|
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 (auto &arg_entry : command.entries()) {
|
|
if (arg_entry.ref.empty()) {
|
|
result.AppendError("empty typenames not allowed");
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
|
|
ConstString typeCS(arg_entry.ref);
|
|
if (!AddFilter(typeCS, entry,
|
|
m_options.m_regex ? eRegexFilter : eRegularFilter,
|
|
m_options.m_category, &error)) {
|
|
result.AppendError(error.AsCString());
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
result.SetStatus(eReturnStatusSuccessFinishNoResult);
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// "type lookup"
|
|
//----------------------------------------------------------------------
|
|
static OptionDefinition g_type_lookup_options[] = {
|
|
// clang-format off
|
|
{ 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" }
|
|
// clang-format on
|
|
};
|
|
|
|
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;
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::makeArrayRef(g_type_lookup_options);
|
|
}
|
|
|
|
Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
|
|
ExecutionContext *execution_context) override {
|
|
Error error;
|
|
|
|
const int short_option = g_type_lookup_options[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(ExecutionContext *execution_context) override {
|
|
m_show_help = false;
|
|
m_language = eLanguageTypeUnknown;
|
|
}
|
|
|
|
// Options table: Required for subclasses of Options.
|
|
|
|
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(), m_command_options() {
|
|
m_option_group.Append(&m_command_options);
|
|
m_option_group.Finalize();
|
|
}
|
|
|
|
~CommandObjectTypeLookup() override = default;
|
|
|
|
Options *GetOptions() override { return &m_option_group; }
|
|
|
|
llvm::StringRef GetHelpLong() override {
|
|
if (!m_cmd_help_long.empty())
|
|
return m_cmd_help_long;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_cmd_help_long = stream.GetString();
|
|
return m_cmd_help_long;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
|
|
m_option_group.NotifyOptionParsingStarting(&exe_ctx);
|
|
|
|
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(&exe_ctx));
|
|
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;
|
|
LanguageType guessed_language = lldb::eLanguageTypeUnknown;
|
|
|
|
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()) {
|
|
guessed_language = GuessLanguage(frame);
|
|
if (guessed_language != eLanguageTypeUnknown) {
|
|
std::sort(
|
|
languages.begin(), languages.end(),
|
|
[guessed_language](Language *lang1, Language *lang2) -> bool {
|
|
if (!lang1 || !lang2)
|
|
return false;
|
|
LanguageType lt1 = lang1->GetLanguageType();
|
|
LanguageType lt2 = lang2->GetLanguageType();
|
|
if (lt1 == guessed_language)
|
|
return true; // make the selected frame's language come first
|
|
if (lt2 == guessed_language)
|
|
return false; // make the selected frame's language come first
|
|
return (lt1 < lt2); // normal comparison otherwise
|
|
});
|
|
}
|
|
}
|
|
|
|
bool is_first_language = true;
|
|
|
|
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;
|
|
else if (is_first_language && is_global_search &&
|
|
guessed_language != lldb::eLanguageTypeUnknown) {
|
|
is_first_language = false;
|
|
result.GetOutputStream().Printf(
|
|
"no type was found in the current language %s matching '%s'; "
|
|
"performing a global search across all languages\n",
|
|
Language::GetNameForLanguageType(guessed_language), name_of_type);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
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, "", "", "",
|
|
eCommandRequiresFrame),
|
|
m_formatter_name(formatter_name ? formatter_name : ""),
|
|
m_discovery_function(discovery_func) {
|
|
StreamString name;
|
|
name.Printf("type %s info", formatter_name);
|
|
SetCommandName(name.GetString());
|
|
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.GetString());
|
|
StreamString syntax;
|
|
syntax.Printf("type %s info <expr>", formatter_name);
|
|
SetSyntax(syntax.GetString());
|
|
}
|
|
|
|
~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;
|