1//===-- ValueObjectPrinter.cpp --------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/DataFormatters/ValueObjectPrinter.h"
10
11#include "lldb/Core/ValueObject.h"
12#include "lldb/DataFormatters/DataVisualization.h"
13#include "lldb/Interpreter/CommandInterpreter.h"
14#include "lldb/Target/Language.h"
15#include "lldb/Target/Target.h"
16#include "lldb/Utility/Stream.h"
17
18using namespace lldb;
19using namespace lldb_private;
20
21ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s) {
22  if (valobj) {
23    DumpValueObjectOptions options(*valobj);
24    Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
25  } else {
26    DumpValueObjectOptions options;
27    Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
28  }
29}
30
31ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s,
32                                       const DumpValueObjectOptions &options) {
33  Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
34}
35
36ValueObjectPrinter::ValueObjectPrinter(
37    ValueObject *valobj, Stream *s, const DumpValueObjectOptions &options,
38    const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth,
39    InstancePointersSetSP printed_instance_pointers) {
40  Init(valobj, s, options, ptr_depth, curr_depth, printed_instance_pointers);
41}
42
43void ValueObjectPrinter::Init(
44    ValueObject *valobj, Stream *s, const DumpValueObjectOptions &options,
45    const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth,
46    InstancePointersSetSP printed_instance_pointers) {
47  m_orig_valobj = valobj;
48  m_valobj = nullptr;
49  m_stream = s;
50  m_options = options;
51  m_ptr_depth = ptr_depth;
52  m_curr_depth = curr_depth;
53  assert(m_orig_valobj && "cannot print a NULL ValueObject");
54  assert(m_stream && "cannot print to a NULL Stream");
55  m_should_print = eLazyBoolCalculate;
56  m_is_nil = eLazyBoolCalculate;
57  m_is_uninit = eLazyBoolCalculate;
58  m_is_ptr = eLazyBoolCalculate;
59  m_is_ref = eLazyBoolCalculate;
60  m_is_aggregate = eLazyBoolCalculate;
61  m_is_instance_ptr = eLazyBoolCalculate;
62  m_summary_formatter = {nullptr, false};
63  m_value.assign("");
64  m_summary.assign("");
65  m_error.assign("");
66  m_val_summary_ok = false;
67  m_printed_instance_pointers =
68      printed_instance_pointers
69          ? printed_instance_pointers
70          : InstancePointersSetSP(new InstancePointersSet());
71}
72
73bool ValueObjectPrinter::PrintValueObject() {
74  if (!m_orig_valobj)
75    return false;
76
77  // If the incoming ValueObject is in an error state, the best we're going to
78  // get out of it is its type.  But if we don't even have that, just print
79  // the error and exit early.
80  if (m_orig_valobj->GetError().Fail()
81      && !m_orig_valobj->GetCompilerType().IsValid()) {
82    m_stream->Printf("Error: '%s'", m_orig_valobj->GetError().AsCString());
83    return true;
84  }
85
86  if (!GetMostSpecializedValue() || m_valobj == nullptr)
87    return false;
88
89  if (ShouldPrintValueObject()) {
90    PrintLocationIfNeeded();
91    m_stream->Indent();
92
93    PrintDecl();
94  }
95
96  bool value_printed = false;
97  bool summary_printed = false;
98
99  m_val_summary_ok =
100      PrintValueAndSummaryIfNeeded(value_printed, summary_printed);
101
102  if (m_val_summary_ok)
103    PrintChildrenIfNeeded(value_printed, summary_printed);
104  else
105    m_stream->EOL();
106
107  return true;
108}
109
110bool ValueObjectPrinter::GetMostSpecializedValue() {
111  if (m_valobj)
112    return true;
113  bool update_success = m_orig_valobj->UpdateValueIfNeeded(true);
114  if (!update_success) {
115    m_valobj = m_orig_valobj;
116  } else {
117    if (m_orig_valobj->IsDynamic()) {
118      if (m_options.m_use_dynamic == eNoDynamicValues) {
119        ValueObject *static_value = m_orig_valobj->GetStaticValue().get();
120        if (static_value)
121          m_valobj = static_value;
122        else
123          m_valobj = m_orig_valobj;
124      } else
125        m_valobj = m_orig_valobj;
126    } else {
127      if (m_options.m_use_dynamic != eNoDynamicValues) {
128        ValueObject *dynamic_value =
129            m_orig_valobj->GetDynamicValue(m_options.m_use_dynamic).get();
130        if (dynamic_value)
131          m_valobj = dynamic_value;
132        else
133          m_valobj = m_orig_valobj;
134      } else
135        m_valobj = m_orig_valobj;
136    }
137
138    if (m_valobj->IsSynthetic()) {
139      if (!m_options.m_use_synthetic) {
140        ValueObject *non_synthetic = m_valobj->GetNonSyntheticValue().get();
141        if (non_synthetic)
142          m_valobj = non_synthetic;
143      }
144    } else {
145      if (m_options.m_use_synthetic) {
146        ValueObject *synthetic = m_valobj->GetSyntheticValue().get();
147        if (synthetic)
148          m_valobj = synthetic;
149      }
150    }
151  }
152  m_compiler_type = m_valobj->GetCompilerType();
153  m_type_flags = m_compiler_type.GetTypeInfo();
154  return true;
155}
156
157const char *ValueObjectPrinter::GetDescriptionForDisplay() {
158  const char *str = m_valobj->GetObjectDescription();
159  if (!str)
160    str = m_valobj->GetSummaryAsCString();
161  if (!str)
162    str = m_valobj->GetValueAsCString();
163  return str;
164}
165
166const char *ValueObjectPrinter::GetRootNameForDisplay() {
167  const char *root_valobj_name = m_options.m_root_valobj_name.empty()
168                                     ? m_valobj->GetName().AsCString()
169                                     : m_options.m_root_valobj_name.c_str();
170  return root_valobj_name ? root_valobj_name : "";
171}
172
173bool ValueObjectPrinter::ShouldPrintValueObject() {
174  if (m_should_print == eLazyBoolCalculate)
175    m_should_print =
176        (!m_options.m_flat_output || m_type_flags.Test(eTypeHasValue))
177            ? eLazyBoolYes
178            : eLazyBoolNo;
179  return m_should_print == eLazyBoolYes;
180}
181
182bool ValueObjectPrinter::IsNil() {
183  if (m_is_nil == eLazyBoolCalculate)
184    m_is_nil = m_valobj->IsNilReference() ? eLazyBoolYes : eLazyBoolNo;
185  return m_is_nil == eLazyBoolYes;
186}
187
188bool ValueObjectPrinter::IsUninitialized() {
189  if (m_is_uninit == eLazyBoolCalculate)
190    m_is_uninit =
191        m_valobj->IsUninitializedReference() ? eLazyBoolYes : eLazyBoolNo;
192  return m_is_uninit == eLazyBoolYes;
193}
194
195bool ValueObjectPrinter::IsPtr() {
196  if (m_is_ptr == eLazyBoolCalculate)
197    m_is_ptr = m_type_flags.Test(eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo;
198  return m_is_ptr == eLazyBoolYes;
199}
200
201bool ValueObjectPrinter::IsRef() {
202  if (m_is_ref == eLazyBoolCalculate)
203    m_is_ref = m_type_flags.Test(eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo;
204  return m_is_ref == eLazyBoolYes;
205}
206
207bool ValueObjectPrinter::IsAggregate() {
208  if (m_is_aggregate == eLazyBoolCalculate)
209    m_is_aggregate =
210        m_type_flags.Test(eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo;
211  return m_is_aggregate == eLazyBoolYes;
212}
213
214bool ValueObjectPrinter::IsInstancePointer() {
215  // you need to do this check on the value's clang type
216  if (m_is_instance_ptr == eLazyBoolCalculate)
217    m_is_instance_ptr = (m_valobj->GetValue().GetCompilerType().GetTypeInfo() &
218                         eTypeInstanceIsPointer) != 0
219                            ? eLazyBoolYes
220                            : eLazyBoolNo;
221  if ((eLazyBoolYes == m_is_instance_ptr) && m_valobj->IsBaseClass())
222    m_is_instance_ptr = eLazyBoolNo;
223  return m_is_instance_ptr == eLazyBoolYes;
224}
225
226bool ValueObjectPrinter::PrintLocationIfNeeded() {
227  if (m_options.m_show_location) {
228    m_stream->Printf("%s: ", m_valobj->GetLocationAsCString());
229    return true;
230  }
231  return false;
232}
233
234void ValueObjectPrinter::PrintDecl() {
235  bool show_type = true;
236  // if we are at the root-level and been asked to hide the root's type, then
237  // hide it
238  if (m_curr_depth == 0 && m_options.m_hide_root_type)
239    show_type = false;
240  else
241    // otherwise decide according to the usual rules (asked to show types -
242    // always at the root level)
243    show_type = m_options.m_show_types ||
244                (m_curr_depth == 0 && !m_options.m_flat_output);
245
246  StreamString typeName;
247
248  // always show the type at the root level if it is invalid
249  if (show_type) {
250    // Some ValueObjects don't have types (like registers sets). Only print the
251    // type if there is one to print
252    ConstString type_name;
253    if (m_compiler_type.IsValid()) {
254      type_name = m_options.m_use_type_display_name
255                      ? m_valobj->GetDisplayTypeName()
256                      : m_valobj->GetQualifiedTypeName();
257    } else {
258      // only show an invalid type name if the user explicitly triggered
259      // show_type
260      if (m_options.m_show_types)
261        type_name = ConstString("<invalid type>");
262    }
263
264    if (type_name) {
265      std::string type_name_str(type_name.GetCString());
266      if (m_options.m_hide_pointer_value) {
267        for (auto iter = type_name_str.find(" *"); iter != std::string::npos;
268             iter = type_name_str.find(" *")) {
269          type_name_str.erase(iter, 2);
270        }
271      }
272      typeName << type_name_str.c_str();
273    }
274  }
275
276  StreamString varName;
277
278  if (ShouldShowName()) {
279    if (m_options.m_flat_output)
280      m_valobj->GetExpressionPath(varName);
281    else
282      varName << GetRootNameForDisplay();
283  }
284
285  bool decl_printed = false;
286  if (!m_options.m_decl_printing_helper) {
287    // if the user didn't give us a custom helper, pick one based upon the
288    // language, either the one that this printer is bound to, or the preferred
289    // one for the ValueObject
290    lldb::LanguageType lang_type =
291        (m_options.m_varformat_language == lldb::eLanguageTypeUnknown)
292            ? m_valobj->GetPreferredDisplayLanguage()
293            : m_options.m_varformat_language;
294    if (Language *lang_plugin = Language::FindPlugin(lang_type)) {
295      m_options.m_decl_printing_helper = lang_plugin->GetDeclPrintingHelper();
296    }
297  }
298
299  if (m_options.m_decl_printing_helper) {
300    ConstString type_name_cstr(typeName.GetString());
301    ConstString var_name_cstr(varName.GetString());
302
303    DumpValueObjectOptions decl_print_options = m_options;
304    // Pass printing helpers an option object that indicates whether the name
305    // should be shown or hidden.
306    decl_print_options.SetHideName(!ShouldShowName());
307
308    StreamString dest_stream;
309    if (m_options.m_decl_printing_helper(type_name_cstr, var_name_cstr,
310                                         decl_print_options, dest_stream)) {
311      decl_printed = true;
312      m_stream->PutCString(dest_stream.GetString());
313    }
314  }
315
316  // if the helper failed, or there is none, do a default thing
317  if (!decl_printed) {
318    if (!typeName.Empty())
319      m_stream->Printf("(%s) ", typeName.GetData());
320    if (!varName.Empty())
321      m_stream->Printf("%s =", varName.GetData());
322    else if (ShouldShowName())
323      m_stream->Printf(" =");
324  }
325}
326
327bool ValueObjectPrinter::CheckScopeIfNeeded() {
328  if (m_options.m_scope_already_checked)
329    return true;
330  return m_valobj->IsInScope();
331}
332
333TypeSummaryImpl *ValueObjectPrinter::GetSummaryFormatter(bool null_if_omitted) {
334  if (!m_summary_formatter.second) {
335    TypeSummaryImpl *entry = m_options.m_summary_sp
336                                 ? m_options.m_summary_sp.get()
337                                 : m_valobj->GetSummaryFormat().get();
338
339    if (m_options.m_omit_summary_depth > 0)
340      entry = nullptr;
341    m_summary_formatter.first = entry;
342    m_summary_formatter.second = true;
343  }
344  if (m_options.m_omit_summary_depth > 0 && null_if_omitted)
345    return nullptr;
346  return m_summary_formatter.first;
347}
348
349static bool IsPointerValue(const CompilerType &type) {
350  Flags type_flags(type.GetTypeInfo());
351  if (type_flags.AnySet(eTypeInstanceIsPointer | eTypeIsPointer))
352    return type_flags.AllClear(eTypeIsBuiltIn);
353  return false;
354}
355
356void ValueObjectPrinter::GetValueSummaryError(std::string &value,
357                                              std::string &summary,
358                                              std::string &error) {
359  lldb::Format format = m_options.m_format;
360  // if I am printing synthetized elements, apply the format to those elements
361  // only
362  if (m_options.m_pointer_as_array)
363    m_valobj->GetValueAsCString(lldb::eFormatDefault, value);
364  else if (format != eFormatDefault && format != m_valobj->GetFormat())
365    m_valobj->GetValueAsCString(format, value);
366  else {
367    const char *val_cstr = m_valobj->GetValueAsCString();
368    if (val_cstr)
369      value.assign(val_cstr);
370  }
371  const char *err_cstr = m_valobj->GetError().AsCString();
372  if (err_cstr)
373    error.assign(err_cstr);
374
375  if (!ShouldPrintValueObject())
376    return;
377
378  if (IsNil()) {
379    lldb::LanguageType lang_type =
380        (m_options.m_varformat_language == lldb::eLanguageTypeUnknown)
381            ? m_valobj->GetPreferredDisplayLanguage()
382            : m_options.m_varformat_language;
383    if (Language *lang_plugin = Language::FindPlugin(lang_type)) {
384      summary.assign(lang_plugin->GetNilReferenceSummaryString().str());
385    } else {
386      // We treat C as the fallback language rather than as a separate Language
387      // plugin.
388      summary.assign("NULL");
389    }
390  } else if (IsUninitialized()) {
391    summary.assign("<uninitialized>");
392  } else if (m_options.m_omit_summary_depth == 0) {
393    TypeSummaryImpl *entry = GetSummaryFormatter();
394    if (entry) {
395      m_valobj->GetSummaryAsCString(entry, summary,
396                                    m_options.m_varformat_language);
397    } else {
398      const char *sum_cstr =
399          m_valobj->GetSummaryAsCString(m_options.m_varformat_language);
400      if (sum_cstr)
401        summary.assign(sum_cstr);
402    }
403  }
404}
405
406bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed,
407                                                      bool &summary_printed) {
408  bool error_printed = false;
409  if (ShouldPrintValueObject()) {
410    if (!CheckScopeIfNeeded())
411      m_error.assign("out of scope");
412    if (m_error.empty()) {
413      GetValueSummaryError(m_value, m_summary, m_error);
414    }
415    if (m_error.size()) {
416      // we need to support scenarios in which it is actually fine for a value
417      // to have no type but - on the other hand - if we get an error *AND*
418      // have no type, we try to get out gracefully, since most often that
419      // combination means "could not resolve a type" and the default failure
420      // mode is quite ugly
421      if (!m_compiler_type.IsValid()) {
422        m_stream->Printf(" <could not resolve type>");
423        return false;
424      }
425
426      error_printed = true;
427      m_stream->Printf(" <%s>\n", m_error.c_str());
428    } else {
429      // Make sure we have a value and make sure the summary didn't specify
430      // that the value should not be printed - and do not print the value if
431      // this thing is nil (but show the value if the user passes a format
432      // explicitly)
433      TypeSummaryImpl *entry = GetSummaryFormatter();
434      const bool has_nil_or_uninitialized_summary =
435          (IsNil() || IsUninitialized()) && !m_summary.empty();
436      if (!has_nil_or_uninitialized_summary && !m_value.empty() &&
437          (entry == nullptr ||
438           (entry->DoesPrintValue(m_valobj) ||
439            m_options.m_format != eFormatDefault) ||
440           m_summary.empty()) &&
441          !m_options.m_hide_value) {
442        if (m_options.m_hide_pointer_value &&
443            IsPointerValue(m_valobj->GetCompilerType())) {
444        } else {
445          if (ShouldShowName())
446            m_stream->PutChar(' ');
447          m_stream->PutCString(m_value);
448          value_printed = true;
449        }
450      }
451
452      if (m_summary.size()) {
453        if (ShouldShowName() || value_printed)
454          m_stream->PutChar(' ');
455        m_stream->PutCString(m_summary);
456        summary_printed = true;
457      }
458    }
459  }
460  return !error_printed;
461}
462
463bool ValueObjectPrinter::PrintObjectDescriptionIfNeeded(bool value_printed,
464                                                        bool summary_printed) {
465  if (ShouldPrintValueObject()) {
466    // let's avoid the overly verbose no description error for a nil thing
467    if (m_options.m_use_objc && !IsNil() && !IsUninitialized() &&
468        (!m_options.m_pointer_as_array)) {
469      if (!m_options.m_hide_value || ShouldShowName())
470        m_stream->Printf(" ");
471      const char *object_desc = nullptr;
472      if (value_printed || summary_printed)
473        object_desc = m_valobj->GetObjectDescription();
474      else
475        object_desc = GetDescriptionForDisplay();
476      if (object_desc && *object_desc) {
477        // If the description already ends with a \n don't add another one.
478        size_t object_end = strlen(object_desc) - 1;
479        if (object_desc[object_end] == '\n')
480          m_stream->Printf("%s", object_desc);
481        else
482          m_stream->Printf("%s\n", object_desc);
483        return true;
484      } else if (!value_printed && !summary_printed)
485        return true;
486      else
487        return false;
488    }
489  }
490  return true;
491}
492
493bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const {
494  switch (m_mode) {
495  case Mode::Always:
496  case Mode::Default:
497    return m_count > 0;
498  case Mode::Never:
499    return false;
500  }
501  return false;
502}
503
504bool ValueObjectPrinter::ShouldPrintChildren(
505    DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
506  const bool is_ref = IsRef();
507  const bool is_ptr = IsPtr();
508  const bool is_uninit = IsUninitialized();
509
510  if (is_uninit)
511    return false;
512
513  // If we have reached the maximum depth we shouldn't print any more children.
514  if (HasReachedMaximumDepth())
515    return false;
516
517  // if the user has specified an element count, always print children as it is
518  // explicit user demand being honored
519  if (m_options.m_pointer_as_array)
520    return true;
521
522  if (m_options.m_use_objc)
523    return false;
524
525  bool print_children = true;
526  if (TypeSummaryImpl *type_summary = GetSummaryFormatter())
527    print_children = type_summary->DoesPrintChildren(m_valobj);
528
529  // We will show children for all concrete types. We won't show pointer
530  // contents unless a pointer depth has been specified. We won't reference
531  // contents unless the reference is the root object (depth of zero).
532
533  // Use a new temporary pointer depth in case we override the current
534  // pointer depth below...
535
536  if (is_ptr || is_ref) {
537    // We have a pointer or reference whose value is an address. Make sure
538    // that address is not NULL
539    AddressType ptr_address_type;
540    if (m_valobj->GetPointerValue(&ptr_address_type) == 0)
541      return false;
542
543    const bool is_root_level = m_curr_depth == 0;
544
545    if (is_ref && is_root_level && print_children) {
546      // If this is the root object (depth is zero) that we are showing and
547      // it is a reference, and no pointer depth has been supplied print out
548      // what it references. Don't do this at deeper depths otherwise we can
549      // end up with infinite recursion...
550      return true;
551    }
552
553    return curr_ptr_depth.CanAllowExpansion();
554  }
555
556  return print_children || m_summary.empty();
557}
558
559bool ValueObjectPrinter::ShouldExpandEmptyAggregates() {
560  TypeSummaryImpl *entry = GetSummaryFormatter();
561
562  if (!entry)
563    return true;
564
565  return entry->DoesPrintEmptyAggregates();
566}
567
568ValueObject *ValueObjectPrinter::GetValueObjectForChildrenGeneration() {
569  return m_valobj;
570}
571
572void ValueObjectPrinter::PrintChildrenPreamble(bool value_printed,
573                                               bool summary_printed) {
574  if (m_options.m_flat_output) {
575    if (ShouldPrintValueObject())
576      m_stream->EOL();
577  } else {
578    if (ShouldPrintValueObject()) {
579      if (IsRef()) {
580        m_stream->PutCString(": ");
581      } else if (value_printed || summary_printed || ShouldShowName()) {
582        m_stream->PutChar(' ');
583      }
584      m_stream->PutCString("{\n");
585    }
586    m_stream->IndentMore();
587  }
588}
589
590void ValueObjectPrinter::PrintChild(
591    ValueObjectSP child_sp,
592    const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
593  const uint32_t consumed_summary_depth = m_options.m_pointer_as_array ? 0 : 1;
594  const bool does_consume_ptr_depth =
595      ((IsPtr() && !m_options.m_pointer_as_array) || IsRef());
596
597  DumpValueObjectOptions child_options(m_options);
598  child_options.SetFormat(m_options.m_format)
599      .SetSummary()
600      .SetRootValueObjectName();
601  child_options.SetScopeChecked(true)
602      .SetHideName(m_options.m_hide_name)
603      .SetHideValue(m_options.m_hide_value)
604      .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1
605                               ? child_options.m_omit_summary_depth -
606                                     consumed_summary_depth
607                               : 0)
608      .SetElementCount(0);
609
610  if (child_sp.get()) {
611    auto ptr_depth = curr_ptr_depth;
612    if (does_consume_ptr_depth)
613      ptr_depth = curr_ptr_depth.Decremented();
614
615    ValueObjectPrinter child_printer(child_sp.get(), m_stream, child_options,
616                                     ptr_depth, m_curr_depth + 1,
617                                     m_printed_instance_pointers);
618    child_printer.PrintValueObject();
619  }
620}
621
622uint32_t ValueObjectPrinter::GetMaxNumChildrenToPrint(bool &print_dotdotdot) {
623  ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
624
625  if (m_options.m_pointer_as_array)
626    return m_options.m_pointer_as_array.m_element_count;
627
628  size_t num_children = synth_m_valobj->GetNumChildren();
629  print_dotdotdot = false;
630  if (num_children) {
631    const size_t max_num_children =
632        m_valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
633
634    if (num_children > max_num_children && !m_options.m_ignore_cap) {
635      print_dotdotdot = true;
636      return max_num_children;
637    }
638  }
639  return num_children;
640}
641
642void ValueObjectPrinter::PrintChildrenPostamble(bool print_dotdotdot) {
643  if (!m_options.m_flat_output) {
644    if (print_dotdotdot) {
645      m_valobj->GetTargetSP()
646          ->GetDebugger()
647          .GetCommandInterpreter()
648          .ChildrenTruncated();
649      m_stream->Indent("...\n");
650    }
651    m_stream->IndentLess();
652    m_stream->Indent("}\n");
653  }
654}
655
656bool ValueObjectPrinter::ShouldPrintEmptyBrackets(bool value_printed,
657                                                  bool summary_printed) {
658  ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
659
660  if (!IsAggregate())
661    return false;
662
663  if (!m_options.m_reveal_empty_aggregates) {
664    if (value_printed || summary_printed)
665      return false;
666  }
667
668  if (synth_m_valobj->MightHaveChildren())
669    return true;
670
671  if (m_val_summary_ok)
672    return false;
673
674  return true;
675}
676
677static constexpr size_t PhysicalIndexForLogicalIndex(size_t base, size_t stride,
678                                                     size_t logical) {
679  return base + logical * stride;
680}
681
682ValueObjectSP ValueObjectPrinter::GenerateChild(ValueObject *synth_valobj,
683                                                size_t idx) {
684  if (m_options.m_pointer_as_array) {
685    // if generating pointer-as-array children, use GetSyntheticArrayMember
686    return synth_valobj->GetSyntheticArrayMember(
687        PhysicalIndexForLogicalIndex(
688            m_options.m_pointer_as_array.m_base_element,
689            m_options.m_pointer_as_array.m_stride, idx),
690        true);
691  } else {
692    // otherwise, do the usual thing
693    return synth_valobj->GetChildAtIndex(idx);
694  }
695}
696
697void ValueObjectPrinter::PrintChildren(
698    bool value_printed, bool summary_printed,
699    const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
700  ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
701
702  bool print_dotdotdot = false;
703  size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
704  if (num_children) {
705    bool any_children_printed = false;
706
707    for (size_t idx = 0; idx < num_children; ++idx) {
708      if (ValueObjectSP child_sp = GenerateChild(synth_m_valobj, idx)) {
709        if (m_options.m_child_printing_decider &&
710            !m_options.m_child_printing_decider(child_sp->GetName()))
711          continue;
712        if (!any_children_printed) {
713          PrintChildrenPreamble(value_printed, summary_printed);
714          any_children_printed = true;
715        }
716        PrintChild(child_sp, curr_ptr_depth);
717      }
718    }
719
720    if (any_children_printed)
721      PrintChildrenPostamble(print_dotdotdot);
722    else {
723      if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) {
724        if (ShouldPrintValueObject())
725          m_stream->PutCString(" {}\n");
726        else
727          m_stream->EOL();
728      } else
729        m_stream->EOL();
730    }
731  } else if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) {
732    // Aggregate, no children...
733    if (ShouldPrintValueObject()) {
734      // if it has a synthetic value, then don't print {}, the synthetic
735      // children are probably only being used to vend a value
736      if (m_valobj->DoesProvideSyntheticValue() ||
737          !ShouldExpandEmptyAggregates())
738        m_stream->PutCString("\n");
739      else
740        m_stream->PutCString(" {}\n");
741    }
742  } else {
743    if (ShouldPrintValueObject())
744      m_stream->EOL();
745  }
746}
747
748bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) {
749  if (!GetMostSpecializedValue() || m_valobj == nullptr)
750    return false;
751
752  ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
753
754  bool print_dotdotdot = false;
755  size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
756
757  if (num_children) {
758    m_stream->PutChar('(');
759
760    bool did_print_children = false;
761    for (uint32_t idx = 0; idx < num_children; ++idx) {
762      lldb::ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx));
763      if (child_sp)
764        child_sp = child_sp->GetQualifiedRepresentationIfAvailable(
765            m_options.m_use_dynamic, m_options.m_use_synthetic);
766      if (child_sp) {
767        if (m_options.m_child_printing_decider &&
768            !m_options.m_child_printing_decider(child_sp->GetName()))
769          continue;
770        if (idx && did_print_children)
771          m_stream->PutCString(", ");
772        did_print_children = true;
773        if (!hide_names) {
774          const char *name = child_sp.get()->GetName().AsCString();
775          if (name && *name) {
776            m_stream->PutCString(name);
777            m_stream->PutCString(" = ");
778          }
779        }
780        child_sp->DumpPrintableRepresentation(
781            *m_stream, ValueObject::eValueObjectRepresentationStyleSummary,
782            m_options.m_format,
783            ValueObject::PrintableRepresentationSpecialCases::eDisable);
784      }
785    }
786
787    if (print_dotdotdot)
788      m_stream->PutCString(", ...)");
789    else
790      m_stream->PutChar(')');
791  }
792  return true;
793}
794
795void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed,
796                                               bool summary_printed) {
797  PrintObjectDescriptionIfNeeded(value_printed, summary_printed);
798
799  DumpValueObjectOptions::PointerDepth curr_ptr_depth = m_ptr_depth;
800  const bool print_children = ShouldPrintChildren(curr_ptr_depth);
801  const bool print_oneline =
802      (curr_ptr_depth.CanAllowExpansion() || m_options.m_show_types ||
803       !m_options.m_allow_oneliner_mode || m_options.m_flat_output ||
804       (m_options.m_pointer_as_array) || m_options.m_show_location)
805          ? false
806          : DataVisualization::ShouldPrintAsOneLiner(*m_valobj);
807  if (print_children && IsInstancePointer()) {
808    uint64_t instance_ptr_value = m_valobj->GetValueAsUnsigned(0);
809    if (m_printed_instance_pointers->count(instance_ptr_value)) {
810      // We already printed this instance-is-pointer thing, so don't expand it.
811      m_stream->PutCString(" {...}\n");
812      return;
813    } else {
814      // Remember this guy for future reference.
815      m_printed_instance_pointers->emplace(instance_ptr_value);
816    }
817  }
818
819  if (print_children) {
820    if (print_oneline) {
821      m_stream->PutChar(' ');
822      PrintChildrenOneLiner(false);
823      m_stream->EOL();
824    } else
825      PrintChildren(value_printed, summary_printed, curr_ptr_depth);
826  } else if (HasReachedMaximumDepth() && IsAggregate() &&
827             ShouldPrintValueObject()) {
828    m_stream->PutCString("{...}\n");
829    // The maximum child depth has been reached. If `m_max_depth` is the default
830    // (i.e. the user has _not_ customized it), then lldb presents a warning to
831    // the user. The warning tells the user that the limit has been reached, but
832    // more importantly tells them how to expand the limit if desired.
833    if (m_options.m_max_depth_is_default)
834      m_valobj->GetTargetSP()
835          ->GetDebugger()
836          .GetCommandInterpreter()
837          .SetReachedMaximumDepth();
838  } else
839    m_stream->EOL();
840}
841
842bool ValueObjectPrinter::HasReachedMaximumDepth() {
843  return m_curr_depth >= m_options.m_max_depth;
844}
845
846bool ValueObjectPrinter::ShouldShowName() const {
847  if (m_curr_depth == 0)
848    return !m_options.m_hide_root_name && !m_options.m_hide_name;
849  return !m_options.m_hide_name;
850}
851