diff options
Diffstat (limited to 'lldb/source/DataFormatters/ValueObjectPrinter.cpp')
| -rw-r--r-- | lldb/source/DataFormatters/ValueObjectPrinter.cpp | 826 | 
1 files changed, 826 insertions, 0 deletions
diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp new file mode 100644 index 000000000000..fa43c677a194 --- /dev/null +++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -0,0 +1,826 @@ +//===-- ValueObjectPrinter.cpp -----------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/DataFormatters/ValueObjectPrinter.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" +#include "lldb/Utility/Stream.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) { +        ValueObject *non_synthetic = m_valobj->GetNonSyntheticValue().get(); +        if (non_synthetic) +          m_valobj = non_synthetic; +      } +    } else { +      if (m_options.m_use_synthetic) { +        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 || 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.GetString()); +    ConstString var_name_cstr(varName.GetString()); + +    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->PutCString(dest_stream.GetString()); +    } +  } + +  // if the helper failed, or there is none, do a default thing +  if (!decl_printed) { +    if (!typeName.Empty()) +      m_stream->Printf("(%s) ", typeName.GetData()); +    if (!varName.Empty()) +      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) { +    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 = nullptr; +    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_pointer_as_array) +    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 == nullptr || +           (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_pointer_as_array)) { +      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) { +        // If the description already ends with a \n don't add another one. +        size_t object_end = strlen(object_desc) - 1; +        if (object_desc[object_end] == '\n') +            m_stream->Printf("%s", object_desc); +        else +            m_stream->Printf("%s\n", object_desc); +        return true; +      } else if (!value_printed && !summary_printed) +        return true; +      else +        return false; +    } +  } +  return true; +} + +bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const { +  switch (m_mode) { +  case Mode::Always: +  case Mode::Default: +    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_pointer_as_array) +    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(); +    } + +    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_pointer_as_array) ? 1 : 0; +  const bool does_consume_ptr_depth = +      ((IsPtr() && !m_options.m_pointer_as_array) || 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_pointer_as_array) +    return m_options.m_pointer_as_array.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) { +    if (value_printed || summary_printed) +      return false; +  } + +  if (synth_m_valobj->MightHaveChildren()) +    return true; + +  if (m_val_summary_ok) +    return false; + +  return true; +} + +static constexpr size_t PhysicalIndexForLogicalIndex(size_t base, size_t stride, +                                                     size_t logical) { +  return base + logical * stride; +} + +ValueObjectSP ValueObjectPrinter::GenerateChild(ValueObject *synth_valobj, +                                                size_t idx) { +  if (m_options.m_pointer_as_array) { +    // if generating pointer-as-array children, use GetSyntheticArrayMember +    return synth_valobj->GetSyntheticArrayMember( +        PhysicalIndexForLogicalIndex( +            m_options.m_pointer_as_array.m_base_element, +            m_options.m_pointer_as_array.m_stride, 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::PrintableRepresentationSpecialCases::eDisable); +      } +    } + +    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); + +  DumpValueObjectOptions::PointerDepth curr_ptr_depth = m_ptr_depth; +  const bool print_children = +      ShouldPrintChildren(is_failed_description, curr_ptr_depth); +  const 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_pointer_as_array) || m_options.m_show_location) +          ? false +          : DataVisualization::ShouldPrintAsOneLiner(*m_valobj); +  if (print_children && IsInstancePointer()) { +    uint64_t 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"); +      return; +    } else { +      // Remember this guy for future reference. +      m_printed_instance_pointers->emplace(instance_ptr_value); +    } +  } + +  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; +}  | 
