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