llvm-project/lldb/source/DataFormatters/ValueObjectPrinter.cpp

947 lines
29 KiB
C++
Raw Normal View History

//===-- 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();
}
Introduce the concept of a "display name" for types Rationale: Pretty simply, the idea is that sometimes type names are way too long and contain way too many details for the average developer to care about. For instance, a plain ol' vector of int might be shown as std::__1::vector<int, std::__1::allocator<.... rather than the much simpler std::vector<int> form, which is what most developers would actually type in their code Proposed solution: Introduce a notion of "display name" and a corresponding API GetDisplayTypeName() to return such a crafted for visual representation type name Obviously, the display name and the fully qualified (or "true") name are not necessarily the same - that's the whole point LLDB could choose to pick the "display name" as its one true notion of a type name, and if somebody really needs the fully qualified version of it, let them deal with the problem Or, LLDB could rename what it currently calls the "type name" to be the "display name", and add new APIs for the fully qualified name, making the display name the default choice The choice that I am making here is that the type name will keep meaning the same, and people who want a type name suited for display will explicitly ask for one It is the less risky/disruptive choice - and it should eventually make it fairly obvious when someone is asking for the wrong type Caveats: - for now, GetDisplayTypeName() == GetTypeName(), there is no logic to produce customized display type names yet. - while the fully-qualified type name is still the main key to the kingdom of data formatters, if we start showing custom names to people, those should match formatters llvm-svn: 209072
2014-05-18 03:14:17 +08:00
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)
{
if (m_options.m_format != eFormatDefault && m_options.m_format != m_valobj->GetFormat())
{
m_valobj->GetValueAsCString(m_options.m_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())
{
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;
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)
{
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 - 1 : 0);
if (child_sp.get())
{
ValueObjectPrinter child_printer(child_sp.get(),
m_stream,
child_options,
(IsPtr() || IsRef()) ? --curr_ptr_depth : curr_ptr_depth,
m_curr_depth + 1,
m_printed_instance_pointers);
child_printer.PrintValueObject();
}
}
uint32_t
ValueObjectPrinter::GetMaxNumChildrenToPrint (bool& print_dotdotdot)
{
ValueObject* synth_m_valobj = GetValueObjectForChildrenGeneration();
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;
}
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)
{
ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx, true));
if (child_sp)
{
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_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;
}