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