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