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