xref: /openbsd-src/gnu/llvm/lldb/source/DataFormatters/ValueObjectPrinter.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- ValueObjectPrinter.cpp --------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/DataFormatters/ValueObjectPrinter.h"
10061da546Spatrick 
11061da546Spatrick #include "lldb/Core/ValueObject.h"
12061da546Spatrick #include "lldb/DataFormatters/DataVisualization.h"
13061da546Spatrick #include "lldb/Interpreter/CommandInterpreter.h"
14061da546Spatrick #include "lldb/Target/Language.h"
15061da546Spatrick #include "lldb/Target/Target.h"
16061da546Spatrick #include "lldb/Utility/Stream.h"
17061da546Spatrick 
18061da546Spatrick using namespace lldb;
19061da546Spatrick using namespace lldb_private;
20061da546Spatrick 
ValueObjectPrinter(ValueObject * valobj,Stream * s)21061da546Spatrick ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s) {
22061da546Spatrick   if (valobj) {
23061da546Spatrick     DumpValueObjectOptions options(*valobj);
24061da546Spatrick     Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
25061da546Spatrick   } else {
26061da546Spatrick     DumpValueObjectOptions options;
27061da546Spatrick     Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
28061da546Spatrick   }
29061da546Spatrick }
30061da546Spatrick 
ValueObjectPrinter(ValueObject * valobj,Stream * s,const DumpValueObjectOptions & options)31061da546Spatrick ValueObjectPrinter::ValueObjectPrinter(ValueObject *valobj, Stream *s,
32061da546Spatrick                                        const DumpValueObjectOptions &options) {
33061da546Spatrick   Init(valobj, s, options, m_options.m_max_ptr_depth, 0, nullptr);
34061da546Spatrick }
35061da546Spatrick 
ValueObjectPrinter(ValueObject * valobj,Stream * s,const DumpValueObjectOptions & options,const DumpValueObjectOptions::PointerDepth & ptr_depth,uint32_t curr_depth,InstancePointersSetSP printed_instance_pointers)36061da546Spatrick ValueObjectPrinter::ValueObjectPrinter(
37061da546Spatrick     ValueObject *valobj, Stream *s, const DumpValueObjectOptions &options,
38061da546Spatrick     const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth,
39061da546Spatrick     InstancePointersSetSP printed_instance_pointers) {
40061da546Spatrick   Init(valobj, s, options, ptr_depth, curr_depth, printed_instance_pointers);
41061da546Spatrick }
42061da546Spatrick 
Init(ValueObject * valobj,Stream * s,const DumpValueObjectOptions & options,const DumpValueObjectOptions::PointerDepth & ptr_depth,uint32_t curr_depth,InstancePointersSetSP printed_instance_pointers)43061da546Spatrick void ValueObjectPrinter::Init(
44061da546Spatrick     ValueObject *valobj, Stream *s, const DumpValueObjectOptions &options,
45061da546Spatrick     const DumpValueObjectOptions::PointerDepth &ptr_depth, uint32_t curr_depth,
46061da546Spatrick     InstancePointersSetSP printed_instance_pointers) {
47061da546Spatrick   m_orig_valobj = valobj;
48061da546Spatrick   m_valobj = nullptr;
49061da546Spatrick   m_stream = s;
50061da546Spatrick   m_options = options;
51061da546Spatrick   m_ptr_depth = ptr_depth;
52061da546Spatrick   m_curr_depth = curr_depth;
53061da546Spatrick   assert(m_orig_valobj && "cannot print a NULL ValueObject");
54061da546Spatrick   assert(m_stream && "cannot print to a NULL Stream");
55061da546Spatrick   m_should_print = eLazyBoolCalculate;
56061da546Spatrick   m_is_nil = eLazyBoolCalculate;
57061da546Spatrick   m_is_uninit = eLazyBoolCalculate;
58061da546Spatrick   m_is_ptr = eLazyBoolCalculate;
59061da546Spatrick   m_is_ref = eLazyBoolCalculate;
60061da546Spatrick   m_is_aggregate = eLazyBoolCalculate;
61061da546Spatrick   m_is_instance_ptr = eLazyBoolCalculate;
62061da546Spatrick   m_summary_formatter = {nullptr, false};
63061da546Spatrick   m_value.assign("");
64061da546Spatrick   m_summary.assign("");
65061da546Spatrick   m_error.assign("");
66061da546Spatrick   m_val_summary_ok = false;
67061da546Spatrick   m_printed_instance_pointers =
68061da546Spatrick       printed_instance_pointers
69061da546Spatrick           ? printed_instance_pointers
70061da546Spatrick           : InstancePointersSetSP(new InstancePointersSet());
71061da546Spatrick }
72061da546Spatrick 
PrintValueObject()73061da546Spatrick bool ValueObjectPrinter::PrintValueObject() {
74061da546Spatrick   if (!GetMostSpecializedValue() || m_valobj == nullptr)
75061da546Spatrick     return false;
76061da546Spatrick 
77061da546Spatrick   if (ShouldPrintValueObject()) {
78061da546Spatrick     PrintLocationIfNeeded();
79061da546Spatrick     m_stream->Indent();
80061da546Spatrick 
81061da546Spatrick     PrintDecl();
82061da546Spatrick   }
83061da546Spatrick 
84061da546Spatrick   bool value_printed = false;
85061da546Spatrick   bool summary_printed = false;
86061da546Spatrick 
87061da546Spatrick   m_val_summary_ok =
88061da546Spatrick       PrintValueAndSummaryIfNeeded(value_printed, summary_printed);
89061da546Spatrick 
90061da546Spatrick   if (m_val_summary_ok)
91061da546Spatrick     PrintChildrenIfNeeded(value_printed, summary_printed);
92061da546Spatrick   else
93061da546Spatrick     m_stream->EOL();
94061da546Spatrick 
95061da546Spatrick   return true;
96061da546Spatrick }
97061da546Spatrick 
GetMostSpecializedValue()98061da546Spatrick bool ValueObjectPrinter::GetMostSpecializedValue() {
99061da546Spatrick   if (m_valobj)
100061da546Spatrick     return true;
101061da546Spatrick   bool update_success = m_orig_valobj->UpdateValueIfNeeded(true);
102061da546Spatrick   if (!update_success) {
103061da546Spatrick     m_valobj = m_orig_valobj;
104061da546Spatrick   } else {
105061da546Spatrick     if (m_orig_valobj->IsDynamic()) {
106061da546Spatrick       if (m_options.m_use_dynamic == eNoDynamicValues) {
107061da546Spatrick         ValueObject *static_value = m_orig_valobj->GetStaticValue().get();
108061da546Spatrick         if (static_value)
109061da546Spatrick           m_valobj = static_value;
110061da546Spatrick         else
111061da546Spatrick           m_valobj = m_orig_valobj;
112061da546Spatrick       } else
113061da546Spatrick         m_valobj = m_orig_valobj;
114061da546Spatrick     } else {
115061da546Spatrick       if (m_options.m_use_dynamic != eNoDynamicValues) {
116061da546Spatrick         ValueObject *dynamic_value =
117061da546Spatrick             m_orig_valobj->GetDynamicValue(m_options.m_use_dynamic).get();
118061da546Spatrick         if (dynamic_value)
119061da546Spatrick           m_valobj = dynamic_value;
120061da546Spatrick         else
121061da546Spatrick           m_valobj = m_orig_valobj;
122061da546Spatrick       } else
123061da546Spatrick         m_valobj = m_orig_valobj;
124061da546Spatrick     }
125061da546Spatrick 
126061da546Spatrick     if (m_valobj->IsSynthetic()) {
127061da546Spatrick       if (!m_options.m_use_synthetic) {
128061da546Spatrick         ValueObject *non_synthetic = m_valobj->GetNonSyntheticValue().get();
129061da546Spatrick         if (non_synthetic)
130061da546Spatrick           m_valobj = non_synthetic;
131061da546Spatrick       }
132061da546Spatrick     } else {
133061da546Spatrick       if (m_options.m_use_synthetic) {
134061da546Spatrick         ValueObject *synthetic = m_valobj->GetSyntheticValue().get();
135061da546Spatrick         if (synthetic)
136061da546Spatrick           m_valobj = synthetic;
137061da546Spatrick       }
138061da546Spatrick     }
139061da546Spatrick   }
140061da546Spatrick   m_compiler_type = m_valobj->GetCompilerType();
141061da546Spatrick   m_type_flags = m_compiler_type.GetTypeInfo();
142061da546Spatrick   return true;
143061da546Spatrick }
144061da546Spatrick 
GetDescriptionForDisplay()145061da546Spatrick const char *ValueObjectPrinter::GetDescriptionForDisplay() {
146061da546Spatrick   const char *str = m_valobj->GetObjectDescription();
147061da546Spatrick   if (!str)
148061da546Spatrick     str = m_valobj->GetSummaryAsCString();
149061da546Spatrick   if (!str)
150061da546Spatrick     str = m_valobj->GetValueAsCString();
151061da546Spatrick   return str;
152061da546Spatrick }
153061da546Spatrick 
GetRootNameForDisplay()154dda28197Spatrick const char *ValueObjectPrinter::GetRootNameForDisplay() {
155061da546Spatrick   const char *root_valobj_name = m_options.m_root_valobj_name.empty()
156061da546Spatrick                                      ? m_valobj->GetName().AsCString()
157061da546Spatrick                                      : m_options.m_root_valobj_name.c_str();
158dda28197Spatrick   return root_valobj_name ? root_valobj_name : "";
159061da546Spatrick }
160061da546Spatrick 
ShouldPrintValueObject()161061da546Spatrick bool ValueObjectPrinter::ShouldPrintValueObject() {
162061da546Spatrick   if (m_should_print == eLazyBoolCalculate)
163061da546Spatrick     m_should_print =
164061da546Spatrick         (!m_options.m_flat_output || m_type_flags.Test(eTypeHasValue))
165061da546Spatrick             ? eLazyBoolYes
166061da546Spatrick             : eLazyBoolNo;
167061da546Spatrick   return m_should_print == eLazyBoolYes;
168061da546Spatrick }
169061da546Spatrick 
IsNil()170061da546Spatrick bool ValueObjectPrinter::IsNil() {
171061da546Spatrick   if (m_is_nil == eLazyBoolCalculate)
172061da546Spatrick     m_is_nil = m_valobj->IsNilReference() ? eLazyBoolYes : eLazyBoolNo;
173061da546Spatrick   return m_is_nil == eLazyBoolYes;
174061da546Spatrick }
175061da546Spatrick 
IsUninitialized()176061da546Spatrick bool ValueObjectPrinter::IsUninitialized() {
177061da546Spatrick   if (m_is_uninit == eLazyBoolCalculate)
178061da546Spatrick     m_is_uninit =
179061da546Spatrick         m_valobj->IsUninitializedReference() ? eLazyBoolYes : eLazyBoolNo;
180061da546Spatrick   return m_is_uninit == eLazyBoolYes;
181061da546Spatrick }
182061da546Spatrick 
IsPtr()183061da546Spatrick bool ValueObjectPrinter::IsPtr() {
184061da546Spatrick   if (m_is_ptr == eLazyBoolCalculate)
185061da546Spatrick     m_is_ptr = m_type_flags.Test(eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo;
186061da546Spatrick   return m_is_ptr == eLazyBoolYes;
187061da546Spatrick }
188061da546Spatrick 
IsRef()189061da546Spatrick bool ValueObjectPrinter::IsRef() {
190061da546Spatrick   if (m_is_ref == eLazyBoolCalculate)
191061da546Spatrick     m_is_ref = m_type_flags.Test(eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo;
192061da546Spatrick   return m_is_ref == eLazyBoolYes;
193061da546Spatrick }
194061da546Spatrick 
IsAggregate()195061da546Spatrick bool ValueObjectPrinter::IsAggregate() {
196061da546Spatrick   if (m_is_aggregate == eLazyBoolCalculate)
197061da546Spatrick     m_is_aggregate =
198061da546Spatrick         m_type_flags.Test(eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo;
199061da546Spatrick   return m_is_aggregate == eLazyBoolYes;
200061da546Spatrick }
201061da546Spatrick 
IsInstancePointer()202061da546Spatrick bool ValueObjectPrinter::IsInstancePointer() {
203061da546Spatrick   // you need to do this check on the value's clang type
204061da546Spatrick   if (m_is_instance_ptr == eLazyBoolCalculate)
205061da546Spatrick     m_is_instance_ptr = (m_valobj->GetValue().GetCompilerType().GetTypeInfo() &
206061da546Spatrick                          eTypeInstanceIsPointer) != 0
207061da546Spatrick                             ? eLazyBoolYes
208061da546Spatrick                             : eLazyBoolNo;
209061da546Spatrick   if ((eLazyBoolYes == m_is_instance_ptr) && m_valobj->IsBaseClass())
210061da546Spatrick     m_is_instance_ptr = eLazyBoolNo;
211061da546Spatrick   return m_is_instance_ptr == eLazyBoolYes;
212061da546Spatrick }
213061da546Spatrick 
PrintLocationIfNeeded()214061da546Spatrick bool ValueObjectPrinter::PrintLocationIfNeeded() {
215061da546Spatrick   if (m_options.m_show_location) {
216061da546Spatrick     m_stream->Printf("%s: ", m_valobj->GetLocationAsCString());
217061da546Spatrick     return true;
218061da546Spatrick   }
219061da546Spatrick   return false;
220061da546Spatrick }
221061da546Spatrick 
PrintDecl()222061da546Spatrick void ValueObjectPrinter::PrintDecl() {
223061da546Spatrick   bool show_type = true;
224061da546Spatrick   // if we are at the root-level and been asked to hide the root's type, then
225061da546Spatrick   // hide it
226061da546Spatrick   if (m_curr_depth == 0 && m_options.m_hide_root_type)
227061da546Spatrick     show_type = false;
228061da546Spatrick   else
229061da546Spatrick     // otherwise decide according to the usual rules (asked to show types -
230061da546Spatrick     // always at the root level)
231061da546Spatrick     show_type = m_options.m_show_types ||
232061da546Spatrick                 (m_curr_depth == 0 && !m_options.m_flat_output);
233061da546Spatrick 
234061da546Spatrick   StreamString typeName;
235061da546Spatrick 
236061da546Spatrick   // always show the type at the root level if it is invalid
237061da546Spatrick   if (show_type) {
238061da546Spatrick     // Some ValueObjects don't have types (like registers sets). Only print the
239061da546Spatrick     // type if there is one to print
240061da546Spatrick     ConstString type_name;
241061da546Spatrick     if (m_compiler_type.IsValid()) {
242dda28197Spatrick       type_name = m_options.m_use_type_display_name
243dda28197Spatrick                       ? m_valobj->GetDisplayTypeName()
244dda28197Spatrick                       : m_valobj->GetQualifiedTypeName();
245061da546Spatrick     } else {
246061da546Spatrick       // only show an invalid type name if the user explicitly triggered
247061da546Spatrick       // show_type
248061da546Spatrick       if (m_options.m_show_types)
249061da546Spatrick         type_name = ConstString("<invalid type>");
250061da546Spatrick     }
251061da546Spatrick 
252061da546Spatrick     if (type_name) {
253061da546Spatrick       std::string type_name_str(type_name.GetCString());
254061da546Spatrick       if (m_options.m_hide_pointer_value) {
255061da546Spatrick         for (auto iter = type_name_str.find(" *"); iter != std::string::npos;
256061da546Spatrick              iter = type_name_str.find(" *")) {
257061da546Spatrick           type_name_str.erase(iter, 2);
258061da546Spatrick         }
259061da546Spatrick       }
260dda28197Spatrick       typeName << type_name_str.c_str();
261061da546Spatrick     }
262061da546Spatrick   }
263061da546Spatrick 
264061da546Spatrick   StreamString varName;
265061da546Spatrick 
266061da546Spatrick   if (!m_options.m_hide_name) {
267dda28197Spatrick     if (m_options.m_flat_output)
268dda28197Spatrick       m_valobj->GetExpressionPath(varName);
269dda28197Spatrick     else
270dda28197Spatrick       varName << GetRootNameForDisplay();
271061da546Spatrick   }
272061da546Spatrick 
273061da546Spatrick   bool decl_printed = false;
274061da546Spatrick   if (!m_options.m_decl_printing_helper) {
275061da546Spatrick     // if the user didn't give us a custom helper, pick one based upon the
276061da546Spatrick     // language, either the one that this printer is bound to, or the preferred
277061da546Spatrick     // one for the ValueObject
278061da546Spatrick     lldb::LanguageType lang_type =
279061da546Spatrick         (m_options.m_varformat_language == lldb::eLanguageTypeUnknown)
280061da546Spatrick             ? m_valobj->GetPreferredDisplayLanguage()
281061da546Spatrick             : m_options.m_varformat_language;
282061da546Spatrick     if (Language *lang_plugin = Language::FindPlugin(lang_type)) {
283061da546Spatrick       m_options.m_decl_printing_helper = lang_plugin->GetDeclPrintingHelper();
284061da546Spatrick     }
285061da546Spatrick   }
286061da546Spatrick 
287061da546Spatrick   if (m_options.m_decl_printing_helper) {
288061da546Spatrick     ConstString type_name_cstr(typeName.GetString());
289061da546Spatrick     ConstString var_name_cstr(varName.GetString());
290061da546Spatrick 
291061da546Spatrick     StreamString dest_stream;
292061da546Spatrick     if (m_options.m_decl_printing_helper(type_name_cstr, var_name_cstr,
293061da546Spatrick                                          m_options, dest_stream)) {
294061da546Spatrick       decl_printed = true;
295061da546Spatrick       m_stream->PutCString(dest_stream.GetString());
296061da546Spatrick     }
297061da546Spatrick   }
298061da546Spatrick 
299061da546Spatrick   // if the helper failed, or there is none, do a default thing
300061da546Spatrick   if (!decl_printed) {
301061da546Spatrick     if (!typeName.Empty())
302061da546Spatrick       m_stream->Printf("(%s) ", typeName.GetData());
303061da546Spatrick     if (!varName.Empty())
304061da546Spatrick       m_stream->Printf("%s =", varName.GetData());
305061da546Spatrick     else if (!m_options.m_hide_name)
306061da546Spatrick       m_stream->Printf(" =");
307061da546Spatrick   }
308061da546Spatrick }
309061da546Spatrick 
CheckScopeIfNeeded()310061da546Spatrick bool ValueObjectPrinter::CheckScopeIfNeeded() {
311061da546Spatrick   if (m_options.m_scope_already_checked)
312061da546Spatrick     return true;
313061da546Spatrick   return m_valobj->IsInScope();
314061da546Spatrick }
315061da546Spatrick 
GetSummaryFormatter(bool null_if_omitted)316061da546Spatrick TypeSummaryImpl *ValueObjectPrinter::GetSummaryFormatter(bool null_if_omitted) {
317061da546Spatrick   if (!m_summary_formatter.second) {
318061da546Spatrick     TypeSummaryImpl *entry = m_options.m_summary_sp
319061da546Spatrick                                  ? m_options.m_summary_sp.get()
320061da546Spatrick                                  : m_valobj->GetSummaryFormat().get();
321061da546Spatrick 
322061da546Spatrick     if (m_options.m_omit_summary_depth > 0)
323061da546Spatrick       entry = nullptr;
324061da546Spatrick     m_summary_formatter.first = entry;
325061da546Spatrick     m_summary_formatter.second = true;
326061da546Spatrick   }
327061da546Spatrick   if (m_options.m_omit_summary_depth > 0 && null_if_omitted)
328061da546Spatrick     return nullptr;
329061da546Spatrick   return m_summary_formatter.first;
330061da546Spatrick }
331061da546Spatrick 
IsPointerValue(const CompilerType & type)332061da546Spatrick static bool IsPointerValue(const CompilerType &type) {
333061da546Spatrick   Flags type_flags(type.GetTypeInfo());
334061da546Spatrick   if (type_flags.AnySet(eTypeInstanceIsPointer | eTypeIsPointer))
335061da546Spatrick     return type_flags.AllClear(eTypeIsBuiltIn);
336061da546Spatrick   return false;
337061da546Spatrick }
338061da546Spatrick 
GetValueSummaryError(std::string & value,std::string & summary,std::string & error)339061da546Spatrick void ValueObjectPrinter::GetValueSummaryError(std::string &value,
340061da546Spatrick                                               std::string &summary,
341061da546Spatrick                                               std::string &error) {
342061da546Spatrick   lldb::Format format = m_options.m_format;
343061da546Spatrick   // if I am printing synthetized elements, apply the format to those elements
344061da546Spatrick   // only
345061da546Spatrick   if (m_options.m_pointer_as_array)
346061da546Spatrick     m_valobj->GetValueAsCString(lldb::eFormatDefault, value);
347061da546Spatrick   else if (format != eFormatDefault && format != m_valobj->GetFormat())
348061da546Spatrick     m_valobj->GetValueAsCString(format, value);
349061da546Spatrick   else {
350061da546Spatrick     const char *val_cstr = m_valobj->GetValueAsCString();
351061da546Spatrick     if (val_cstr)
352061da546Spatrick       value.assign(val_cstr);
353061da546Spatrick   }
354061da546Spatrick   const char *err_cstr = m_valobj->GetError().AsCString();
355061da546Spatrick   if (err_cstr)
356061da546Spatrick     error.assign(err_cstr);
357061da546Spatrick 
358be691f3bSpatrick   if (!ShouldPrintValueObject())
359be691f3bSpatrick     return;
360be691f3bSpatrick 
361be691f3bSpatrick   if (IsNil()) {
362be691f3bSpatrick     lldb::LanguageType lang_type =
363be691f3bSpatrick         (m_options.m_varformat_language == lldb::eLanguageTypeUnknown)
364be691f3bSpatrick             ? m_valobj->GetPreferredDisplayLanguage()
365be691f3bSpatrick             : m_options.m_varformat_language;
366be691f3bSpatrick     if (Language *lang_plugin = Language::FindPlugin(lang_type)) {
367be691f3bSpatrick       summary.assign(lang_plugin->GetNilReferenceSummaryString().str());
368be691f3bSpatrick     } else {
369be691f3bSpatrick       // We treat C as the fallback language rather than as a separate Language
370be691f3bSpatrick       // plugin.
371be691f3bSpatrick       summary.assign("NULL");
372be691f3bSpatrick     }
373be691f3bSpatrick   } else if (IsUninitialized()) {
374061da546Spatrick     summary.assign("<uninitialized>");
375be691f3bSpatrick   } else if (m_options.m_omit_summary_depth == 0) {
376061da546Spatrick     TypeSummaryImpl *entry = GetSummaryFormatter();
377be691f3bSpatrick     if (entry) {
378061da546Spatrick       m_valobj->GetSummaryAsCString(entry, summary,
379061da546Spatrick                                     m_options.m_varformat_language);
380be691f3bSpatrick     } else {
381061da546Spatrick       const char *sum_cstr =
382061da546Spatrick           m_valobj->GetSummaryAsCString(m_options.m_varformat_language);
383061da546Spatrick       if (sum_cstr)
384061da546Spatrick         summary.assign(sum_cstr);
385061da546Spatrick     }
386061da546Spatrick   }
387061da546Spatrick }
388061da546Spatrick 
PrintValueAndSummaryIfNeeded(bool & value_printed,bool & summary_printed)389061da546Spatrick bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed,
390061da546Spatrick                                                       bool &summary_printed) {
391061da546Spatrick   bool error_printed = false;
392061da546Spatrick   if (ShouldPrintValueObject()) {
393061da546Spatrick     if (!CheckScopeIfNeeded())
394061da546Spatrick       m_error.assign("out of scope");
395061da546Spatrick     if (m_error.empty()) {
396061da546Spatrick       GetValueSummaryError(m_value, m_summary, m_error);
397061da546Spatrick     }
398061da546Spatrick     if (m_error.size()) {
399061da546Spatrick       // we need to support scenarios in which it is actually fine for a value
400061da546Spatrick       // to have no type but - on the other hand - if we get an error *AND*
401061da546Spatrick       // have no type, we try to get out gracefully, since most often that
402061da546Spatrick       // combination means "could not resolve a type" and the default failure
403061da546Spatrick       // mode is quite ugly
404061da546Spatrick       if (!m_compiler_type.IsValid()) {
405061da546Spatrick         m_stream->Printf(" <could not resolve type>");
406061da546Spatrick         return false;
407061da546Spatrick       }
408061da546Spatrick 
409061da546Spatrick       error_printed = true;
410061da546Spatrick       m_stream->Printf(" <%s>\n", m_error.c_str());
411061da546Spatrick     } else {
412061da546Spatrick       // Make sure we have a value and make sure the summary didn't specify
413061da546Spatrick       // that the value should not be printed - and do not print the value if
414061da546Spatrick       // this thing is nil (but show the value if the user passes a format
415061da546Spatrick       // explicitly)
416061da546Spatrick       TypeSummaryImpl *entry = GetSummaryFormatter();
417be691f3bSpatrick       const bool has_nil_or_uninitialized_summary =
418be691f3bSpatrick           (IsNil() || IsUninitialized()) && !m_summary.empty();
419be691f3bSpatrick       if (!has_nil_or_uninitialized_summary && !m_value.empty() &&
420061da546Spatrick           (entry == nullptr ||
421061da546Spatrick            (entry->DoesPrintValue(m_valobj) ||
422061da546Spatrick             m_options.m_format != eFormatDefault) ||
423061da546Spatrick            m_summary.empty()) &&
424061da546Spatrick           !m_options.m_hide_value) {
425061da546Spatrick         if (m_options.m_hide_pointer_value &&
426061da546Spatrick             IsPointerValue(m_valobj->GetCompilerType())) {
427061da546Spatrick         } else {
428061da546Spatrick           m_stream->Printf(" %s", m_value.c_str());
429061da546Spatrick           value_printed = true;
430061da546Spatrick         }
431061da546Spatrick       }
432061da546Spatrick 
433061da546Spatrick       if (m_summary.size()) {
434061da546Spatrick         m_stream->Printf(" %s", m_summary.c_str());
435061da546Spatrick         summary_printed = true;
436061da546Spatrick       }
437061da546Spatrick     }
438061da546Spatrick   }
439061da546Spatrick   return !error_printed;
440061da546Spatrick }
441061da546Spatrick 
PrintObjectDescriptionIfNeeded(bool value_printed,bool summary_printed)442061da546Spatrick bool ValueObjectPrinter::PrintObjectDescriptionIfNeeded(bool value_printed,
443061da546Spatrick                                                         bool summary_printed) {
444061da546Spatrick   if (ShouldPrintValueObject()) {
445061da546Spatrick     // let's avoid the overly verbose no description error for a nil thing
446061da546Spatrick     if (m_options.m_use_objc && !IsNil() && !IsUninitialized() &&
447061da546Spatrick         (!m_options.m_pointer_as_array)) {
448061da546Spatrick       if (!m_options.m_hide_value || !m_options.m_hide_name)
449061da546Spatrick         m_stream->Printf(" ");
450061da546Spatrick       const char *object_desc = nullptr;
451061da546Spatrick       if (value_printed || summary_printed)
452061da546Spatrick         object_desc = m_valobj->GetObjectDescription();
453061da546Spatrick       else
454061da546Spatrick         object_desc = GetDescriptionForDisplay();
455061da546Spatrick       if (object_desc && *object_desc) {
456061da546Spatrick         // If the description already ends with a \n don't add another one.
457061da546Spatrick         size_t object_end = strlen(object_desc) - 1;
458061da546Spatrick         if (object_desc[object_end] == '\n')
459061da546Spatrick           m_stream->Printf("%s", object_desc);
460061da546Spatrick         else
461061da546Spatrick           m_stream->Printf("%s\n", object_desc);
462061da546Spatrick         return true;
463061da546Spatrick       } else if (!value_printed && !summary_printed)
464061da546Spatrick         return true;
465061da546Spatrick       else
466061da546Spatrick         return false;
467061da546Spatrick     }
468061da546Spatrick   }
469061da546Spatrick   return true;
470061da546Spatrick }
471061da546Spatrick 
CanAllowExpansion() const472061da546Spatrick bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const {
473061da546Spatrick   switch (m_mode) {
474061da546Spatrick   case Mode::Always:
475061da546Spatrick   case Mode::Default:
476061da546Spatrick     return m_count > 0;
477061da546Spatrick   case Mode::Never:
478061da546Spatrick     return false;
479061da546Spatrick   }
480061da546Spatrick   return false;
481061da546Spatrick }
482061da546Spatrick 
ShouldPrintChildren(bool is_failed_description,DumpValueObjectOptions::PointerDepth & curr_ptr_depth)483061da546Spatrick bool ValueObjectPrinter::ShouldPrintChildren(
484061da546Spatrick     bool is_failed_description,
485061da546Spatrick     DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
486061da546Spatrick   const bool is_ref = IsRef();
487061da546Spatrick   const bool is_ptr = IsPtr();
488061da546Spatrick   const bool is_uninit = IsUninitialized();
489061da546Spatrick 
490061da546Spatrick   if (is_uninit)
491061da546Spatrick     return false;
492061da546Spatrick 
493061da546Spatrick   // if the user has specified an element count, always print children as it is
494061da546Spatrick   // explicit user demand being honored
495061da546Spatrick   if (m_options.m_pointer_as_array)
496061da546Spatrick     return true;
497061da546Spatrick 
498061da546Spatrick   TypeSummaryImpl *entry = GetSummaryFormatter();
499061da546Spatrick 
500061da546Spatrick   if (m_options.m_use_objc)
501061da546Spatrick     return false;
502061da546Spatrick 
503*f6aab3d8Srobert   if (is_failed_description || !HasReachedMaximumDepth()) {
504061da546Spatrick     // We will show children for all concrete types. We won't show pointer
505061da546Spatrick     // contents unless a pointer depth has been specified. We won't reference
506061da546Spatrick     // contents unless the reference is the root object (depth of zero).
507061da546Spatrick 
508061da546Spatrick     // Use a new temporary pointer depth in case we override the current
509061da546Spatrick     // pointer depth below...
510061da546Spatrick 
511061da546Spatrick     if (is_ptr || is_ref) {
512061da546Spatrick       // We have a pointer or reference whose value is an address. Make sure
513061da546Spatrick       // that address is not NULL
514061da546Spatrick       AddressType ptr_address_type;
515061da546Spatrick       if (m_valobj->GetPointerValue(&ptr_address_type) == 0)
516061da546Spatrick         return false;
517061da546Spatrick 
518061da546Spatrick       const bool is_root_level = m_curr_depth == 0;
519061da546Spatrick 
520061da546Spatrick       if (is_ref && is_root_level) {
521061da546Spatrick         // If this is the root object (depth is zero) that we are showing and
522061da546Spatrick         // it is a reference, and no pointer depth has been supplied print out
523061da546Spatrick         // what it references. Don't do this at deeper depths otherwise we can
524061da546Spatrick         // end up with infinite recursion...
525061da546Spatrick         return true;
526061da546Spatrick       }
527061da546Spatrick 
528061da546Spatrick       return curr_ptr_depth.CanAllowExpansion();
529061da546Spatrick     }
530061da546Spatrick 
531061da546Spatrick     return (!entry || entry->DoesPrintChildren(m_valobj) || m_summary.empty());
532061da546Spatrick   }
533061da546Spatrick   return false;
534061da546Spatrick }
535061da546Spatrick 
ShouldExpandEmptyAggregates()536061da546Spatrick bool ValueObjectPrinter::ShouldExpandEmptyAggregates() {
537061da546Spatrick   TypeSummaryImpl *entry = GetSummaryFormatter();
538061da546Spatrick 
539061da546Spatrick   if (!entry)
540061da546Spatrick     return true;
541061da546Spatrick 
542061da546Spatrick   return entry->DoesPrintEmptyAggregates();
543061da546Spatrick }
544061da546Spatrick 
GetValueObjectForChildrenGeneration()545061da546Spatrick ValueObject *ValueObjectPrinter::GetValueObjectForChildrenGeneration() {
546061da546Spatrick   return m_valobj;
547061da546Spatrick }
548061da546Spatrick 
PrintChildrenPreamble()549061da546Spatrick void ValueObjectPrinter::PrintChildrenPreamble() {
550061da546Spatrick   if (m_options.m_flat_output) {
551061da546Spatrick     if (ShouldPrintValueObject())
552061da546Spatrick       m_stream->EOL();
553061da546Spatrick   } else {
554061da546Spatrick     if (ShouldPrintValueObject())
555061da546Spatrick       m_stream->PutCString(IsRef() ? ": {\n" : " {\n");
556061da546Spatrick     m_stream->IndentMore();
557061da546Spatrick   }
558061da546Spatrick }
559061da546Spatrick 
PrintChild(ValueObjectSP child_sp,const DumpValueObjectOptions::PointerDepth & curr_ptr_depth)560061da546Spatrick void ValueObjectPrinter::PrintChild(
561061da546Spatrick     ValueObjectSP child_sp,
562061da546Spatrick     const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
563061da546Spatrick   const uint32_t consumed_depth = (!m_options.m_pointer_as_array) ? 1 : 0;
564061da546Spatrick   const bool does_consume_ptr_depth =
565061da546Spatrick       ((IsPtr() && !m_options.m_pointer_as_array) || IsRef());
566061da546Spatrick 
567061da546Spatrick   DumpValueObjectOptions child_options(m_options);
568061da546Spatrick   child_options.SetFormat(m_options.m_format)
569061da546Spatrick       .SetSummary()
570061da546Spatrick       .SetRootValueObjectName();
571061da546Spatrick   child_options.SetScopeChecked(true)
572061da546Spatrick       .SetHideName(m_options.m_hide_name)
573061da546Spatrick       .SetHideValue(m_options.m_hide_value)
574061da546Spatrick       .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1
575061da546Spatrick                                ? child_options.m_omit_summary_depth -
576061da546Spatrick                                      consumed_depth
577061da546Spatrick                                : 0)
578061da546Spatrick       .SetElementCount(0);
579061da546Spatrick 
580061da546Spatrick   if (child_sp.get()) {
581061da546Spatrick     ValueObjectPrinter child_printer(
582061da546Spatrick         child_sp.get(), m_stream, child_options,
583061da546Spatrick         does_consume_ptr_depth ? --curr_ptr_depth : curr_ptr_depth,
584061da546Spatrick         m_curr_depth + consumed_depth, m_printed_instance_pointers);
585061da546Spatrick     child_printer.PrintValueObject();
586061da546Spatrick   }
587061da546Spatrick }
588061da546Spatrick 
GetMaxNumChildrenToPrint(bool & print_dotdotdot)589061da546Spatrick uint32_t ValueObjectPrinter::GetMaxNumChildrenToPrint(bool &print_dotdotdot) {
590061da546Spatrick   ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
591061da546Spatrick 
592061da546Spatrick   if (m_options.m_pointer_as_array)
593061da546Spatrick     return m_options.m_pointer_as_array.m_element_count;
594061da546Spatrick 
595061da546Spatrick   size_t num_children = synth_m_valobj->GetNumChildren();
596061da546Spatrick   print_dotdotdot = false;
597061da546Spatrick   if (num_children) {
598061da546Spatrick     const size_t max_num_children =
599061da546Spatrick         m_valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
600061da546Spatrick 
601061da546Spatrick     if (num_children > max_num_children && !m_options.m_ignore_cap) {
602061da546Spatrick       print_dotdotdot = true;
603061da546Spatrick       return max_num_children;
604061da546Spatrick     }
605061da546Spatrick   }
606061da546Spatrick   return num_children;
607061da546Spatrick }
608061da546Spatrick 
PrintChildrenPostamble(bool print_dotdotdot)609061da546Spatrick void ValueObjectPrinter::PrintChildrenPostamble(bool print_dotdotdot) {
610061da546Spatrick   if (!m_options.m_flat_output) {
611061da546Spatrick     if (print_dotdotdot) {
612061da546Spatrick       m_valobj->GetTargetSP()
613061da546Spatrick           ->GetDebugger()
614061da546Spatrick           .GetCommandInterpreter()
615061da546Spatrick           .ChildrenTruncated();
616061da546Spatrick       m_stream->Indent("...\n");
617061da546Spatrick     }
618061da546Spatrick     m_stream->IndentLess();
619061da546Spatrick     m_stream->Indent("}\n");
620061da546Spatrick   }
621061da546Spatrick }
622061da546Spatrick 
ShouldPrintEmptyBrackets(bool value_printed,bool summary_printed)623061da546Spatrick bool ValueObjectPrinter::ShouldPrintEmptyBrackets(bool value_printed,
624061da546Spatrick                                                   bool summary_printed) {
625061da546Spatrick   ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
626061da546Spatrick 
627061da546Spatrick   if (!IsAggregate())
628061da546Spatrick     return false;
629061da546Spatrick 
630061da546Spatrick   if (!m_options.m_reveal_empty_aggregates) {
631061da546Spatrick     if (value_printed || summary_printed)
632061da546Spatrick       return false;
633061da546Spatrick   }
634061da546Spatrick 
635061da546Spatrick   if (synth_m_valobj->MightHaveChildren())
636061da546Spatrick     return true;
637061da546Spatrick 
638061da546Spatrick   if (m_val_summary_ok)
639061da546Spatrick     return false;
640061da546Spatrick 
641061da546Spatrick   return true;
642061da546Spatrick }
643061da546Spatrick 
PhysicalIndexForLogicalIndex(size_t base,size_t stride,size_t logical)644061da546Spatrick static constexpr size_t PhysicalIndexForLogicalIndex(size_t base, size_t stride,
645061da546Spatrick                                                      size_t logical) {
646061da546Spatrick   return base + logical * stride;
647061da546Spatrick }
648061da546Spatrick 
GenerateChild(ValueObject * synth_valobj,size_t idx)649061da546Spatrick ValueObjectSP ValueObjectPrinter::GenerateChild(ValueObject *synth_valobj,
650061da546Spatrick                                                 size_t idx) {
651061da546Spatrick   if (m_options.m_pointer_as_array) {
652061da546Spatrick     // if generating pointer-as-array children, use GetSyntheticArrayMember
653061da546Spatrick     return synth_valobj->GetSyntheticArrayMember(
654061da546Spatrick         PhysicalIndexForLogicalIndex(
655061da546Spatrick             m_options.m_pointer_as_array.m_base_element,
656061da546Spatrick             m_options.m_pointer_as_array.m_stride, idx),
657061da546Spatrick         true);
658061da546Spatrick   } else {
659061da546Spatrick     // otherwise, do the usual thing
660061da546Spatrick     return synth_valobj->GetChildAtIndex(idx, true);
661061da546Spatrick   }
662061da546Spatrick }
663061da546Spatrick 
PrintChildren(bool value_printed,bool summary_printed,const DumpValueObjectOptions::PointerDepth & curr_ptr_depth)664061da546Spatrick void ValueObjectPrinter::PrintChildren(
665061da546Spatrick     bool value_printed, bool summary_printed,
666061da546Spatrick     const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) {
667061da546Spatrick   ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
668061da546Spatrick 
669061da546Spatrick   bool print_dotdotdot = false;
670061da546Spatrick   size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
671061da546Spatrick   if (num_children) {
672061da546Spatrick     bool any_children_printed = false;
673061da546Spatrick 
674061da546Spatrick     for (size_t idx = 0; idx < num_children; ++idx) {
675061da546Spatrick       if (ValueObjectSP child_sp = GenerateChild(synth_m_valobj, idx)) {
676061da546Spatrick         if (!any_children_printed) {
677061da546Spatrick           PrintChildrenPreamble();
678061da546Spatrick           any_children_printed = true;
679061da546Spatrick         }
680061da546Spatrick         PrintChild(child_sp, curr_ptr_depth);
681061da546Spatrick       }
682061da546Spatrick     }
683061da546Spatrick 
684061da546Spatrick     if (any_children_printed)
685061da546Spatrick       PrintChildrenPostamble(print_dotdotdot);
686061da546Spatrick     else {
687061da546Spatrick       if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) {
688061da546Spatrick         if (ShouldPrintValueObject())
689061da546Spatrick           m_stream->PutCString(" {}\n");
690061da546Spatrick         else
691061da546Spatrick           m_stream->EOL();
692061da546Spatrick       } else
693061da546Spatrick         m_stream->EOL();
694061da546Spatrick     }
695061da546Spatrick   } else if (ShouldPrintEmptyBrackets(value_printed, summary_printed)) {
696061da546Spatrick     // Aggregate, no children...
697061da546Spatrick     if (ShouldPrintValueObject()) {
698061da546Spatrick       // if it has a synthetic value, then don't print {}, the synthetic
699061da546Spatrick       // children are probably only being used to vend a value
700061da546Spatrick       if (m_valobj->DoesProvideSyntheticValue() ||
701061da546Spatrick           !ShouldExpandEmptyAggregates())
702061da546Spatrick         m_stream->PutCString("\n");
703061da546Spatrick       else
704061da546Spatrick         m_stream->PutCString(" {}\n");
705061da546Spatrick     }
706061da546Spatrick   } else {
707061da546Spatrick     if (ShouldPrintValueObject())
708061da546Spatrick       m_stream->EOL();
709061da546Spatrick   }
710061da546Spatrick }
711061da546Spatrick 
PrintChildrenOneLiner(bool hide_names)712061da546Spatrick bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) {
713061da546Spatrick   if (!GetMostSpecializedValue() || m_valobj == nullptr)
714061da546Spatrick     return false;
715061da546Spatrick 
716061da546Spatrick   ValueObject *synth_m_valobj = GetValueObjectForChildrenGeneration();
717061da546Spatrick 
718061da546Spatrick   bool print_dotdotdot = false;
719061da546Spatrick   size_t num_children = GetMaxNumChildrenToPrint(print_dotdotdot);
720061da546Spatrick 
721061da546Spatrick   if (num_children) {
722061da546Spatrick     m_stream->PutChar('(');
723061da546Spatrick 
724061da546Spatrick     for (uint32_t idx = 0; idx < num_children; ++idx) {
725061da546Spatrick       lldb::ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx, true));
726061da546Spatrick       if (child_sp)
727061da546Spatrick         child_sp = child_sp->GetQualifiedRepresentationIfAvailable(
728061da546Spatrick             m_options.m_use_dynamic, m_options.m_use_synthetic);
729061da546Spatrick       if (child_sp) {
730061da546Spatrick         if (idx)
731061da546Spatrick           m_stream->PutCString(", ");
732061da546Spatrick         if (!hide_names) {
733061da546Spatrick           const char *name = child_sp.get()->GetName().AsCString();
734061da546Spatrick           if (name && *name) {
735061da546Spatrick             m_stream->PutCString(name);
736061da546Spatrick             m_stream->PutCString(" = ");
737061da546Spatrick           }
738061da546Spatrick         }
739061da546Spatrick         child_sp->DumpPrintableRepresentation(
740061da546Spatrick             *m_stream, ValueObject::eValueObjectRepresentationStyleSummary,
741061da546Spatrick             m_options.m_format,
742061da546Spatrick             ValueObject::PrintableRepresentationSpecialCases::eDisable);
743061da546Spatrick       }
744061da546Spatrick     }
745061da546Spatrick 
746061da546Spatrick     if (print_dotdotdot)
747061da546Spatrick       m_stream->PutCString(", ...)");
748061da546Spatrick     else
749061da546Spatrick       m_stream->PutChar(')');
750061da546Spatrick   }
751061da546Spatrick   return true;
752061da546Spatrick }
753061da546Spatrick 
PrintChildrenIfNeeded(bool value_printed,bool summary_printed)754061da546Spatrick void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed,
755061da546Spatrick                                                bool summary_printed) {
756061da546Spatrick   // This flag controls whether we tried to display a description for this
757061da546Spatrick   // object and failed if that happens, we want to display the children if any.
758061da546Spatrick   bool is_failed_description =
759061da546Spatrick       !PrintObjectDescriptionIfNeeded(value_printed, summary_printed);
760061da546Spatrick 
761061da546Spatrick   DumpValueObjectOptions::PointerDepth curr_ptr_depth = m_ptr_depth;
762061da546Spatrick   const bool print_children =
763061da546Spatrick       ShouldPrintChildren(is_failed_description, curr_ptr_depth);
764061da546Spatrick   const bool print_oneline =
765061da546Spatrick       (curr_ptr_depth.CanAllowExpansion() || m_options.m_show_types ||
766061da546Spatrick        !m_options.m_allow_oneliner_mode || m_options.m_flat_output ||
767061da546Spatrick        (m_options.m_pointer_as_array) || m_options.m_show_location)
768061da546Spatrick           ? false
769061da546Spatrick           : DataVisualization::ShouldPrintAsOneLiner(*m_valobj);
770061da546Spatrick   if (print_children && IsInstancePointer()) {
771061da546Spatrick     uint64_t instance_ptr_value = m_valobj->GetValueAsUnsigned(0);
772061da546Spatrick     if (m_printed_instance_pointers->count(instance_ptr_value)) {
773061da546Spatrick       // We already printed this instance-is-pointer thing, so don't expand it.
774061da546Spatrick       m_stream->PutCString(" {...}\n");
775061da546Spatrick       return;
776061da546Spatrick     } else {
777061da546Spatrick       // Remember this guy for future reference.
778061da546Spatrick       m_printed_instance_pointers->emplace(instance_ptr_value);
779061da546Spatrick     }
780061da546Spatrick   }
781061da546Spatrick 
782061da546Spatrick   if (print_children) {
783061da546Spatrick     if (print_oneline) {
784061da546Spatrick       m_stream->PutChar(' ');
785061da546Spatrick       PrintChildrenOneLiner(false);
786061da546Spatrick       m_stream->EOL();
787061da546Spatrick     } else
788061da546Spatrick       PrintChildren(value_printed, summary_printed, curr_ptr_depth);
789*f6aab3d8Srobert   } else if (HasReachedMaximumDepth() && IsAggregate() &&
790061da546Spatrick              ShouldPrintValueObject()) {
791061da546Spatrick     m_stream->PutCString("{...}\n");
792*f6aab3d8Srobert     // The maximum child depth has been reached. If `m_max_depth` is the default
793*f6aab3d8Srobert     // (i.e. the user has _not_ customized it), then lldb presents a warning to
794*f6aab3d8Srobert     // the user. The warning tells the user that the limit has been reached, but
795*f6aab3d8Srobert     // more importantly tells them how to expand the limit if desired.
796*f6aab3d8Srobert     if (m_options.m_max_depth_is_default)
797*f6aab3d8Srobert       m_valobj->GetTargetSP()
798*f6aab3d8Srobert           ->GetDebugger()
799*f6aab3d8Srobert           .GetCommandInterpreter()
800*f6aab3d8Srobert           .SetReachedMaximumDepth();
801061da546Spatrick   } else
802061da546Spatrick     m_stream->EOL();
803061da546Spatrick }
804*f6aab3d8Srobert 
HasReachedMaximumDepth()805*f6aab3d8Srobert bool ValueObjectPrinter::HasReachedMaximumDepth() {
806*f6aab3d8Srobert   return m_curr_depth >= m_options.m_max_depth;
807*f6aab3d8Srobert }
808