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 
18 using namespace lldb;
19 using namespace lldb_private;
20 
21 ValueObjectPrinter::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 
31 ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s,
32                                        const DumpValueObjectOptions &options) {
33   Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
34 }
35 
36 ValueObjectPrinter::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 
43 void 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 
73 bool 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 
110 bool 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 
157 const 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 
166 const 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 
173 bool 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 
182 bool 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 
188 bool 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 
195 bool 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 
201 bool 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 
207 bool 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 
214 bool 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 
226 bool 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 
234 void 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 
327 bool ValueObjectPrinter::CheckScopeIfNeeded() {
328   if (m_options.m_scope_already_checked)
329     return true;
330   return m_valobj->IsInScope();
331 }
332 
333 TypeSummaryImpl *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 
349 static 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 
356 void 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 
406 bool 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 
463 bool 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 
493 bool 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 
504 bool 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 
559 bool ValueObjectPrinter::ShouldExpandEmptyAggregates() {
560   TypeSummaryImpl *entry = GetSummaryFormatter();
561 
562   if (!entry)
563     return true;
564 
565   return entry->DoesPrintEmptyAggregates();
566 }
567 
568 ValueObject *ValueObjectPrinter::GetValueObjectForChildrenGeneration() {
569   return m_valobj;
570 }
571 
572 void 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 
590 void 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 
622 uint32_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 
642 void 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 
656 bool 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 
677 static constexpr size_t PhysicalIndexForLogicalIndex(size_t base, size_t stride,
678                                                      size_t logical) {
679   return base + logical * stride;
680 }
681 
682 ValueObjectSP 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 
697 void 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 
748 bool 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 
795 void 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 
842 bool ValueObjectPrinter::HasReachedMaximumDepth() {
843   return m_curr_depth >= m_options.m_max_depth;
844 }
845 
846 bool 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