1 //===-- Cocoa.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 "Cocoa.h" 10 11 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 12 #include "lldb/Core/Mangled.h" 13 #include "lldb/Core/ValueObject.h" 14 #include "lldb/Core/ValueObjectConstResult.h" 15 #include "lldb/DataFormatters/FormattersHelpers.h" 16 #include "lldb/DataFormatters/StringPrinter.h" 17 #include "lldb/DataFormatters/TypeSummary.h" 18 #include "lldb/Host/Time.h" 19 #include "lldb/Target/Language.h" 20 #include "lldb/Target/Process.h" 21 #include "lldb/Target/ProcessStructReader.h" 22 #include "lldb/Target/Target.h" 23 #include "lldb/Utility/DataBufferHeap.h" 24 #include "lldb/Utility/Endian.h" 25 #include "lldb/Utility/Status.h" 26 #include "lldb/Utility/Stream.h" 27 28 #include "llvm/ADT/APInt.h" 29 #include "llvm/ADT/bit.h" 30 31 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" 32 33 #include "NSString.h" 34 35 using namespace lldb; 36 using namespace lldb_private; 37 using namespace lldb_private::formatters; 38 39 bool lldb_private::formatters::NSBundleSummaryProvider( 40 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 41 ProcessSP process_sp = valobj.GetProcessSP(); 42 if (!process_sp) 43 return false; 44 45 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 46 47 if (!runtime) 48 return false; 49 50 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 51 runtime->GetClassDescriptor(valobj)); 52 53 if (!descriptor || !descriptor->IsValid()) 54 return false; 55 56 uint32_t ptr_size = process_sp->GetAddressByteSize(); 57 58 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 59 60 if (!valobj_addr) 61 return false; 62 63 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 64 65 if (class_name.empty()) 66 return false; 67 68 if (class_name == "NSBundle") { 69 uint64_t offset = 5 * ptr_size; 70 ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 71 offset, 72 valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), 73 true)); 74 75 if (!text) 76 return false; 77 78 StreamString summary_stream; 79 bool was_nsstring_ok = 80 NSStringSummaryProvider(*text, summary_stream, options); 81 if (was_nsstring_ok && summary_stream.GetSize() > 0) { 82 stream.Printf("%s", summary_stream.GetData()); 83 return true; 84 } 85 } 86 87 return false; 88 } 89 90 bool lldb_private::formatters::NSTimeZoneSummaryProvider( 91 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 92 ProcessSP process_sp = valobj.GetProcessSP(); 93 if (!process_sp) 94 return false; 95 96 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 97 98 if (!runtime) 99 return false; 100 101 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 102 runtime->GetClassDescriptor(valobj)); 103 104 if (!descriptor || !descriptor->IsValid()) 105 return false; 106 107 uint32_t ptr_size = process_sp->GetAddressByteSize(); 108 109 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 110 111 if (!valobj_addr) 112 return false; 113 114 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 115 116 if (class_name.empty()) 117 return false; 118 119 if (class_name == "__NSTimeZone") { 120 uint64_t offset = ptr_size; 121 ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 122 offset, valobj.GetCompilerType(), true)); 123 124 if (!text) 125 return false; 126 127 StreamString summary_stream; 128 bool was_nsstring_ok = 129 NSStringSummaryProvider(*text, summary_stream, options); 130 if (was_nsstring_ok && summary_stream.GetSize() > 0) { 131 stream.Printf("%s", summary_stream.GetData()); 132 return true; 133 } 134 } 135 136 return false; 137 } 138 139 bool lldb_private::formatters::NSNotificationSummaryProvider( 140 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 141 ProcessSP process_sp = valobj.GetProcessSP(); 142 if (!process_sp) 143 return false; 144 145 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 146 147 if (!runtime) 148 return false; 149 150 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 151 runtime->GetClassDescriptor(valobj)); 152 153 if (!descriptor || !descriptor->IsValid()) 154 return false; 155 156 uint32_t ptr_size = process_sp->GetAddressByteSize(); 157 158 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 159 160 if (!valobj_addr) 161 return false; 162 163 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 164 165 if (class_name.empty()) 166 return false; 167 168 if (class_name == "NSConcreteNotification") { 169 uint64_t offset = ptr_size; 170 ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 171 offset, valobj.GetCompilerType(), true)); 172 173 if (!text) 174 return false; 175 176 StreamString summary_stream; 177 bool was_nsstring_ok = 178 NSStringSummaryProvider(*text, summary_stream, options); 179 if (was_nsstring_ok && summary_stream.GetSize() > 0) { 180 stream.Printf("%s", summary_stream.GetData()); 181 return true; 182 } 183 } 184 185 return false; 186 } 187 188 bool lldb_private::formatters::NSMachPortSummaryProvider( 189 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 190 ProcessSP process_sp = valobj.GetProcessSP(); 191 if (!process_sp) 192 return false; 193 194 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 195 196 if (!runtime) 197 return false; 198 199 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 200 runtime->GetClassDescriptor(valobj)); 201 202 if (!descriptor || !descriptor->IsValid()) 203 return false; 204 205 uint32_t ptr_size = process_sp->GetAddressByteSize(); 206 207 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 208 209 if (!valobj_addr) 210 return false; 211 212 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 213 214 if (class_name.empty()) 215 return false; 216 217 uint64_t port_number = 0; 218 219 if (class_name == "NSMachPort") { 220 uint64_t offset = (ptr_size == 4 ? 12 : 20); 221 Status error; 222 port_number = process_sp->ReadUnsignedIntegerFromMemory( 223 offset + valobj_addr, 4, 0, error); 224 if (error.Success()) { 225 stream.Printf("mach port: %u", 226 (uint32_t)(port_number & 0x00000000FFFFFFFF)); 227 return true; 228 } 229 } 230 231 return false; 232 } 233 234 bool lldb_private::formatters::NSIndexSetSummaryProvider( 235 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 236 ProcessSP process_sp = valobj.GetProcessSP(); 237 if (!process_sp) 238 return false; 239 240 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 241 242 if (!runtime) 243 return false; 244 245 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 246 runtime->GetClassDescriptor(valobj)); 247 248 if (!descriptor || !descriptor->IsValid()) 249 return false; 250 251 uint32_t ptr_size = process_sp->GetAddressByteSize(); 252 253 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 254 255 if (!valobj_addr) 256 return false; 257 258 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 259 260 if (class_name.empty()) 261 return false; 262 263 uint64_t count = 0; 264 265 do { 266 if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") { 267 Status error; 268 uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory( 269 valobj_addr + ptr_size, 4, 0, error); 270 if (error.Fail()) 271 return false; 272 // this means the set is empty - count = 0 273 if ((mode & 1) == 1) { 274 count = 0; 275 break; 276 } 277 if ((mode & 2) == 2) 278 mode = 1; // this means the set only has one range 279 else 280 mode = 2; // this means the set has multiple ranges 281 if (mode == 1) { 282 count = process_sp->ReadUnsignedIntegerFromMemory( 283 valobj_addr + 3 * ptr_size, ptr_size, 0, error); 284 if (error.Fail()) 285 return false; 286 } else { 287 // read a pointer to the data at 2*ptr_size 288 count = process_sp->ReadUnsignedIntegerFromMemory( 289 valobj_addr + 2 * ptr_size, ptr_size, 0, error); 290 if (error.Fail()) 291 return false; 292 // read the data at 2*ptr_size from the first location 293 count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size, 294 ptr_size, 0, error); 295 if (error.Fail()) 296 return false; 297 } 298 } else 299 return false; 300 } while (false); 301 stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es")); 302 return true; 303 } 304 305 static void NSNumber_FormatChar(ValueObject &valobj, Stream &stream, char value, 306 lldb::LanguageType lang) { 307 static ConstString g_TypeHint("NSNumber:char"); 308 309 std::string prefix, suffix; 310 if (Language *language = Language::FindPlugin(lang)) { 311 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 312 suffix)) { 313 prefix.clear(); 314 suffix.clear(); 315 } 316 } 317 318 stream.Printf("%s%hhd%s", prefix.c_str(), value, suffix.c_str()); 319 } 320 321 static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream, 322 short value, lldb::LanguageType lang) { 323 static ConstString g_TypeHint("NSNumber:short"); 324 325 std::string prefix, suffix; 326 if (Language *language = Language::FindPlugin(lang)) { 327 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 328 suffix)) { 329 prefix.clear(); 330 suffix.clear(); 331 } 332 } 333 334 stream.Printf("%s%hd%s", prefix.c_str(), value, suffix.c_str()); 335 } 336 337 static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value, 338 lldb::LanguageType lang) { 339 static ConstString g_TypeHint("NSNumber:int"); 340 341 std::string prefix, suffix; 342 if (Language *language = Language::FindPlugin(lang)) { 343 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 344 suffix)) { 345 prefix.clear(); 346 suffix.clear(); 347 } 348 } 349 350 stream.Printf("%s%d%s", prefix.c_str(), value, suffix.c_str()); 351 } 352 353 static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream, 354 uint64_t value, lldb::LanguageType lang) { 355 static ConstString g_TypeHint("NSNumber:long"); 356 357 std::string prefix, suffix; 358 if (Language *language = Language::FindPlugin(lang)) { 359 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 360 suffix)) { 361 prefix.clear(); 362 suffix.clear(); 363 } 364 } 365 366 stream.Printf("%s%" PRId64 "%s", prefix.c_str(), value, suffix.c_str()); 367 } 368 369 static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream, 370 const llvm::APInt &value, 371 lldb::LanguageType lang) { 372 static ConstString g_TypeHint("NSNumber:int128_t"); 373 374 std::string prefix, suffix; 375 if (Language *language = Language::FindPlugin(lang)) { 376 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 377 suffix)) { 378 prefix.clear(); 379 suffix.clear(); 380 } 381 } 382 383 stream.PutCString(prefix.c_str()); 384 const int radix = 10; 385 const bool isSigned = true; 386 std::string str = value.toString(radix, isSigned); 387 stream.PutCString(str.c_str()); 388 stream.PutCString(suffix.c_str()); 389 } 390 391 static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream, 392 float value, lldb::LanguageType lang) { 393 static ConstString g_TypeHint("NSNumber:float"); 394 395 std::string prefix, suffix; 396 if (Language *language = Language::FindPlugin(lang)) { 397 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 398 suffix)) { 399 prefix.clear(); 400 suffix.clear(); 401 } 402 } 403 404 stream.Printf("%s%f%s", prefix.c_str(), value, suffix.c_str()); 405 } 406 407 static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream, 408 double value, lldb::LanguageType lang) { 409 static ConstString g_TypeHint("NSNumber:double"); 410 411 std::string prefix, suffix; 412 if (Language *language = Language::FindPlugin(lang)) { 413 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 414 suffix)) { 415 prefix.clear(); 416 suffix.clear(); 417 } 418 } 419 420 stream.Printf("%s%g%s", prefix.c_str(), value, suffix.c_str()); 421 } 422 423 bool lldb_private::formatters::NSNumberSummaryProvider( 424 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 425 ProcessSP process_sp = valobj.GetProcessSP(); 426 if (!process_sp) 427 return false; 428 429 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 430 431 if (!runtime) 432 return false; 433 434 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 435 runtime->GetClassDescriptor(valobj)); 436 437 if (!descriptor || !descriptor->IsValid()) 438 return false; 439 440 uint32_t ptr_size = process_sp->GetAddressByteSize(); 441 442 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 443 444 if (!valobj_addr) 445 return false; 446 447 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 448 449 if (class_name.empty()) 450 return false; 451 452 if (class_name == "__NSCFBoolean") 453 return ObjCBooleanSummaryProvider(valobj, stream, options); 454 455 if (class_name == "NSDecimalNumber") 456 return NSDecimalNumberSummaryProvider(valobj, stream, options); 457 458 if (class_name == "NSNumber" || class_name == "__NSCFNumber") { 459 uint64_t value = 0; 460 uint64_t i_bits = 0; 461 if (descriptor->GetTaggedPointerInfo(&i_bits, &value)) { 462 switch (i_bits) { 463 case 0: 464 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 465 break; 466 case 1: 467 case 4: 468 NSNumber_FormatShort(valobj, stream, (short)value, 469 options.GetLanguage()); 470 break; 471 case 2: 472 case 8: 473 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 474 break; 475 case 3: 476 case 12: 477 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 478 break; 479 default: 480 return false; 481 } 482 return true; 483 } else { 484 Status error; 485 486 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 487 ObjCLanguageRuntime::Get(*process_sp)); 488 489 const bool new_format = 490 (runtime && runtime->GetFoundationVersion() >= 1400); 491 492 enum class TypeCodes : int { 493 sint8 = 0x0, 494 sint16 = 0x1, 495 sint32 = 0x2, 496 sint64 = 0x3, 497 f32 = 0x4, 498 f64 = 0x5, 499 sint128 = 0x6 500 }; 501 502 uint64_t data_location = valobj_addr + 2 * ptr_size; 503 TypeCodes type_code; 504 505 if (new_format) { 506 uint64_t cfinfoa = 507 process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 508 ptr_size, 0, error); 509 510 if (error.Fail()) 511 return false; 512 513 bool is_preserved_number = cfinfoa & 0x8; 514 if (is_preserved_number) { 515 lldbassert(!static_cast<bool>("We should handle preserved numbers!")); 516 return false; 517 } 518 519 type_code = static_cast<TypeCodes>(cfinfoa & 0x7); 520 } else { 521 uint8_t data_type = 522 process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 523 0, error) & 0x1F; 524 525 if (error.Fail()) 526 return false; 527 528 switch (data_type) { 529 case 1: type_code = TypeCodes::sint8; break; 530 case 2: type_code = TypeCodes::sint16; break; 531 case 3: type_code = TypeCodes::sint32; break; 532 case 17: data_location += 8; LLVM_FALLTHROUGH; 533 case 4: type_code = TypeCodes::sint64; break; 534 case 5: type_code = TypeCodes::f32; break; 535 case 6: type_code = TypeCodes::f64; break; 536 default: return false; 537 } 538 } 539 540 uint64_t value = 0; 541 bool success = false; 542 switch (type_code) { 543 case TypeCodes::sint8: 544 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, 545 error); 546 if (error.Fail()) 547 return false; 548 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 549 success = true; 550 break; 551 case TypeCodes::sint16: 552 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, 553 error); 554 if (error.Fail()) 555 return false; 556 NSNumber_FormatShort(valobj, stream, (short)value, 557 options.GetLanguage()); 558 success = true; 559 break; 560 case TypeCodes::sint32: 561 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, 562 error); 563 if (error.Fail()) 564 return false; 565 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 566 success = true; 567 break; 568 case TypeCodes::sint64: 569 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, 570 error); 571 if (error.Fail()) 572 return false; 573 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 574 success = true; 575 break; 576 case TypeCodes::f32: 577 { 578 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( 579 data_location, 4, 0, error); 580 if (error.Fail()) 581 return false; 582 float flt_value = 0.0f; 583 memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); 584 NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 585 success = true; 586 break; 587 } 588 case TypeCodes::f64: 589 { 590 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( 591 data_location, 8, 0, error); 592 if (error.Fail()) 593 return false; 594 double dbl_value = 0.0; 595 memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); 596 NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 597 success = true; 598 break; 599 } 600 case TypeCodes::sint128: // internally, this is the same 601 { 602 uint64_t words[2]; 603 words[1] = process_sp->ReadUnsignedIntegerFromMemory( 604 data_location, 8, 0, error); 605 if (error.Fail()) 606 return false; 607 words[0] = process_sp->ReadUnsignedIntegerFromMemory( 608 data_location + 8, 8, 0, error); 609 if (error.Fail()) 610 return false; 611 llvm::APInt i128_value(128, words); 612 NSNumber_FormatInt128(valobj, stream, i128_value, options.GetLanguage()); 613 success = true; 614 break; 615 } 616 } 617 return success; 618 } 619 } 620 621 return false; 622 } 623 624 bool lldb_private::formatters::NSDecimalNumberSummaryProvider( 625 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 626 ProcessSP process_sp = valobj.GetProcessSP(); 627 if (!process_sp) 628 return false; 629 630 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 631 uint32_t ptr_size = process_sp->GetAddressByteSize(); 632 633 Status error; 634 int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory( 635 valobj_addr + ptr_size, 1, 0, error); 636 if (error.Fail()) 637 return false; 638 639 uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory( 640 valobj_addr + ptr_size + 1, 1, 0, error); 641 if (error.Fail()) 642 return false; 643 644 // Fifth bit marks negativity. 645 const bool is_negative = (length_and_negative >> 4) & 1; 646 647 // Zero length and negative means NaN. 648 uint8_t length = length_and_negative & 0xf; 649 const bool is_nan = is_negative && (length == 0); 650 651 if (is_nan) { 652 stream.Printf("NaN"); 653 return true; 654 } 655 656 if (length == 0) { 657 stream.Printf("0"); 658 return true; 659 } 660 661 uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory( 662 valobj_addr + ptr_size + 4, 8, 0, error); 663 if (error.Fail()) 664 return false; 665 666 if (is_negative) 667 stream.Printf("-"); 668 669 stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent); 670 return true; 671 } 672 673 bool lldb_private::formatters::NSURLSummaryProvider( 674 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 675 ProcessSP process_sp = valobj.GetProcessSP(); 676 if (!process_sp) 677 return false; 678 679 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 680 681 if (!runtime) 682 return false; 683 684 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 685 runtime->GetClassDescriptor(valobj)); 686 687 if (!descriptor || !descriptor->IsValid()) 688 return false; 689 690 uint32_t ptr_size = process_sp->GetAddressByteSize(); 691 692 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 693 694 if (!valobj_addr) 695 return false; 696 697 llvm::StringRef class_name = descriptor->GetClassName().GetStringRef(); 698 699 if (!class_name.equals("NSURL")) 700 return false; 701 702 uint64_t offset_text = ptr_size + ptr_size + 703 8; // ISA + pointer + 8 bytes of data (even on 32bit) 704 uint64_t offset_base = offset_text + ptr_size; 705 CompilerType type(valobj.GetCompilerType()); 706 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); 707 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); 708 if (!text || text->GetValueAsUnsigned(0) == 0) 709 return false; 710 711 StreamString base_summary; 712 if (base && base->GetValueAsUnsigned(0)) { 713 if (!NSURLSummaryProvider(*base, base_summary, options)) 714 base_summary.Clear(); 715 } 716 if (base_summary.Empty()) 717 return NSStringSummaryProvider(*text, stream, options); 718 719 StreamString summary; 720 if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty()) 721 return false; 722 723 const char quote_char = '"'; 724 std::string prefix, suffix; 725 if (Language *language = Language::FindPlugin(options.GetLanguage())) { 726 if (!language->GetFormatterPrefixSuffix(*text, ConstString("NSString"), 727 prefix, suffix)) { 728 prefix.clear(); 729 suffix.clear(); 730 } 731 } 732 // @"A" -> @"A 733 llvm::StringRef summary_str = summary.GetString(); 734 bool back_consumed = summary_str.consume_back(quote_char + suffix); 735 assert(back_consumed); 736 UNUSED_IF_ASSERT_DISABLED(back_consumed); 737 // @"B" -> B" 738 llvm::StringRef base_summary_str = base_summary.GetString(); 739 bool front_consumed = base_summary_str.consume_front(prefix + quote_char); 740 assert(front_consumed); 741 UNUSED_IF_ASSERT_DISABLED(front_consumed); 742 // @"A -- B" 743 if (!summary_str.empty() && !base_summary_str.empty()) { 744 stream.Printf("%s -- %s", summary_str.str().c_str(), 745 base_summary_str.str().c_str()); 746 return true; 747 } 748 749 return false; 750 } 751 752 /// Bias value for tagged pointer exponents. 753 /// Recommended values: 754 /// 0x3e3: encodes all dates between distantPast and distantFuture 755 /// except for the range within about 1e-28 second of the reference date. 756 /// 0x3ef: encodes all dates for a few million years beyond distantPast and 757 /// distantFuture, except within about 1e-25 second of the reference date. 758 const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef; 759 760 struct DoubleBits { 761 uint64_t fraction : 52; // unsigned 762 uint64_t exponent : 11; // signed 763 uint64_t sign : 1; 764 }; 765 766 struct TaggedDoubleBits { 767 uint64_t fraction : 52; // unsigned 768 uint64_t exponent : 7; // signed 769 uint64_t sign : 1; 770 uint64_t unused : 4; // placeholder for pointer tag bits 771 }; 772 773 static uint64_t decodeExponent(uint64_t exp) { 774 // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits 775 // before performing arithmetic. 776 return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS; 777 } 778 779 static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) { 780 if (encodedTimeInterval == 0) 781 return 0.0; 782 if (encodedTimeInterval == std::numeric_limits<uint64_t>::max()) 783 return (uint64_t)-0.0; 784 785 TaggedDoubleBits encodedBits = 786 llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval); 787 assert(encodedBits.unused == 0); 788 789 // Sign and fraction are represented exactly. 790 // Exponent is encoded. 791 DoubleBits decodedBits; 792 decodedBits.sign = encodedBits.sign; 793 decodedBits.fraction = encodedBits.fraction; 794 decodedBits.exponent = decodeExponent(encodedBits.exponent); 795 796 return llvm::bit_cast<double>(decodedBits); 797 } 798 799 bool lldb_private::formatters::NSDateSummaryProvider( 800 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 801 ProcessSP process_sp = valobj.GetProcessSP(); 802 if (!process_sp) 803 return false; 804 805 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 806 807 if (!runtime) 808 return false; 809 810 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 811 runtime->GetClassDescriptor(valobj)); 812 813 if (!descriptor || !descriptor->IsValid()) 814 return false; 815 816 uint32_t ptr_size = process_sp->GetAddressByteSize(); 817 818 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 819 820 if (!valobj_addr) 821 return false; 822 823 uint64_t date_value_bits = 0; 824 double date_value = 0.0; 825 826 ConstString class_name = descriptor->GetClassName(); 827 828 static const ConstString g_NSDate("NSDate"); 829 static const ConstString g___NSDate("__NSDate"); 830 static const ConstString g___NSTaggedDate("__NSTaggedDate"); 831 static const ConstString g_NSCalendarDate("NSCalendarDate"); 832 static const ConstString g_NSConstantDate("NSConstantDate"); 833 834 if (class_name.IsEmpty()) 835 return false; 836 837 uint64_t info_bits = 0, value_bits = 0; 838 if ((class_name == g_NSDate) || (class_name == g___NSDate) || 839 (class_name == g___NSTaggedDate) || (class_name == g_NSConstantDate)) { 840 if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { 841 date_value_bits = ((value_bits << 8) | (info_bits << 4)); 842 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 843 } else { 844 llvm::Triple triple( 845 process_sp->GetTarget().GetArchitecture().GetTriple()); 846 uint32_t delta = 847 (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; 848 Status error; 849 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 850 valobj_addr + delta, 8, 0, error); 851 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 852 if (error.Fail()) 853 return false; 854 } 855 } else if (class_name == g_NSCalendarDate) { 856 Status error; 857 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 858 valobj_addr + 2 * ptr_size, 8, 0, error); 859 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 860 if (error.Fail()) 861 return false; 862 } else 863 return false; 864 865 // FIXME: It seems old dates are not formatted according to NSDate's calendar 866 // so we hardcode distantPast's value so that it looks like LLDB is doing 867 // the right thing. 868 869 // The relative time in seconds from Cocoa Epoch to [NSDate distantPast]. 870 const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800; 871 if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) { 872 stream.Printf("0001-01-01 00:00:00 UTC"); 873 return true; 874 } 875 876 // Accomodate for the __NSTaggedDate format introduced in Foundation 1600. 877 if (class_name == g___NSTaggedDate) { 878 auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 879 ObjCLanguageRuntime::Get(*process_sp)); 880 if (runtime && runtime->GetFoundationVersion() >= 1600) 881 date_value = decodeTaggedTimeInterval(value_bits << 4); 882 } 883 884 // this snippet of code assumes that time_t == seconds since Jan-1-1970 this 885 // is generally true and POSIXly happy, but might break if a library vendor 886 // decides to get creative 887 time_t epoch = GetOSXEpoch(); 888 epoch = epoch + static_cast<time_t>(std::floor(date_value)); 889 tm *tm_date = gmtime(&epoch); 890 if (!tm_date) 891 return false; 892 std::string buffer(1024, 0); 893 if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) 894 return false; 895 stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900, 896 tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour, 897 tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); 898 return true; 899 } 900 901 bool lldb_private::formatters::ObjCClassSummaryProvider( 902 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 903 ProcessSP process_sp = valobj.GetProcessSP(); 904 if (!process_sp) 905 return false; 906 907 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 908 909 if (!runtime) 910 return false; 911 912 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 913 runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); 914 915 if (!descriptor || !descriptor->IsValid()) 916 return false; 917 918 ConstString class_name = descriptor->GetClassName(); 919 920 if (class_name.IsEmpty()) 921 return false; 922 923 if (ConstString cs = Mangled(class_name).GetDemangledName()) 924 class_name = cs; 925 926 stream.Printf("%s", class_name.AsCString("<unknown class>")); 927 return true; 928 } 929 930 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd { 931 public: 932 ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp) 933 : SyntheticChildrenFrontEnd(*valobj_sp) {} 934 935 ~ObjCClassSyntheticChildrenFrontEnd() override = default; 936 937 size_t CalculateNumChildren() override { return 0; } 938 939 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { 940 return lldb::ValueObjectSP(); 941 } 942 943 bool Update() override { return false; } 944 945 bool MightHaveChildren() override { return false; } 946 947 size_t GetIndexOfChildWithName(ConstString name) override { 948 return UINT32_MAX; 949 } 950 }; 951 952 SyntheticChildrenFrontEnd * 953 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator( 954 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 955 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); 956 } 957 958 template <bool needs_at> 959 bool lldb_private::formatters::NSDataSummaryProvider( 960 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 961 ProcessSP process_sp = valobj.GetProcessSP(); 962 if (!process_sp) 963 return false; 964 965 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 966 967 if (!runtime) 968 return false; 969 970 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 971 runtime->GetClassDescriptor(valobj)); 972 973 if (!descriptor || !descriptor->IsValid()) 974 return false; 975 976 bool is_64bit = (process_sp->GetAddressByteSize() == 8); 977 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 978 979 if (!valobj_addr) 980 return false; 981 982 uint64_t value = 0; 983 984 llvm::StringRef class_name = descriptor->GetClassName().GetCString(); 985 986 if (class_name.empty()) 987 return false; 988 989 bool isNSConcreteData = class_name == "NSConcreteData"; 990 bool isNSConcreteMutableData = class_name == "NSConcreteMutableData"; 991 bool isNSCFData = class_name == "__NSCFData"; 992 if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) { 993 uint32_t offset; 994 if (isNSConcreteData) 995 offset = is_64bit ? 8 : 4; 996 else 997 offset = is_64bit ? 16 : 8; 998 999 Status error; 1000 value = process_sp->ReadUnsignedIntegerFromMemory( 1001 valobj_addr + offset, is_64bit ? 8 : 4, 0, error); 1002 if (error.Fail()) 1003 return false; 1004 } else if (class_name == "_NSInlineData") { 1005 uint32_t offset = (is_64bit ? 8 : 4); 1006 Status error; 1007 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2, 1008 0, error); 1009 if (error.Fail()) 1010 return false; 1011 } else if (class_name == "_NSZeroData") { 1012 value = 0; 1013 } else 1014 return false; 1015 1016 stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value, 1017 (value != 1 ? "s" : ""), (needs_at ? "\"" : "")); 1018 1019 return true; 1020 } 1021 1022 bool lldb_private::formatters::ObjCBOOLSummaryProvider( 1023 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1024 const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); 1025 1026 ValueObjectSP real_guy_sp = valobj.GetSP(); 1027 1028 if (type_info & eTypeIsPointer) { 1029 Status err; 1030 real_guy_sp = valobj.Dereference(err); 1031 if (err.Fail() || !real_guy_sp) 1032 return false; 1033 } else if (type_info & eTypeIsReference) { 1034 real_guy_sp = valobj.GetChildAtIndex(0, true); 1035 if (!real_guy_sp) 1036 return false; 1037 } 1038 int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF); 1039 switch (value) { 1040 case 0: 1041 stream.Printf("NO"); 1042 break; 1043 case 1: 1044 stream.Printf("YES"); 1045 break; 1046 default: 1047 stream.Printf("%d", value); 1048 break; 1049 } 1050 return true; 1051 } 1052 1053 bool lldb_private::formatters::ObjCBooleanSummaryProvider( 1054 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1055 lldb::addr_t valobj_ptr_value = 1056 valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1057 if (valobj_ptr_value == LLDB_INVALID_ADDRESS) 1058 return false; 1059 1060 ProcessSP process_sp(valobj.GetProcessSP()); 1061 if (!process_sp) 1062 return false; 1063 1064 if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 1065 ObjCLanguageRuntime::Get(*process_sp))) { 1066 lldb::addr_t cf_true = LLDB_INVALID_ADDRESS, 1067 cf_false = LLDB_INVALID_ADDRESS; 1068 objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false); 1069 if (valobj_ptr_value == cf_true) { 1070 stream.PutCString("YES"); 1071 return true; 1072 } 1073 if (valobj_ptr_value == cf_false) { 1074 stream.PutCString("NO"); 1075 return true; 1076 } 1077 } 1078 1079 return false; 1080 } 1081 1082 template <bool is_sel_ptr> 1083 bool lldb_private::formatters::ObjCSELSummaryProvider( 1084 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1085 lldb::ValueObjectSP valobj_sp; 1086 1087 CompilerType charstar(valobj.GetCompilerType() 1088 .GetBasicTypeFromAST(eBasicTypeChar) 1089 .GetPointerType()); 1090 1091 if (!charstar) 1092 return false; 1093 1094 ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); 1095 1096 if (is_sel_ptr) { 1097 lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1098 if (data_address == LLDB_INVALID_ADDRESS) 1099 return false; 1100 valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, 1101 exe_ctx, charstar); 1102 } else { 1103 DataExtractor data; 1104 Status error; 1105 valobj.GetData(data, error); 1106 if (error.Fail()) 1107 return false; 1108 valobj_sp = 1109 ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); 1110 } 1111 1112 if (!valobj_sp) 1113 return false; 1114 1115 stream.Printf("%s", valobj_sp->GetSummaryAsCString()); 1116 return true; 1117 } 1118 1119 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 1120 // this call gives the POSIX equivalent of the Cocoa epoch 1121 time_t lldb_private::formatters::GetOSXEpoch() { 1122 static time_t epoch = 0; 1123 if (!epoch) { 1124 #ifndef _WIN32 1125 tzset(); 1126 tm tm_epoch; 1127 tm_epoch.tm_sec = 0; 1128 tm_epoch.tm_hour = 0; 1129 tm_epoch.tm_min = 0; 1130 tm_epoch.tm_mon = 0; 1131 tm_epoch.tm_mday = 1; 1132 tm_epoch.tm_year = 2001 - 1900; 1133 tm_epoch.tm_isdst = -1; 1134 tm_epoch.tm_gmtoff = 0; 1135 tm_epoch.tm_zone = nullptr; 1136 epoch = timegm(&tm_epoch); 1137 #endif 1138 } 1139 return epoch; 1140 } 1141 1142 template bool lldb_private::formatters::NSDataSummaryProvider<true>( 1143 ValueObject &, Stream &, const TypeSummaryOptions &); 1144 1145 template bool lldb_private::formatters::NSDataSummaryProvider<false>( 1146 ValueObject &, Stream &, const TypeSummaryOptions &); 1147 1148 template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( 1149 ValueObject &, Stream &, const TypeSummaryOptions &); 1150 1151 template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( 1152 ValueObject &, Stream &, const TypeSummaryOptions &); 1153