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