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 #include "NSString.h" 11 #include "ObjCConstants.h" 12 13 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" 14 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 15 #include "lldb/Core/Mangled.h" 16 #include "lldb/Core/ValueObject.h" 17 #include "lldb/Core/ValueObjectConstResult.h" 18 #include "lldb/DataFormatters/FormattersHelpers.h" 19 #include "lldb/DataFormatters/StringPrinter.h" 20 #include "lldb/DataFormatters/TypeSummary.h" 21 #include "lldb/Host/Time.h" 22 #include "lldb/Target/Language.h" 23 #include "lldb/Target/Process.h" 24 #include "lldb/Target/ProcessStructReader.h" 25 #include "lldb/Target/Target.h" 26 #include "lldb/Utility/DataBufferHeap.h" 27 #include "lldb/Utility/Endian.h" 28 #include "lldb/Utility/Status.h" 29 #include "lldb/Utility/Stream.h" 30 31 #include "llvm/ADT/APInt.h" 32 #include "llvm/ADT/bit.h" 33 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 int64_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 = llvm::toString(value, 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 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS); 430 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 431 432 if (!runtime) 433 return false; 434 435 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 436 runtime->GetClassDescriptor(valobj)); 437 438 if (!descriptor || !descriptor->IsValid()) 439 return false; 440 441 uint32_t ptr_size = process_sp->GetAddressByteSize(); 442 443 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 444 445 if (!valobj_addr) 446 return false; 447 448 llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 449 450 if (class_name.empty()) 451 return false; 452 453 if (class_name == "__NSCFBoolean") 454 return ObjCBooleanSummaryProvider(valobj, stream, options); 455 456 if (class_name == "NSDecimalNumber") 457 return NSDecimalNumberSummaryProvider(valobj, stream, options); 458 459 if (class_name == "NSConstantIntegerNumber") { 460 Status error; 461 int64_t value = process_sp->ReadSignedIntegerFromMemory( 462 valobj_addr + 2 * ptr_size, 8, 0, error); 463 if (error.Fail()) 464 return false; 465 uint64_t encoding_addr = process_sp->ReadUnsignedIntegerFromMemory( 466 valobj_addr + ptr_size, ptr_size, 0, error); 467 if (error.Fail()) 468 return false; 469 char encoding = 470 process_sp->ReadUnsignedIntegerFromMemory(encoding_addr, 1, 0, error); 471 if (error.Fail()) 472 return false; 473 474 switch (encoding) { 475 case _C_CHR: 476 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 477 return true; 478 case _C_SHT: 479 NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage()); 480 return true; 481 case _C_INT: 482 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 483 return true; 484 case _C_LNG: 485 case _C_LNG_LNG: 486 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 487 return true; 488 489 case _C_UCHR: 490 case _C_USHT: 491 case _C_UINT: 492 case _C_ULNG: 493 case _C_ULNG_LNG: 494 stream.Printf("%" PRIu64, value); 495 return true; 496 } 497 498 return false; 499 } 500 501 if (class_name == "NSConstantFloatNumber") { 502 Status error; 503 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( 504 valobj_addr + ptr_size, 4, 0, error); 505 if (error.Fail()) 506 return false; 507 float flt_value = 0.0f; 508 memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); 509 NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 510 return true; 511 } 512 513 if (class_name == "NSConstantDoubleNumber") { 514 Status error; 515 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( 516 valobj_addr + ptr_size, 8, 0, error); 517 if (error.Fail()) 518 return false; 519 double dbl_value = 0.0; 520 memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); 521 NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 522 return true; 523 } 524 525 if (class_name == "NSNumber" || class_name == "__NSCFNumber") { 526 int64_t value = 0; 527 uint64_t i_bits = 0; 528 if (descriptor->GetTaggedPointerInfoSigned(&i_bits, &value)) { 529 // Check for "preserved" numbers. We still don't support them yet. 530 if (i_bits & 0x8) { 531 if (log) 532 log->Printf( 533 "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64, 534 valobj_addr); 535 return false; 536 } 537 538 switch (i_bits) { 539 case 0: 540 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 541 break; 542 case 1: 543 case 4: 544 NSNumber_FormatShort(valobj, stream, (short)value, 545 options.GetLanguage()); 546 break; 547 case 2: 548 case 8: 549 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 550 break; 551 case 3: 552 case 12: 553 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 554 break; 555 default: 556 return false; 557 } 558 return true; 559 } else { 560 Status error; 561 562 AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 563 ObjCLanguageRuntime::Get(*process_sp)); 564 565 const bool new_format = 566 (runtime && runtime->GetFoundationVersion() >= 1400); 567 568 enum class TypeCodes : int { 569 sint8 = 0x0, 570 sint16 = 0x1, 571 sint32 = 0x2, 572 sint64 = 0x3, 573 f32 = 0x4, 574 f64 = 0x5, 575 sint128 = 0x6 576 }; 577 578 uint64_t data_location = valobj_addr + 2 * ptr_size; 579 TypeCodes type_code; 580 581 if (new_format) { 582 uint64_t cfinfoa = process_sp->ReadUnsignedIntegerFromMemory( 583 valobj_addr + ptr_size, ptr_size, 0, error); 584 585 if (error.Fail()) 586 return false; 587 588 bool is_preserved_number = cfinfoa & 0x8; 589 if (is_preserved_number) { 590 if (log) 591 log->Printf( 592 "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64, 593 valobj_addr); 594 return false; 595 } 596 597 type_code = static_cast<TypeCodes>(cfinfoa & 0x7); 598 } else { 599 uint8_t data_type = process_sp->ReadUnsignedIntegerFromMemory( 600 valobj_addr + ptr_size, 1, 0, error) & 601 0x1F; 602 603 if (error.Fail()) 604 return false; 605 606 switch (data_type) { 607 case 1: 608 type_code = TypeCodes::sint8; 609 break; 610 case 2: 611 type_code = TypeCodes::sint16; 612 break; 613 case 3: 614 type_code = TypeCodes::sint32; 615 break; 616 case 17: 617 data_location += 8; 618 LLVM_FALLTHROUGH; 619 case 4: 620 type_code = TypeCodes::sint64; 621 break; 622 case 5: 623 type_code = TypeCodes::f32; 624 break; 625 case 6: 626 type_code = TypeCodes::f64; 627 break; 628 default: 629 return false; 630 } 631 } 632 633 uint64_t value = 0; 634 bool success = false; 635 switch (type_code) { 636 case TypeCodes::sint8: 637 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, 638 error); 639 if (error.Fail()) 640 return false; 641 NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 642 success = true; 643 break; 644 case TypeCodes::sint16: 645 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, 646 error); 647 if (error.Fail()) 648 return false; 649 NSNumber_FormatShort(valobj, stream, (short)value, 650 options.GetLanguage()); 651 success = true; 652 break; 653 case TypeCodes::sint32: 654 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, 655 error); 656 if (error.Fail()) 657 return false; 658 NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 659 success = true; 660 break; 661 case TypeCodes::sint64: 662 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, 663 error); 664 if (error.Fail()) 665 return false; 666 NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 667 success = true; 668 break; 669 case TypeCodes::f32: { 670 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( 671 data_location, 4, 0, error); 672 if (error.Fail()) 673 return false; 674 float flt_value = 0.0f; 675 memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); 676 NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 677 success = true; 678 break; 679 } 680 case TypeCodes::f64: { 681 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( 682 data_location, 8, 0, error); 683 if (error.Fail()) 684 return false; 685 double dbl_value = 0.0; 686 memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); 687 NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 688 success = true; 689 break; 690 } 691 case TypeCodes::sint128: // internally, this is the same 692 { 693 uint64_t words[2]; 694 words[1] = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 695 0, error); 696 if (error.Fail()) 697 return false; 698 words[0] = process_sp->ReadUnsignedIntegerFromMemory(data_location + 8, 699 8, 0, error); 700 if (error.Fail()) 701 return false; 702 llvm::APInt i128_value(128, words); 703 NSNumber_FormatInt128(valobj, stream, i128_value, 704 options.GetLanguage()); 705 success = true; 706 break; 707 } 708 } 709 return success; 710 } 711 } 712 713 return false; 714 } 715 716 bool lldb_private::formatters::NSDecimalNumberSummaryProvider( 717 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 718 ProcessSP process_sp = valobj.GetProcessSP(); 719 if (!process_sp) 720 return false; 721 722 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 723 uint32_t ptr_size = process_sp->GetAddressByteSize(); 724 725 Status error; 726 int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory( 727 valobj_addr + ptr_size, 1, 0, error); 728 if (error.Fail()) 729 return false; 730 731 uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory( 732 valobj_addr + ptr_size + 1, 1, 0, error); 733 if (error.Fail()) 734 return false; 735 736 // Fifth bit marks negativity. 737 const bool is_negative = (length_and_negative >> 4) & 1; 738 739 // Zero length and negative means NaN. 740 uint8_t length = length_and_negative & 0xf; 741 const bool is_nan = is_negative && (length == 0); 742 743 if (is_nan) { 744 stream.Printf("NaN"); 745 return true; 746 } 747 748 if (length == 0) { 749 stream.Printf("0"); 750 return true; 751 } 752 753 uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory( 754 valobj_addr + ptr_size + 4, 8, 0, error); 755 if (error.Fail()) 756 return false; 757 758 if (is_negative) 759 stream.Printf("-"); 760 761 stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent); 762 return true; 763 } 764 765 bool lldb_private::formatters::NSURLSummaryProvider( 766 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 767 ProcessSP process_sp = valobj.GetProcessSP(); 768 if (!process_sp) 769 return false; 770 771 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 772 773 if (!runtime) 774 return false; 775 776 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 777 runtime->GetClassDescriptor(valobj)); 778 779 if (!descriptor || !descriptor->IsValid()) 780 return false; 781 782 uint32_t ptr_size = process_sp->GetAddressByteSize(); 783 784 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 785 786 if (!valobj_addr) 787 return false; 788 789 llvm::StringRef class_name = descriptor->GetClassName().GetStringRef(); 790 791 if (!class_name.equals("NSURL")) 792 return false; 793 794 uint64_t offset_text = ptr_size + ptr_size + 795 8; // ISA + pointer + 8 bytes of data (even on 32bit) 796 uint64_t offset_base = offset_text + ptr_size; 797 CompilerType type(valobj.GetCompilerType()); 798 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); 799 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); 800 if (!text || text->GetValueAsUnsigned(0) == 0) 801 return false; 802 803 StreamString base_summary; 804 if (base && base->GetValueAsUnsigned(0)) { 805 if (!NSURLSummaryProvider(*base, base_summary, options)) 806 base_summary.Clear(); 807 } 808 if (base_summary.Empty()) 809 return NSStringSummaryProvider(*text, stream, options); 810 811 StreamString summary; 812 if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty()) 813 return false; 814 815 const char quote_char = '"'; 816 std::string prefix, suffix; 817 if (Language *language = Language::FindPlugin(options.GetLanguage())) { 818 if (!language->GetFormatterPrefixSuffix(*text, ConstString("NSString"), 819 prefix, suffix)) { 820 prefix.clear(); 821 suffix.clear(); 822 } 823 } 824 // @"A" -> @"A 825 llvm::StringRef summary_str = summary.GetString(); 826 bool back_consumed = summary_str.consume_back(quote_char + suffix); 827 assert(back_consumed); 828 UNUSED_IF_ASSERT_DISABLED(back_consumed); 829 // @"B" -> B" 830 llvm::StringRef base_summary_str = base_summary.GetString(); 831 bool front_consumed = base_summary_str.consume_front(prefix + quote_char); 832 assert(front_consumed); 833 UNUSED_IF_ASSERT_DISABLED(front_consumed); 834 // @"A -- B" 835 if (!summary_str.empty() && !base_summary_str.empty()) { 836 stream.Printf("%s -- %s", summary_str.str().c_str(), 837 base_summary_str.str().c_str()); 838 return true; 839 } 840 841 return false; 842 } 843 844 /// Bias value for tagged pointer exponents. 845 /// Recommended values: 846 /// 0x3e3: encodes all dates between distantPast and distantFuture 847 /// except for the range within about 1e-28 second of the reference date. 848 /// 0x3ef: encodes all dates for a few million years beyond distantPast and 849 /// distantFuture, except within about 1e-25 second of the reference date. 850 const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef; 851 852 struct DoubleBits { 853 uint64_t fraction : 52; // unsigned 854 uint64_t exponent : 11; // signed 855 uint64_t sign : 1; 856 }; 857 858 struct TaggedDoubleBits { 859 uint64_t fraction : 52; // unsigned 860 uint64_t exponent : 7; // signed 861 uint64_t sign : 1; 862 uint64_t unused : 4; // placeholder for pointer tag bits 863 }; 864 865 static uint64_t decodeExponent(uint64_t exp) { 866 // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits 867 // before performing arithmetic. 868 return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS; 869 } 870 871 static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) { 872 if (encodedTimeInterval == 0) 873 return 0.0; 874 if (encodedTimeInterval == std::numeric_limits<uint64_t>::max()) 875 return (uint64_t)-0.0; 876 877 TaggedDoubleBits encodedBits = 878 llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval); 879 assert(encodedBits.unused == 0); 880 881 // Sign and fraction are represented exactly. 882 // Exponent is encoded. 883 DoubleBits decodedBits; 884 decodedBits.sign = encodedBits.sign; 885 decodedBits.fraction = encodedBits.fraction; 886 decodedBits.exponent = decodeExponent(encodedBits.exponent); 887 888 return llvm::bit_cast<double>(decodedBits); 889 } 890 891 bool lldb_private::formatters::NSDateSummaryProvider( 892 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 893 ProcessSP process_sp = valobj.GetProcessSP(); 894 if (!process_sp) 895 return false; 896 897 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 898 899 if (!runtime) 900 return false; 901 902 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 903 runtime->GetClassDescriptor(valobj)); 904 905 if (!descriptor || !descriptor->IsValid()) 906 return false; 907 908 uint32_t ptr_size = process_sp->GetAddressByteSize(); 909 910 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 911 912 if (!valobj_addr) 913 return false; 914 915 uint64_t date_value_bits = 0; 916 double date_value = 0.0; 917 918 ConstString class_name = descriptor->GetClassName(); 919 920 static const ConstString g_NSDate("NSDate"); 921 static const ConstString g___NSDate("__NSDate"); 922 static const ConstString g___NSTaggedDate("__NSTaggedDate"); 923 static const ConstString g_NSCalendarDate("NSCalendarDate"); 924 static const ConstString g_NSConstantDate("NSConstantDate"); 925 926 if (class_name.IsEmpty()) 927 return false; 928 929 uint64_t info_bits = 0, value_bits = 0; 930 if ((class_name == g_NSDate) || (class_name == g___NSDate) || 931 (class_name == g___NSTaggedDate) || (class_name == g_NSConstantDate)) { 932 if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { 933 date_value_bits = ((value_bits << 8) | (info_bits << 4)); 934 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 935 } else { 936 llvm::Triple triple( 937 process_sp->GetTarget().GetArchitecture().GetTriple()); 938 uint32_t delta = 939 (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; 940 Status error; 941 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 942 valobj_addr + delta, 8, 0, error); 943 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 944 if (error.Fail()) 945 return false; 946 } 947 } else if (class_name == g_NSCalendarDate) { 948 Status error; 949 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 950 valobj_addr + 2 * ptr_size, 8, 0, error); 951 memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 952 if (error.Fail()) 953 return false; 954 } else 955 return false; 956 957 // FIXME: It seems old dates are not formatted according to NSDate's calendar 958 // so we hardcode distantPast's value so that it looks like LLDB is doing 959 // the right thing. 960 961 // The relative time in seconds from Cocoa Epoch to [NSDate distantPast]. 962 const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800; 963 if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) { 964 stream.Printf("0001-01-01 00:00:00 UTC"); 965 return true; 966 } 967 968 // Accomodate for the __NSTaggedDate format introduced in Foundation 1600. 969 if (class_name == g___NSTaggedDate) { 970 auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 971 ObjCLanguageRuntime::Get(*process_sp)); 972 if (runtime && runtime->GetFoundationVersion() >= 1600) 973 date_value = decodeTaggedTimeInterval(value_bits << 4); 974 } 975 976 // this snippet of code assumes that time_t == seconds since Jan-1-1970 this 977 // is generally true and POSIXly happy, but might break if a library vendor 978 // decides to get creative 979 time_t epoch = GetOSXEpoch(); 980 epoch = epoch + static_cast<time_t>(std::floor(date_value)); 981 tm *tm_date = gmtime(&epoch); 982 if (!tm_date) 983 return false; 984 std::string buffer(1024, 0); 985 if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) 986 return false; 987 stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900, 988 tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour, 989 tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); 990 return true; 991 } 992 993 bool lldb_private::formatters::ObjCClassSummaryProvider( 994 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 995 ProcessSP process_sp = valobj.GetProcessSP(); 996 if (!process_sp) 997 return false; 998 999 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 1000 1001 if (!runtime) 1002 return false; 1003 1004 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 1005 runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); 1006 1007 if (!descriptor || !descriptor->IsValid()) 1008 return false; 1009 1010 ConstString class_name = descriptor->GetClassName(); 1011 1012 if (class_name.IsEmpty()) 1013 return false; 1014 1015 if (ConstString cs = Mangled(class_name).GetDemangledName()) 1016 class_name = cs; 1017 1018 stream.Printf("%s", class_name.AsCString("<unknown class>")); 1019 return true; 1020 } 1021 1022 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd { 1023 public: 1024 ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp) 1025 : SyntheticChildrenFrontEnd(*valobj_sp) {} 1026 1027 ~ObjCClassSyntheticChildrenFrontEnd() override = default; 1028 1029 size_t CalculateNumChildren() override { return 0; } 1030 1031 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { 1032 return lldb::ValueObjectSP(); 1033 } 1034 1035 bool Update() override { return false; } 1036 1037 bool MightHaveChildren() override { return false; } 1038 1039 size_t GetIndexOfChildWithName(ConstString name) override { 1040 return UINT32_MAX; 1041 } 1042 }; 1043 1044 SyntheticChildrenFrontEnd * 1045 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator( 1046 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 1047 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); 1048 } 1049 1050 template <bool needs_at> 1051 bool lldb_private::formatters::NSDataSummaryProvider( 1052 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1053 ProcessSP process_sp = valobj.GetProcessSP(); 1054 if (!process_sp) 1055 return false; 1056 1057 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 1058 1059 if (!runtime) 1060 return false; 1061 1062 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 1063 runtime->GetClassDescriptor(valobj)); 1064 1065 if (!descriptor || !descriptor->IsValid()) 1066 return false; 1067 1068 bool is_64bit = (process_sp->GetAddressByteSize() == 8); 1069 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 1070 1071 if (!valobj_addr) 1072 return false; 1073 1074 uint64_t value = 0; 1075 1076 llvm::StringRef class_name = descriptor->GetClassName().GetCString(); 1077 1078 if (class_name.empty()) 1079 return false; 1080 1081 bool isNSConcreteData = class_name == "NSConcreteData"; 1082 bool isNSConcreteMutableData = class_name == "NSConcreteMutableData"; 1083 bool isNSCFData = class_name == "__NSCFData"; 1084 if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) { 1085 uint32_t offset; 1086 if (isNSConcreteData) 1087 offset = is_64bit ? 8 : 4; 1088 else 1089 offset = is_64bit ? 16 : 8; 1090 1091 Status error; 1092 value = process_sp->ReadUnsignedIntegerFromMemory( 1093 valobj_addr + offset, is_64bit ? 8 : 4, 0, error); 1094 if (error.Fail()) 1095 return false; 1096 } else if (class_name == "_NSInlineData") { 1097 uint32_t offset = (is_64bit ? 8 : 4); 1098 Status error; 1099 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2, 1100 0, error); 1101 if (error.Fail()) 1102 return false; 1103 } else if (class_name == "_NSZeroData") { 1104 value = 0; 1105 } else 1106 return false; 1107 1108 stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value, 1109 (value != 1 ? "s" : ""), (needs_at ? "\"" : "")); 1110 1111 return true; 1112 } 1113 1114 bool lldb_private::formatters::ObjCBOOLSummaryProvider( 1115 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1116 const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); 1117 1118 ValueObjectSP real_guy_sp = valobj.GetSP(); 1119 1120 if (type_info & eTypeIsPointer) { 1121 Status err; 1122 real_guy_sp = valobj.Dereference(err); 1123 if (err.Fail() || !real_guy_sp) 1124 return false; 1125 } else if (type_info & eTypeIsReference) { 1126 real_guy_sp = valobj.GetChildAtIndex(0, true); 1127 if (!real_guy_sp) 1128 return false; 1129 } 1130 int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF); 1131 switch (value) { 1132 case 0: 1133 stream.Printf("NO"); 1134 break; 1135 case 1: 1136 stream.Printf("YES"); 1137 break; 1138 default: 1139 stream.Printf("%d", value); 1140 break; 1141 } 1142 return true; 1143 } 1144 1145 bool lldb_private::formatters::ObjCBooleanSummaryProvider( 1146 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1147 lldb::addr_t valobj_ptr_value = 1148 valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1149 if (valobj_ptr_value == LLDB_INVALID_ADDRESS) 1150 return false; 1151 1152 ProcessSP process_sp(valobj.GetProcessSP()); 1153 if (!process_sp) 1154 return false; 1155 1156 if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 1157 ObjCLanguageRuntime::Get(*process_sp))) { 1158 lldb::addr_t cf_true = LLDB_INVALID_ADDRESS, 1159 cf_false = LLDB_INVALID_ADDRESS; 1160 objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false); 1161 if (valobj_ptr_value == cf_true) { 1162 stream.PutCString("YES"); 1163 return true; 1164 } 1165 if (valobj_ptr_value == cf_false) { 1166 stream.PutCString("NO"); 1167 return true; 1168 } 1169 } 1170 1171 return false; 1172 } 1173 1174 template <bool is_sel_ptr> 1175 bool lldb_private::formatters::ObjCSELSummaryProvider( 1176 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1177 lldb::ValueObjectSP valobj_sp; 1178 1179 CompilerType charstar(valobj.GetCompilerType() 1180 .GetBasicTypeFromAST(eBasicTypeChar) 1181 .GetPointerType()); 1182 1183 if (!charstar) 1184 return false; 1185 1186 ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); 1187 1188 if (is_sel_ptr) { 1189 lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 1190 if (data_address == LLDB_INVALID_ADDRESS) 1191 return false; 1192 valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, 1193 exe_ctx, charstar); 1194 } else { 1195 DataExtractor data; 1196 Status error; 1197 valobj.GetData(data, error); 1198 if (error.Fail()) 1199 return false; 1200 valobj_sp = 1201 ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); 1202 } 1203 1204 if (!valobj_sp) 1205 return false; 1206 1207 stream.Printf("%s", valobj_sp->GetSummaryAsCString()); 1208 return true; 1209 } 1210 1211 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 1212 // this call gives the POSIX equivalent of the Cocoa epoch 1213 time_t lldb_private::formatters::GetOSXEpoch() { 1214 static time_t epoch = 0; 1215 if (!epoch) { 1216 #ifndef _WIN32 1217 tzset(); 1218 tm tm_epoch; 1219 tm_epoch.tm_sec = 0; 1220 tm_epoch.tm_hour = 0; 1221 tm_epoch.tm_min = 0; 1222 tm_epoch.tm_mon = 0; 1223 tm_epoch.tm_mday = 1; 1224 tm_epoch.tm_year = 2001 - 1900; 1225 tm_epoch.tm_isdst = -1; 1226 tm_epoch.tm_gmtoff = 0; 1227 tm_epoch.tm_zone = nullptr; 1228 epoch = timegm(&tm_epoch); 1229 #endif 1230 } 1231 return epoch; 1232 } 1233 1234 template bool lldb_private::formatters::NSDataSummaryProvider<true>( 1235 ValueObject &, Stream &, const TypeSummaryOptions &); 1236 1237 template bool lldb_private::formatters::NSDataSummaryProvider<false>( 1238 ValueObject &, Stream &, const TypeSummaryOptions &); 1239 1240 template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( 1241 ValueObject &, Stream &, const TypeSummaryOptions &); 1242 1243 template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( 1244 ValueObject &, Stream &, const TypeSummaryOptions &); 1245