mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-05-17 19:36:48 +00:00

*** to conform to clang-format’s LLVM style. This kind of mass change has *** two obvious implications: Firstly, merging this particular commit into a downstream fork may be a huge effort. Alternatively, it may be worth merging all changes up to this commit, performing the same reformatting operation locally, and then discarding the merge for this particular commit. The commands used to accomplish this reformatting were as follows (with current working directory as the root of the repository): find . \( -iname "*.c" -or -iname "*.cpp" -or -iname "*.h" -or -iname "*.mm" \) -exec clang-format -i {} + find . -iname "*.py" -exec autopep8 --in-place --aggressive --aggressive {} + ; The version of clang-format used was 3.9.0, and autopep8 was 1.2.4. Secondly, “blame” style tools will generally point to this commit instead of a meaningful prior commit. There are alternatives available that will attempt to look through this change and find the appropriate prior commit. YMMV. llvm-svn: 280751
848 lines
27 KiB
C++
848 lines
27 KiB
C++
//===-- ValueObjectPrinter.cpp -------------------------------------*- C++
|
|
//-*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/DataFormatters/ValueObjectPrinter.h"
|
|
|
|
// C Includes
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
// Project includes
|
|
#include "lldb/Core/Stream.h"
|
|
#include "lldb/Core/ValueObject.h"
|
|
#include "lldb/DataFormatters/DataVisualization.h"
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
|
#include "lldb/Target/Language.h"
|
|
#include "lldb/Target/Target.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s) {
|
|
if (valobj) {
|
|
DumpValueObjectOptions options(*valobj);
|
|
Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
|
|
} else {
|
|
DumpValueObjectOptions options;
|
|
Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
|
|
}
|
|
}
|
|
|
|
ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s,
|
|
const DumpValueObjectOptions &options) {
|
|
Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
|
|
}
|
|
|
|
ValueObjectPrinter::ValueObjectPrinter(
|
|
ValueObject *valobj, Stream *s, const DumpValueObjectOptions &options,
|
|
const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth,
|
|
InstancePointersSetSP printed_instance_pointers) {
|
|
Init(valobj, s, options, ptr_depth, curr_depth, printed_instance_pointers);
|
|
}
|
|
|
|
void ValueObjectPrinter::Init(
|
|
ValueObject *valobj, Stream *s, const DumpValueObjectOptions &options,
|
|
const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth,
|
|
InstancePointersSetSP printed_instance_pointers) {
|
|
m_orig_valobj = valobj;
|
|
m_valobj = nullptr;
|
|
m_stream = s;
|
|
m_options = options;
|
|
m_ptr_depth = ptr_depth;
|
|
m_curr_depth = curr_depth;
|
|
assert(m_orig_valobj && "cannot print a NULL ValueObject");
|
|
assert(m_stream && "cannot print to a NULL Stream");
|
|
m_should_print = eLazyBoolCalculate;
|
|
m_is_nil = eLazyBoolCalculate;
|
|
m_is_uninit = eLazyBoolCalculate;
|
|
m_is_ptr = eLazyBoolCalculate;
|
|
m_is_ref = eLazyBoolCalculate;
|
|
m_is_aggregate = eLazyBoolCalculate;
|
|
m_is_instance_ptr = eLazyBoolCalculate;
|
|
m_summary_formatter = {nullptr, false};
|
|
m_value.assign("");
|
|
m_summary.assign("");
|
|
m_error.assign("");
|
|
m_val_summary_ok = false;
|
|
m_printed_instance_pointers =
|
|
printed_instance_pointers
|
|
? printed_instance_pointers
|
|
: InstancePointersSetSP(new InstancePointersSet());
|
|
}
|
|
|
|
bool ValueObjectPrinter::PrintValueObject() {
|
|
if (!GetMostSpecializedValue() || m_valobj == nullptr)
|
|
return false;
|
|
|
|
if (ShouldPrintValueObject()) {
|
|
PrintValidationMarkerIfNeeded();
|
|
|
|
PrintLocationIfNeeded();
|
|
m_stream->Indent();
|
|
|
|
PrintDecl();
|
|
}
|
|
|
|
bool value_printed = false;
|
|
bool summary_printed = false;
|
|
|
|
m_val_summary_ok =
|
|
PrintValueAndSummaryIfNeeded(value_printed, summary_printed);
|
|
|
|
if (m_val_summary_ok)
|
|
PrintChildrenIfNeeded(value_printed, summary_printed);
|
|
else
|
|
m_stream->EOL();
|
|
|
|
PrintValidationErrorIfNeeded();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ValueObjectPrinter::GetMostSpecializedValue() {
|
|
if (m_valobj)
|
|
return true;
|
|
bool update_success = m_orig_valobj->UpdateValueIfNeeded(true);
|
|
if (!update_success) {
|
|
m_valobj = m_orig_valobj;
|
|
} else {
|
|
if (m_orig_valobj->IsDynamic()) {
|
|
if (m_options.m_use_dynamic == eNoDynamicValues) {
|
|
ValueObject *static_value = m_orig_valobj->GetStaticValue().get();
|
|
if (static_value)
|
|
m_valobj = static_value;
|
|
else
|
|
m_valobj = m_orig_valobj;
|
|
} else
|
|
m_valobj = m_orig_valobj;
|
|
} else {
|
|
if (m_options.m_use_dynamic != eNoDynamicValues) {
|
|
ValueObject *dynamic_value =
|
|
m_orig_valobj->GetDynamicValue(m_options.m_use_dynamic).get();
|
|
if (dynamic_value)
|
|
m_valobj = dynamic_value;
|
|
else
|
|
m_valobj = m_orig_valobj;
|
|
} else
|
|
m_valobj = m_orig_valobj;
|
|
}
|
|
|
|
if (m_valobj->IsSynthetic()) {
|
|
if (m_options.m_use_synthetic == false) {
|
|
ValueObject *non_synthetic = m_valobj->GetNonSyntheticValue().get();
|
|
if (non_synthetic)
|
|
m_valobj = non_synthetic;
|
|
}
|
|
} else {
|
|
if (m_options.m_use_synthetic == true) {
|
|
ValueObject *synthetic = m_valobj->GetSyntheticValue().get();
|
|
if (synthetic)
|
|
m_valobj = synthetic;
|
|
}
|
|
}
|
|
}
|
|
m_compiler_type = m_valobj->GetCompilerType();
|
|
m_type_flags = m_compiler_type.GetTypeInfo();
|
|
return true;
|
|
}
|
|
|
|
const char *ValueObjectPrinter::GetDescriptionForDisplay() {
|
|
const char *str = m_valobj->GetObjectDescription();
|
|
if (!str)
|
|
str = m_valobj->GetSummaryAsCString();
|
|
if (!str)
|
|
str = m_valobj->GetValueAsCString();
|
|
return str;
|
|
}
|
|
|
|
const char *ValueObjectPrinter::GetRootNameForDisplay(const char *if_fail) {
|
|
const char *root_valobj_name = m_options.m_root_valobj_name.empty()
|
|
? m_valobj->GetName().AsCString()
|
|
: m_options.m_root_valobj_name.c_str();
|
|
return root_valobj_name ? root_valobj_name : if_fail;
|
|
}
|
|
|
|
bool ValueObjectPrinter::ShouldPrintValueObject() {
|
|
if (m_should_print == eLazyBoolCalculate)
|
|
m_should_print =
|
|
(m_options.m_flat_output == false || m_type_flags.Test(eTypeHasValue))
|
|
? eLazyBoolYes
|
|
: eLazyBoolNo;
|
|
return m_should_print == eLazyBoolYes;
|
|
}
|
|
|
|
bool ValueObjectPrinter::IsNil() {
|
|
if (m_is_nil == eLazyBoolCalculate)
|
|
m_is_nil = m_valobj->IsNilReference() ? eLazyBoolYes : eLazyBoolNo;
|
|
return m_is_nil == eLazyBoolYes;
|
|
}
|
|
|
|
bool ValueObjectPrinter::IsUninitialized() {
|
|
if (m_is_uninit == eLazyBoolCalculate)
|
|
m_is_uninit =
|
|
m_valobj->IsUninitializedReference() ? eLazyBoolYes : eLazyBoolNo;
|
|
return m_is_uninit == eLazyBoolYes;
|
|
}
|
|
|
|
bool ValueObjectPrinter::IsPtr() {
|
|
if (m_is_ptr == eLazyBoolCalculate)
|
|
m_is_ptr = m_type_flags.Test(eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo;
|
|
return m_is_ptr == eLazyBoolYes;
|
|
}
|
|
|
|
bool ValueObjectPrinter::IsRef() {
|
|
if (m_is_ref == eLazyBoolCalculate)
|
|
m_is_ref = m_type_flags.Test(eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo;
|
|
return m_is_ref == eLazyBoolYes;
|
|
}
|
|
|
|
bool ValueObjectPrinter::IsAggregate() {
|
|
if (m_is_aggregate == eLazyBoolCalculate)
|
|
m_is_aggregate =
|
|
m_type_flags.Test(eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo;
|
|
return m_is_aggregate == eLazyBoolYes;
|
|
}
|
|
|
|
bool ValueObjectPrinter::IsInstancePointer() {
|
|
// you need to do this check on the value's clang type
|
|
if (m_is_instance_ptr == eLazyBoolCalculate)
|
|
m_is_instance_ptr = (m_valobj->GetValue().GetCompilerType().GetTypeInfo() &
|
|
eTypeInstanceIsPointer) != 0
|
|
? eLazyBoolYes
|
|
: eLazyBoolNo;
|
|
if ((eLazyBoolYes == m_is_instance_ptr) && m_valobj->IsBaseClass())
|
|
m_is_instance_ptr = eLazyBoolNo;
|
|
return m_is_instance_ptr == eLazyBoolYes;
|
|
}
|
|
|
|
bool ValueObjectPrinter::PrintLocationIfNeeded() {
|
|
if (m_options.m_show_location) {
|
|
m_stream->Printf("%s: ", m_valobj->GetLocationAsCString());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ValueObjectPrinter::PrintDecl() {
|
|
bool show_type = true;
|
|
// if we are at the root-level and been asked to hide the root's type, then
|
|
// hide it
|
|
if (m_curr_depth == 0 && m_options.m_hide_root_type)
|
|
show_type = false;
|
|
else
|
|
// otherwise decide according to the usual rules (asked to show types -
|
|
// always at the root level)
|
|
show_type = m_options.m_show_types ||
|
|
(m_curr_depth == 0 && !m_options.m_flat_output);
|
|
|
|
StreamString typeName;
|
|
|
|
// always show the type at the root level if it is invalid
|
|
if (show_type) {
|
|
// Some ValueObjects don't have types (like registers sets). Only print
|
|
// the type if there is one to print
|
|
ConstString type_name;
|
|
if (m_compiler_type.IsValid()) {
|
|
if (m_options.m_use_type_display_name)
|
|
type_name = m_valobj->GetDisplayTypeName();
|
|
else
|
|
type_name = m_valobj->GetQualifiedTypeName();
|
|
} else {
|
|
// only show an invalid type name if the user explicitly triggered
|
|
// show_type
|
|
if (m_options.m_show_types)
|
|
type_name = ConstString("<invalid type>");
|
|
else
|
|
type_name.Clear();
|
|
}
|
|
|
|
if (type_name) {
|
|
std::string type_name_str(type_name.GetCString());
|
|
if (m_options.m_hide_pointer_value) {
|
|
for (auto iter = type_name_str.find(" *"); iter != std::string::npos;
|
|
iter = type_name_str.find(" *")) {
|
|
type_name_str.erase(iter, 2);
|
|
}
|
|
}
|
|
typeName.Printf("%s", type_name_str.c_str());
|
|
}
|
|
}
|
|
|
|
StreamString varName;
|
|
|
|
if (m_options.m_flat_output) {
|
|
// If we are showing types, also qualify the C++ base classes
|
|
const bool qualify_cxx_base_classes = show_type;
|
|
if (!m_options.m_hide_name) {
|
|
m_valobj->GetExpressionPath(varName, qualify_cxx_base_classes);
|
|
}
|
|
} else if (!m_options.m_hide_name) {
|
|
const char *name_cstr = GetRootNameForDisplay("");
|
|
varName.Printf("%s", name_cstr);
|
|
}
|
|
|
|
bool decl_printed = false;
|
|
if (!m_options.m_decl_printing_helper) {
|
|
// if the user didn't give us a custom helper, pick one based upon the
|
|
// language, either the one that this printer is bound to, or the preferred
|
|
// one for the ValueObject
|
|
lldb::LanguageType lang_type =
|
|
(m_options.m_varformat_language == lldb::eLanguageTypeUnknown)
|
|
? m_valobj->GetPreferredDisplayLanguage()
|
|
: m_options.m_varformat_language;
|
|
if (Language *lang_plugin = Language::FindPlugin(lang_type)) {
|
|
m_options.m_decl_printing_helper = lang_plugin->GetDeclPrintingHelper();
|
|
}
|
|
}
|
|
|
|
if (m_options.m_decl_printing_helper) {
|
|
ConstString type_name_cstr(typeName.GetData());
|
|
ConstString var_name_cstr(varName.GetData());
|
|
|
|
StreamString dest_stream;
|
|
if (m_options.m_decl_printing_helper(type_name_cstr, var_name_cstr,
|
|
m_options, dest_stream)) {
|
|
decl_printed = true;
|
|
m_stream->Printf("%s", dest_stream.GetData());
|
|
}
|
|
}
|
|
|
|
// if the helper failed, or there is none, do a default thing
|
|
if (!decl_printed) {
|
|
if (typeName.GetSize())
|
|
m_stream->Printf("(%s) ", typeName.GetData());
|
|
if (varName.GetSize())
|
|
m_stream->Printf("%s =", varName.GetData());
|
|
else if (!m_options.m_hide_name)
|
|
m_stream->Printf(" =");
|
|
}
|
|
}
|
|
|
|
bool ValueObjectPrinter::CheckScopeIfNeeded() {
|
|
if (m_options.m_scope_already_checked)
|
|
return true;
|
|
return m_valobj->IsInScope();
|
|
}
|
|
|
|
TypeSummaryImpl *ValueObjectPrinter::GetSummaryFormatter(bool null_if_omitted) {
|
|
if (m_summary_formatter.second == false) {
|
|
TypeSummaryImpl *entry = m_options.m_summary_sp
|
|
? m_options.m_summary_sp.get()
|
|
: m_valobj->GetSummaryFormat().get();
|
|
|
|
if (m_options.m_omit_summary_depth > 0)
|
|
entry = NULL;
|
|
m_summary_formatter.first = entry;
|
|
m_summary_formatter.second = true;
|
|
}
|
|
if (m_options.m_omit_summary_depth > 0 && null_if_omitted)
|
|
return nullptr;
|
|
return m_summary_formatter.first;
|
|
}
|
|
|
|
static bool IsPointerValue(const CompilerType &type) {
|
|
Flags type_flags(type.GetTypeInfo());
|
|
if (type_flags.AnySet(eTypeInstanceIsPointer | eTypeIsPointer))
|
|
return type_flags.AllClear(eTypeIsBuiltIn);
|
|
return false;
|
|
}
|
|
|
|
void ValueObjectPrinter::GetValueSummaryError(std::string &value,
|
|
std::string &summary,
|
|
std::string &error) {
|
|
lldb::Format format = m_options.m_format;
|
|
// if I am printing synthetized elements, apply the format to those elements
|
|
// only
|
|
if (m_options.m_element_count > 0)
|
|
m_valobj->GetValueAsCString(lldb::eFormatDefault, value);
|
|
else if (format != eFormatDefault && format != m_valobj->GetFormat())
|
|
m_valobj->GetValueAsCString(format, value);
|
|
else {
|
|
const char *val_cstr = m_valobj->GetValueAsCString();
|
|
if (val_cstr)
|
|
value.assign(val_cstr);
|
|
}
|
|
const char *err_cstr = m_valobj->GetError().AsCString();
|
|
if (err_cstr)
|
|
error.assign(err_cstr);
|
|
|
|
if (ShouldPrintValueObject()) {
|
|
if (IsNil())
|
|
summary.assign("nil");
|
|
else if (IsUninitialized())
|
|
summary.assign("<uninitialized>");
|
|
else if (m_options.m_omit_summary_depth == 0) {
|
|
TypeSummaryImpl *entry = GetSummaryFormatter();
|
|
if (entry)
|
|
m_valobj->GetSummaryAsCString(entry, summary,
|
|
m_options.m_varformat_language);
|
|
else {
|
|
const char *sum_cstr =
|
|
m_valobj->GetSummaryAsCString(m_options.m_varformat_language);
|
|
if (sum_cstr)
|
|
summary.assign(sum_cstr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed,
|
|
bool &summary_printed) {
|
|
bool error_printed = false;
|
|
if (ShouldPrintValueObject()) {
|
|
if (!CheckScopeIfNeeded())
|
|
m_error.assign("out of scope");
|
|
if (m_error.empty()) {
|
|
GetValueSummaryError(m_value, m_summary, m_error);
|
|
}
|
|
if (m_error.size()) {
|
|
// we need to support scenarios in which it is actually fine for a value
|
|
// to have no type
|
|
// but - on the other hand - if we get an error *AND* have no type, we try
|
|
// to get out
|
|
// gracefully, since most often that combination means "could not resolve
|
|
// a type"
|
|
// and the default failure mode is quite ugly
|
|
if (!m_compiler_type.IsValid()) {
|
|
m_stream->Printf(" <could not resolve type>");
|
|
return false;
|
|
}
|
|
|
|
error_printed = true;
|
|
m_stream->Printf(" <%s>\n", m_error.c_str());
|
|
} else {
|
|
// Make sure we have a value and make sure the summary didn't
|
|
// specify that the value should not be printed - and do not print
|
|
// the value if this thing is nil
|
|
// (but show the value if the user passes a format explicitly)
|
|
TypeSummaryImpl *entry = GetSummaryFormatter();
|
|
if (!IsNil() && !IsUninitialized() && !m_value.empty() &&
|
|
(entry == NULL || (entry->DoesPrintValue(m_valobj) ||
|
|
m_options.m_format != eFormatDefault) ||
|
|
m_summary.empty()) &&
|
|
!m_options.m_hide_value) {
|
|
if (m_options.m_hide_pointer_value &&
|
|
IsPointerValue(m_valobj->GetCompilerType())) {
|
|
} else {
|
|
m_stream->Printf(" %s", m_value.c_str());
|
|
value_printed = true;
|
|
}
|
|
}
|
|
|
|
if (m_summary.size()) {
|
|
m_stream->Printf(" %s", m_summary.c_str());
|
|
summary_printed = true;
|
|
}
|
|
}
|
|
}
|
|
return !error_printed;
|
|
}
|
|
|
|
bool ValueObjectPrinter::PrintObjectDescriptionIfNeeded(bool value_printed,
|
|
bool summary_printed) {
|
|
if (ShouldPrintValueObject()) {
|
|
// let's avoid the overly verbose no description error for a nil thing
|
|
if (m_options.m_use_objc && !IsNil() && !IsUninitialized() &&
|
|
(m_options.m_element_count == 0)) {
|
|
if (!m_options.m_hide_value || !m_options.m_hide_name)
|
|
m_stream->Printf(" ");
|
|
const char *object_desc = nullptr;
|
|
if (value_printed || summary_printed)
|
|
object_desc = m_valobj->GetObjectDescription();
|
|
else
|
|
object_desc = GetDescriptionForDisplay();
|
|
if (object_desc && *object_desc) {
|
|
m_stream->Printf("%s\n", object_desc);
|
|
return true;
|
|
} else if (value_printed == false && summary_printed == false)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion(
|
|
bool is_root, TypeSummaryImpl *entry, ValueObject *valobj,
|
|
const std::string &summary) {
|
|
switch (m_mode) {
|
|
case Mode::Always:
|
|
return (m_count > 0);
|
|
case Mode::Never:
|
|
return false;
|
|
case Mode::Default:
|
|
if (is_root)
|
|
m_count = std::min<decltype(m_count)>(m_count, 1);
|
|
return m_count > 0;
|
|
case Mode::Formatters:
|
|
if (!entry || entry->DoesPrintChildren(valobj) || summary.empty())
|
|
return m_count > 0;
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const {
|
|
switch (m_mode) {
|
|
case Mode::Always:
|
|
case Mode::Default:
|
|
case Mode::Formatters:
|
|
return (m_count > 0);
|
|
case Mode::Never:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ValueObjectPrinter::ShouldPrintChildren(
|
|
bool is_failed_description,
|
|
DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
|
|
const bool is_ref = IsRef();
|
|
const bool is_ptr = IsPtr();
|
|
const bool is_uninit = IsUninitialized();
|
|
|
|
if (is_uninit)
|
|
return false;
|
|
|
|
// if the user has specified an element count, always print children
|
|
// as it is explicit user demand being honored
|
|
if (m_options.m_element_count > 0)
|
|
return true;
|
|
|
|
TypeSummaryImpl *entry = GetSummaryFormatter();
|
|
|
|
if (m_options.m_use_objc)
|
|
return false;
|
|
|
|
if (is_failed_description || m_curr_depth < m_options.m_max_depth) {
|
|
// We will show children for all concrete types. We won't show
|
|
// pointer contents unless a pointer depth has been specified.
|
|
// We won't reference contents unless the reference is the
|
|
// root object (depth of zero).
|
|
|
|
// Use a new temporary pointer depth in case we override the
|
|
// current pointer depth below...
|
|
|
|
if (is_ptr || is_ref) {
|
|
// We have a pointer or reference whose value is an address.
|
|
// Make sure that address is not NULL
|
|
AddressType ptr_address_type;
|
|
if (m_valobj->GetPointerValue(&ptr_address_type) == 0)
|
|
return false;
|
|
|
|
const bool is_root_level = m_curr_depth == 0;
|
|
|
|
if (is_ref && is_root_level) {
|
|
// If this is the root object (depth is zero) that we are showing
|
|
// and it is a reference, and no pointer depth has been supplied
|
|
// print out what it references. Don't do this at deeper depths
|
|
// otherwise we can end up with infinite recursion...
|
|
return true;
|
|
}
|
|
|
|
return curr_ptr_depth.CanAllowExpansion(false, entry, m_valobj,
|
|
m_summary);
|
|
}
|
|
|
|
return (!entry || entry->DoesPrintChildren(m_valobj) || m_summary.empty());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ValueObjectPrinter::ShouldExpandEmptyAggregates() {
|
|
TypeSummaryImpl *entry = GetSummaryFormatter();
|
|
|
|
if (!entry)
|
|
return true;
|
|
|
|
return entry->DoesPrintEmptyAggregates();
|
|
}
|
|
|
|
ValueObject *ValueObjectPrinter::GetValueObjectForChildrenGeneration() {
|
|
return m_valobj;
|
|
}
|
|
|
|
void ValueObjectPrinter::PrintChildrenPreamble() {
|
|
if (m_options.m_flat_output) {
|
|
if (ShouldPrintValueObject())
|
|
m_stream->EOL();
|
|
} else {
|
|
if (ShouldPrintValueObject())
|
|
m_stream->PutCString(IsRef() ? ": {\n" : " {\n");
|
|
m_stream->IndentMore();
|
|
}
|
|
}
|
|
|
|
void ValueObjectPrinter::PrintChild(
|
|
ValueObjectSP child_sp,
|
|
const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
|
|
const uint32_t consumed_depth = (m_options.m_element_count == 0) ? 1 : 0;
|
|
const bool does_consume_ptr_depth =
|
|
((IsPtr() && m_options.m_element_count == 0) || IsRef());
|
|
|
|
DumpValueObjectOptions child_options(m_options);
|
|
child_options.SetFormat(m_options.m_format)
|
|
.SetSummary()
|
|
.SetRootValueObjectName();
|
|
child_options.SetScopeChecked(true)
|
|
.SetHideName(m_options.m_hide_name)
|
|
.SetHideValue(m_options.m_hide_value)
|
|
.SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1
|
|
? child_options.m_omit_summary_depth -
|
|
consumed_depth
|
|
: 0)
|
|
.SetElementCount(0);
|
|
|
|
if (child_sp.get()) {
|
|
ValueObjectPrinter child_printer(
|
|
child_sp.get(), m_stream, child_options,
|
|
does_consume_ptr_depth ? --curr_ptr_depth : curr_ptr_depth,
|
|
m_curr_depth + consumed_depth, m_printed_instance_pointers);
|
|
child_printer.PrintValueObject();
|
|
}
|
|
}
|
|
|
|
uint32_t ValueObjectPrinter::GetMaxNumChildrenToPrint(bool &print_dotdotdot) {
|
|
ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
|
|
|
|
if (m_options.m_element_count > 0)
|
|
return m_options.m_element_count;
|
|
|
|
size_t num_children = synth_m_valobj->GetNumChildren();
|
|
print_dotdotdot = false;
|
|
if (num_children) {
|
|
const size_t max_num_children =
|
|
m_valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
|
|
|
|
if (num_children > max_num_children && !m_options.m_ignore_cap) {
|
|
print_dotdotdot = true;
|
|
return max_num_children;
|
|
}
|
|
}
|
|
return num_children;
|
|
}
|
|
|
|
void ValueObjectPrinter::PrintChildrenPostamble(bool print_dotdotdot) {
|
|
if (!m_options.m_flat_output) {
|
|
if (print_dotdotdot) {
|
|
m_valobj->GetTargetSP()
|
|
->GetDebugger()
|
|
.GetCommandInterpreter()
|
|
.ChildrenTruncated();
|
|
m_stream->Indent("...\n");
|
|
}
|
|
m_stream->IndentLess();
|
|
m_stream->Indent("}\n");
|
|
}
|
|
}
|
|
|
|
bool ValueObjectPrinter::ShouldPrintEmptyBrackets(bool value_printed,
|
|
bool summary_printed) {
|
|
ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
|
|
|
|
if (!IsAggregate())
|
|
return false;
|
|
|
|
if (m_options.m_reveal_empty_aggregates == false) {
|
|
if (value_printed || summary_printed)
|
|
return false;
|
|
}
|
|
|
|
if (synth_m_valobj->MightHaveChildren())
|
|
return true;
|
|
|
|
if (m_val_summary_ok)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
ValueObjectSP ValueObjectPrinter::GenerateChild(ValueObject *synth_valobj,
|
|
size_t idx) {
|
|
if (m_options.m_element_count > 0) {
|
|
// if generating pointer-as-array children, use GetSyntheticArrayMember
|
|
return synth_valobj->GetSyntheticArrayMember(idx, true);
|
|
} else {
|
|
// otherwise, do the usual thing
|
|
return synth_valobj->GetChildAtIndex(idx, true);
|
|
}
|
|
}
|
|
|
|
void ValueObjectPrinter::PrintChildren(
|
|
bool value_printed, bool summary_printed,
|
|
const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
|
|
ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
|
|
|
|
bool print_dotdotdot = false;
|
|
size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
|
|
if (num_children) {
|
|
bool any_children_printed = false;
|
|
|
|
for (size_t idx = 0; idx < num_children; ++idx) {
|
|
if (ValueObjectSP child_sp = GenerateChild(synth_m_valobj, idx)) {
|
|
if (!any_children_printed) {
|
|
PrintChildrenPreamble();
|
|
any_children_printed = true;
|
|
}
|
|
PrintChild(child_sp, curr_ptr_depth);
|
|
}
|
|
}
|
|
|
|
if (any_children_printed)
|
|
PrintChildrenPostamble(print_dotdotdot);
|
|
else {
|
|
if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) {
|
|
if (ShouldPrintValueObject())
|
|
m_stream->PutCString(" {}\n");
|
|
else
|
|
m_stream->EOL();
|
|
} else
|
|
m_stream->EOL();
|
|
}
|
|
} else if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) {
|
|
// Aggregate, no children...
|
|
if (ShouldPrintValueObject()) {
|
|
// if it has a synthetic value, then don't print {}, the synthetic
|
|
// children are probably only being used to vend a value
|
|
if (m_valobj->DoesProvideSyntheticValue() ||
|
|
!ShouldExpandEmptyAggregates())
|
|
m_stream->PutCString("\n");
|
|
else
|
|
m_stream->PutCString(" {}\n");
|
|
}
|
|
} else {
|
|
if (ShouldPrintValueObject())
|
|
m_stream->EOL();
|
|
}
|
|
}
|
|
|
|
bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) {
|
|
if (!GetMostSpecializedValue() || m_valobj == nullptr)
|
|
return false;
|
|
|
|
ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
|
|
|
|
bool print_dotdotdot = false;
|
|
size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
|
|
|
|
if (num_children) {
|
|
m_stream->PutChar('(');
|
|
|
|
for (uint32_t idx = 0; idx < num_children; ++idx) {
|
|
lldb::ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx, true));
|
|
if (child_sp)
|
|
child_sp = child_sp->GetQualifiedRepresentationIfAvailable(
|
|
m_options.m_use_dynamic, m_options.m_use_synthetic);
|
|
if (child_sp) {
|
|
if (idx)
|
|
m_stream->PutCString(", ");
|
|
if (!hide_names) {
|
|
const char *name = child_sp.get()->GetName().AsCString();
|
|
if (name && *name) {
|
|
m_stream->PutCString(name);
|
|
m_stream->PutCString(" = ");
|
|
}
|
|
}
|
|
child_sp->DumpPrintableRepresentation(
|
|
*m_stream, ValueObject::eValueObjectRepresentationStyleSummary,
|
|
m_options.m_format,
|
|
ValueObject::ePrintableRepresentationSpecialCasesDisable);
|
|
}
|
|
}
|
|
|
|
if (print_dotdotdot)
|
|
m_stream->PutCString(", ...)");
|
|
else
|
|
m_stream->PutChar(')');
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed,
|
|
bool summary_printed) {
|
|
// this flag controls whether we tried to display a description for this
|
|
// object and failed
|
|
// if that happens, we want to display the children, if any
|
|
bool is_failed_description =
|
|
!PrintObjectDescriptionIfNeeded(value_printed, summary_printed);
|
|
|
|
auto curr_ptr_depth = m_ptr_depth;
|
|
bool print_children =
|
|
ShouldPrintChildren(is_failed_description, curr_ptr_depth);
|
|
bool print_oneline =
|
|
(curr_ptr_depth.CanAllowExpansion() || m_options.m_show_types ||
|
|
!m_options.m_allow_oneliner_mode || m_options.m_flat_output ||
|
|
(m_options.m_element_count > 0) || m_options.m_show_location)
|
|
? false
|
|
: DataVisualization::ShouldPrintAsOneLiner(*m_valobj);
|
|
bool is_instance_ptr = IsInstancePointer();
|
|
uint64_t instance_ptr_value = LLDB_INVALID_ADDRESS;
|
|
|
|
if (print_children && is_instance_ptr) {
|
|
instance_ptr_value = m_valobj->GetValueAsUnsigned(0);
|
|
if (m_printed_instance_pointers->count(instance_ptr_value)) {
|
|
// we already printed this instance-is-pointer thing, so don't expand it
|
|
m_stream->PutCString(" {...}\n");
|
|
|
|
// we're done here - get out fast
|
|
return;
|
|
} else
|
|
m_printed_instance_pointers->emplace(
|
|
instance_ptr_value); // remember this guy for future reference
|
|
}
|
|
|
|
if (print_children) {
|
|
if (print_oneline) {
|
|
m_stream->PutChar(' ');
|
|
PrintChildrenOneLiner(false);
|
|
m_stream->EOL();
|
|
} else
|
|
PrintChildren(value_printed, summary_printed, curr_ptr_depth);
|
|
} else if (m_curr_depth >= m_options.m_max_depth && IsAggregate() &&
|
|
ShouldPrintValueObject()) {
|
|
m_stream->PutCString("{...}\n");
|
|
} else
|
|
m_stream->EOL();
|
|
}
|
|
|
|
bool ValueObjectPrinter::ShouldPrintValidation() {
|
|
return m_options.m_run_validator;
|
|
}
|
|
|
|
bool ValueObjectPrinter::PrintValidationMarkerIfNeeded() {
|
|
if (!ShouldPrintValidation())
|
|
return false;
|
|
|
|
m_validation = m_valobj->GetValidationStatus();
|
|
|
|
if (TypeValidatorResult::Failure == m_validation.first) {
|
|
m_stream->Printf("! ");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ValueObjectPrinter::PrintValidationErrorIfNeeded() {
|
|
if (!ShouldPrintValidation())
|
|
return false;
|
|
|
|
if (TypeValidatorResult::Success == m_validation.first)
|
|
return false;
|
|
|
|
if (m_validation.second.empty())
|
|
m_validation.second.assign("unknown error");
|
|
|
|
m_stream->Printf(" ! validation error: %s", m_validation.second.c_str());
|
|
m_stream->EOL();
|
|
|
|
return true;
|
|
}
|