15ffd83dbSDimitry Andric //===-- Cocoa.cpp ---------------------------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "Cocoa.h" 10349cc55cSDimitry Andric #include "NSString.h" 11349cc55cSDimitry Andric #include "ObjCConstants.h" 120b57cec5SDimitry Andric 13349cc55cSDimitry Andric #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" 145ffd83dbSDimitry Andric #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 150b57cec5SDimitry Andric #include "lldb/Core/Mangled.h" 160b57cec5SDimitry Andric #include "lldb/Core/ValueObject.h" 170b57cec5SDimitry Andric #include "lldb/Core/ValueObjectConstResult.h" 180b57cec5SDimitry Andric #include "lldb/DataFormatters/FormattersHelpers.h" 190b57cec5SDimitry Andric #include "lldb/DataFormatters/StringPrinter.h" 200b57cec5SDimitry Andric #include "lldb/DataFormatters/TypeSummary.h" 210b57cec5SDimitry Andric #include "lldb/Host/Time.h" 220b57cec5SDimitry Andric #include "lldb/Target/Language.h" 230b57cec5SDimitry Andric #include "lldb/Target/Process.h" 240b57cec5SDimitry Andric #include "lldb/Target/Target.h" 250b57cec5SDimitry Andric #include "lldb/Utility/DataBufferHeap.h" 260b57cec5SDimitry Andric #include "lldb/Utility/Endian.h" 2781ad6265SDimitry Andric #include "lldb/Utility/LLDBLog.h" 280b57cec5SDimitry Andric #include "lldb/Utility/Status.h" 290b57cec5SDimitry Andric #include "lldb/Utility/Stream.h" 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric #include "llvm/ADT/APInt.h" 320b57cec5SDimitry Andric #include "llvm/ADT/bit.h" 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric using namespace lldb; 360b57cec5SDimitry Andric using namespace lldb_private; 370b57cec5SDimitry Andric using namespace lldb_private::formatters; 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric bool lldb_private::formatters::NSBundleSummaryProvider( 400b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 410b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 420b57cec5SDimitry Andric if (!process_sp) 430b57cec5SDimitry Andric return false; 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric if (!runtime) 480b57cec5SDimitry Andric return false; 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 510b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 540b57cec5SDimitry Andric return false; 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric if (!valobj_addr) 610b57cec5SDimitry Andric return false; 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric if (class_name.empty()) 660b57cec5SDimitry Andric return false; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric if (class_name == "NSBundle") { 690b57cec5SDimitry Andric uint64_t offset = 5 * ptr_size; 700b57cec5SDimitry Andric ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 710b57cec5SDimitry Andric offset, 720b57cec5SDimitry Andric valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), 730b57cec5SDimitry Andric true)); 740b57cec5SDimitry Andric 75e8d8bef9SDimitry Andric if (!text) 76e8d8bef9SDimitry Andric return false; 77e8d8bef9SDimitry Andric 780b57cec5SDimitry Andric StreamString summary_stream; 790b57cec5SDimitry Andric bool was_nsstring_ok = 800b57cec5SDimitry Andric NSStringSummaryProvider(*text, summary_stream, options); 810b57cec5SDimitry Andric if (was_nsstring_ok && summary_stream.GetSize() > 0) { 820b57cec5SDimitry Andric stream.Printf("%s", summary_stream.GetData()); 830b57cec5SDimitry Andric return true; 840b57cec5SDimitry Andric } 850b57cec5SDimitry Andric } 860b57cec5SDimitry Andric 870b57cec5SDimitry Andric return false; 880b57cec5SDimitry Andric } 890b57cec5SDimitry Andric 900b57cec5SDimitry Andric bool lldb_private::formatters::NSTimeZoneSummaryProvider( 910b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 920b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 930b57cec5SDimitry Andric if (!process_sp) 940b57cec5SDimitry Andric return false; 950b57cec5SDimitry Andric 960b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 970b57cec5SDimitry Andric 980b57cec5SDimitry Andric if (!runtime) 990b57cec5SDimitry Andric return false; 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 1020b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 1030b57cec5SDimitry Andric 1040b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 1050b57cec5SDimitry Andric return false; 1060b57cec5SDimitry Andric 1070b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 1100b57cec5SDimitry Andric 1110b57cec5SDimitry Andric if (!valobj_addr) 1120b57cec5SDimitry Andric return false; 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 1150b57cec5SDimitry Andric 1160b57cec5SDimitry Andric if (class_name.empty()) 1170b57cec5SDimitry Andric return false; 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric if (class_name == "__NSTimeZone") { 1200b57cec5SDimitry Andric uint64_t offset = ptr_size; 1210b57cec5SDimitry Andric ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 1220b57cec5SDimitry Andric offset, valobj.GetCompilerType(), true)); 123e8d8bef9SDimitry Andric 124e8d8bef9SDimitry Andric if (!text) 125e8d8bef9SDimitry Andric return false; 126e8d8bef9SDimitry Andric 1270b57cec5SDimitry Andric StreamString summary_stream; 1280b57cec5SDimitry Andric bool was_nsstring_ok = 1290b57cec5SDimitry Andric NSStringSummaryProvider(*text, summary_stream, options); 1300b57cec5SDimitry Andric if (was_nsstring_ok && summary_stream.GetSize() > 0) { 1310b57cec5SDimitry Andric stream.Printf("%s", summary_stream.GetData()); 1320b57cec5SDimitry Andric return true; 1330b57cec5SDimitry Andric } 1340b57cec5SDimitry Andric } 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric return false; 1370b57cec5SDimitry Andric } 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric bool lldb_private::formatters::NSNotificationSummaryProvider( 1400b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1410b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 1420b57cec5SDimitry Andric if (!process_sp) 1430b57cec5SDimitry Andric return false; 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric if (!runtime) 1480b57cec5SDimitry Andric return false; 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 1510b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 1520b57cec5SDimitry Andric 1530b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 1540b57cec5SDimitry Andric return false; 1550b57cec5SDimitry Andric 1560b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric if (!valobj_addr) 1610b57cec5SDimitry Andric return false; 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric if (class_name.empty()) 1660b57cec5SDimitry Andric return false; 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric if (class_name == "NSConcreteNotification") { 1690b57cec5SDimitry Andric uint64_t offset = ptr_size; 1700b57cec5SDimitry Andric ValueObjectSP text(valobj.GetSyntheticChildAtOffset( 1710b57cec5SDimitry Andric offset, valobj.GetCompilerType(), true)); 172e8d8bef9SDimitry Andric 173e8d8bef9SDimitry Andric if (!text) 174e8d8bef9SDimitry Andric return false; 175e8d8bef9SDimitry Andric 1760b57cec5SDimitry Andric StreamString summary_stream; 1770b57cec5SDimitry Andric bool was_nsstring_ok = 1780b57cec5SDimitry Andric NSStringSummaryProvider(*text, summary_stream, options); 1790b57cec5SDimitry Andric if (was_nsstring_ok && summary_stream.GetSize() > 0) { 1800b57cec5SDimitry Andric stream.Printf("%s", summary_stream.GetData()); 1810b57cec5SDimitry Andric return true; 1820b57cec5SDimitry Andric } 1830b57cec5SDimitry Andric } 1840b57cec5SDimitry Andric 1850b57cec5SDimitry Andric return false; 1860b57cec5SDimitry Andric } 1870b57cec5SDimitry Andric 1880b57cec5SDimitry Andric bool lldb_private::formatters::NSMachPortSummaryProvider( 1890b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 1900b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 1910b57cec5SDimitry Andric if (!process_sp) 1920b57cec5SDimitry Andric return false; 1930b57cec5SDimitry Andric 1940b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric if (!runtime) 1970b57cec5SDimitry Andric return false; 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 2000b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 2010b57cec5SDimitry Andric 2020b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 2030b57cec5SDimitry Andric return false; 2040b57cec5SDimitry Andric 2050b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 2060b57cec5SDimitry Andric 2070b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric if (!valobj_addr) 2100b57cec5SDimitry Andric return false; 2110b57cec5SDimitry Andric 2120b57cec5SDimitry Andric llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric if (class_name.empty()) 2150b57cec5SDimitry Andric return false; 2160b57cec5SDimitry Andric 2170b57cec5SDimitry Andric uint64_t port_number = 0; 2180b57cec5SDimitry Andric 2190b57cec5SDimitry Andric if (class_name == "NSMachPort") { 2200b57cec5SDimitry Andric uint64_t offset = (ptr_size == 4 ? 12 : 20); 2210b57cec5SDimitry Andric Status error; 2220b57cec5SDimitry Andric port_number = process_sp->ReadUnsignedIntegerFromMemory( 2230b57cec5SDimitry Andric offset + valobj_addr, 4, 0, error); 2240b57cec5SDimitry Andric if (error.Success()) { 2250b57cec5SDimitry Andric stream.Printf("mach port: %u", 2260b57cec5SDimitry Andric (uint32_t)(port_number & 0x00000000FFFFFFFF)); 2270b57cec5SDimitry Andric return true; 2280b57cec5SDimitry Andric } 2290b57cec5SDimitry Andric } 2300b57cec5SDimitry Andric 2310b57cec5SDimitry Andric return false; 2320b57cec5SDimitry Andric } 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric bool lldb_private::formatters::NSIndexSetSummaryProvider( 2350b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 2360b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 2370b57cec5SDimitry Andric if (!process_sp) 2380b57cec5SDimitry Andric return false; 2390b57cec5SDimitry Andric 2405f757f3fSDimitry Andric AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 2415f757f3fSDimitry Andric ObjCLanguageRuntime::Get(*process_sp)); 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric if (!runtime) 2440b57cec5SDimitry Andric return false; 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 2470b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 2500b57cec5SDimitry Andric return false; 2510b57cec5SDimitry Andric 2520b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 2530b57cec5SDimitry Andric 2540b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric if (!valobj_addr) 2570b57cec5SDimitry Andric return false; 2580b57cec5SDimitry Andric 2590b57cec5SDimitry Andric llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 2600b57cec5SDimitry Andric 2610b57cec5SDimitry Andric if (class_name.empty()) 2620b57cec5SDimitry Andric return false; 2630b57cec5SDimitry Andric 2640b57cec5SDimitry Andric uint64_t count = 0; 2650b57cec5SDimitry Andric 2660b57cec5SDimitry Andric do { 2670b57cec5SDimitry Andric if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") { 2685f757f3fSDimitry Andric // Foundation version 2000 added a bitmask if the index set fit in 64 bits 2695f757f3fSDimitry Andric // and a Tagged Pointer version if the bitmask is small enough to fit in 2705f757f3fSDimitry Andric // the tagged pointer payload. 2715f757f3fSDimitry Andric // It also changed the layout (but not the size) of the set descriptor. 2725f757f3fSDimitry Andric 2735f757f3fSDimitry Andric // First check whether this is a tagged pointer. The bitmask will be in 2745f757f3fSDimitry Andric // the payload of the tagged pointer. 2755f757f3fSDimitry Andric uint64_t payload; 2765f757f3fSDimitry Andric if (runtime->GetFoundationVersion() >= 2000 2775f757f3fSDimitry Andric && descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload)) { 2785f757f3fSDimitry Andric count = llvm::popcount(payload); 2795f757f3fSDimitry Andric break; 2805f757f3fSDimitry Andric } 2815f757f3fSDimitry Andric // The first 32 bits describe the index set in all cases: 2820b57cec5SDimitry Andric Status error; 2830b57cec5SDimitry Andric uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory( 2840b57cec5SDimitry Andric valobj_addr + ptr_size, 4, 0, error); 2850b57cec5SDimitry Andric if (error.Fail()) 2860b57cec5SDimitry Andric return false; 2875f757f3fSDimitry Andric // Now check if the index is held in a bitmask in the object: 2885f757f3fSDimitry Andric if (runtime->GetFoundationVersion() >= 2000) { 2895f757f3fSDimitry Andric // The first two bits are "isSingleRange" and "isBitfield". If this is 2905f757f3fSDimitry Andric // a bitfield we handle it here, otherwise set mode appropriately and 2915f757f3fSDimitry Andric // the rest of the treatment is in common. 2925f757f3fSDimitry Andric if ((mode & 2) == 2) { 2935f757f3fSDimitry Andric // The bitfield is a 64 bit uint at the beginning of the data var. 2945f757f3fSDimitry Andric uint64_t bitfield = process_sp->ReadUnsignedIntegerFromMemory( 2955f757f3fSDimitry Andric valobj_addr + 2 * ptr_size, 8, 0, error); 2965f757f3fSDimitry Andric if (error.Fail()) 2975f757f3fSDimitry Andric return false; 2985f757f3fSDimitry Andric count = llvm::popcount(bitfield); 2995f757f3fSDimitry Andric break; 3005f757f3fSDimitry Andric } 3015f757f3fSDimitry Andric // It wasn't a bitfield, so read the isSingleRange from its new loc: 3025f757f3fSDimitry Andric if ((mode & 1) == 1) 3035f757f3fSDimitry Andric mode = 1; // this means the set only has one range 3045f757f3fSDimitry Andric else 3055f757f3fSDimitry Andric mode = 2; // this means the set has multiple ranges 3065f757f3fSDimitry Andric } else { 3070b57cec5SDimitry Andric // this means the set is empty - count = 0 3080b57cec5SDimitry Andric if ((mode & 1) == 1) { 3090b57cec5SDimitry Andric count = 0; 3100b57cec5SDimitry Andric break; 3110b57cec5SDimitry Andric } 3125f757f3fSDimitry Andric 3130b57cec5SDimitry Andric if ((mode & 2) == 2) 3140b57cec5SDimitry Andric mode = 1; // this means the set only has one range 3150b57cec5SDimitry Andric else 3160b57cec5SDimitry Andric mode = 2; // this means the set has multiple ranges 3175f757f3fSDimitry Andric } 3180b57cec5SDimitry Andric if (mode == 1) { 3190b57cec5SDimitry Andric count = process_sp->ReadUnsignedIntegerFromMemory( 3200b57cec5SDimitry Andric valobj_addr + 3 * ptr_size, ptr_size, 0, error); 3210b57cec5SDimitry Andric if (error.Fail()) 3220b57cec5SDimitry Andric return false; 3230b57cec5SDimitry Andric } else { 3240b57cec5SDimitry Andric // read a pointer to the data at 2*ptr_size 3250b57cec5SDimitry Andric count = process_sp->ReadUnsignedIntegerFromMemory( 3260b57cec5SDimitry Andric valobj_addr + 2 * ptr_size, ptr_size, 0, error); 3270b57cec5SDimitry Andric if (error.Fail()) 3280b57cec5SDimitry Andric return false; 3290b57cec5SDimitry Andric // read the data at 2*ptr_size from the first location 3300b57cec5SDimitry Andric count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size, 3310b57cec5SDimitry Andric ptr_size, 0, error); 3320b57cec5SDimitry Andric if (error.Fail()) 3330b57cec5SDimitry Andric return false; 3340b57cec5SDimitry Andric } 3350b57cec5SDimitry Andric } else 3360b57cec5SDimitry Andric return false; 3370b57cec5SDimitry Andric } while (false); 3380b57cec5SDimitry Andric stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es")); 3390b57cec5SDimitry Andric return true; 3400b57cec5SDimitry Andric } 3410b57cec5SDimitry Andric 3420b57cec5SDimitry Andric static void NSNumber_FormatChar(ValueObject &valobj, Stream &stream, char value, 3430b57cec5SDimitry Andric lldb::LanguageType lang) { 34406c3fb27SDimitry Andric static constexpr llvm::StringLiteral g_TypeHint("NSNumber:char"); 3450b57cec5SDimitry Andric 34606c3fb27SDimitry Andric llvm::StringRef prefix, suffix; 34706c3fb27SDimitry Andric if (Language *language = Language::FindPlugin(lang)) 34806c3fb27SDimitry Andric std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 3490b57cec5SDimitry Andric 35006c3fb27SDimitry Andric stream << prefix; 35106c3fb27SDimitry Andric stream.Printf("%hhd", value); 35206c3fb27SDimitry Andric stream << suffix; 3530b57cec5SDimitry Andric } 3540b57cec5SDimitry Andric 3550b57cec5SDimitry Andric static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream, 3560b57cec5SDimitry Andric short value, lldb::LanguageType lang) { 35706c3fb27SDimitry Andric static constexpr llvm::StringLiteral g_TypeHint("NSNumber:short"); 3580b57cec5SDimitry Andric 35906c3fb27SDimitry Andric llvm::StringRef prefix, suffix; 36006c3fb27SDimitry Andric if (Language *language = Language::FindPlugin(lang)) 36106c3fb27SDimitry Andric std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 3620b57cec5SDimitry Andric 36306c3fb27SDimitry Andric stream << prefix; 36406c3fb27SDimitry Andric stream.Printf("%hd", value); 36506c3fb27SDimitry Andric stream << suffix; 3660b57cec5SDimitry Andric } 3670b57cec5SDimitry Andric 3680b57cec5SDimitry Andric static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value, 3690b57cec5SDimitry Andric lldb::LanguageType lang) { 37006c3fb27SDimitry Andric static constexpr llvm::StringLiteral g_TypeHint("NSNumber:int"); 3710b57cec5SDimitry Andric 37206c3fb27SDimitry Andric llvm::StringRef prefix, suffix; 37306c3fb27SDimitry Andric if (Language *language = Language::FindPlugin(lang)) 37406c3fb27SDimitry Andric std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 3750b57cec5SDimitry Andric 37606c3fb27SDimitry Andric stream << prefix; 37706c3fb27SDimitry Andric stream.Printf("%d", value); 37806c3fb27SDimitry Andric stream << suffix; 3790b57cec5SDimitry Andric } 3800b57cec5SDimitry Andric 3810b57cec5SDimitry Andric static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream, 382fe6060f1SDimitry Andric int64_t value, lldb::LanguageType lang) { 38306c3fb27SDimitry Andric static constexpr llvm::StringLiteral g_TypeHint("NSNumber:long"); 3840b57cec5SDimitry Andric 38506c3fb27SDimitry Andric llvm::StringRef prefix, suffix; 38606c3fb27SDimitry Andric if (Language *language = Language::FindPlugin(lang)) 38706c3fb27SDimitry Andric std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 3880b57cec5SDimitry Andric 38906c3fb27SDimitry Andric stream << prefix; 39006c3fb27SDimitry Andric stream.Printf("%" PRId64 "", value); 39106c3fb27SDimitry Andric stream << suffix; 3920b57cec5SDimitry Andric } 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream, 3950b57cec5SDimitry Andric const llvm::APInt &value, 3960b57cec5SDimitry Andric lldb::LanguageType lang) { 39706c3fb27SDimitry Andric static constexpr llvm::StringLiteral g_TypeHint("NSNumber:int128_t"); 3980b57cec5SDimitry Andric 39906c3fb27SDimitry Andric llvm::StringRef prefix, suffix; 40006c3fb27SDimitry Andric if (Language *language = Language::FindPlugin(lang)) 40106c3fb27SDimitry Andric std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 4020b57cec5SDimitry Andric 40306c3fb27SDimitry Andric stream << prefix; 4040b57cec5SDimitry Andric const int radix = 10; 4050b57cec5SDimitry Andric const bool isSigned = true; 406fe6060f1SDimitry Andric std::string str = llvm::toString(value, radix, isSigned); 4070b57cec5SDimitry Andric stream.PutCString(str.c_str()); 40806c3fb27SDimitry Andric stream << suffix; 4090b57cec5SDimitry Andric } 4100b57cec5SDimitry Andric 4110b57cec5SDimitry Andric static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream, 4120b57cec5SDimitry Andric float value, lldb::LanguageType lang) { 41306c3fb27SDimitry Andric static constexpr llvm::StringLiteral g_TypeHint("NSNumber:float"); 4140b57cec5SDimitry Andric 41506c3fb27SDimitry Andric llvm::StringRef prefix, suffix; 41606c3fb27SDimitry Andric if (Language *language = Language::FindPlugin(lang)) 41706c3fb27SDimitry Andric std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 4180b57cec5SDimitry Andric 41906c3fb27SDimitry Andric stream << prefix; 42006c3fb27SDimitry Andric stream.Printf("%f", value); 42106c3fb27SDimitry Andric stream << suffix; 4220b57cec5SDimitry Andric } 4230b57cec5SDimitry Andric 4240b57cec5SDimitry Andric static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream, 4250b57cec5SDimitry Andric double value, lldb::LanguageType lang) { 42606c3fb27SDimitry Andric static constexpr llvm::StringLiteral g_TypeHint("NSNumber:double"); 4270b57cec5SDimitry Andric 42806c3fb27SDimitry Andric llvm::StringRef prefix, suffix; 42906c3fb27SDimitry Andric if (Language *language = Language::FindPlugin(lang)) 43006c3fb27SDimitry Andric std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 4310b57cec5SDimitry Andric 43206c3fb27SDimitry Andric stream << prefix; 43306c3fb27SDimitry Andric stream.Printf("%g", value); 43406c3fb27SDimitry Andric stream << suffix; 4350b57cec5SDimitry Andric } 4360b57cec5SDimitry Andric 4370b57cec5SDimitry Andric bool lldb_private::formatters::NSNumberSummaryProvider( 4380b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 4390b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 4400b57cec5SDimitry Andric if (!process_sp) 4410b57cec5SDimitry Andric return false; 4420b57cec5SDimitry Andric 44381ad6265SDimitry Andric Log *log = GetLog(LLDBLog::DataFormatters); 4440b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 4450b57cec5SDimitry Andric 4460b57cec5SDimitry Andric if (!runtime) 4470b57cec5SDimitry Andric return false; 4480b57cec5SDimitry Andric 4490b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 4500b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 4510b57cec5SDimitry Andric 4520b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 4530b57cec5SDimitry Andric return false; 4540b57cec5SDimitry Andric 4550b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 4560b57cec5SDimitry Andric 4570b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 4580b57cec5SDimitry Andric 4590b57cec5SDimitry Andric if (!valobj_addr) 4600b57cec5SDimitry Andric return false; 4610b57cec5SDimitry Andric 4620b57cec5SDimitry Andric llvm::StringRef class_name(descriptor->GetClassName().GetCString()); 4630b57cec5SDimitry Andric 4640b57cec5SDimitry Andric if (class_name.empty()) 4650b57cec5SDimitry Andric return false; 4660b57cec5SDimitry Andric 4670b57cec5SDimitry Andric if (class_name == "__NSCFBoolean") 4680b57cec5SDimitry Andric return ObjCBooleanSummaryProvider(valobj, stream, options); 4690b57cec5SDimitry Andric 4700b57cec5SDimitry Andric if (class_name == "NSDecimalNumber") 4710b57cec5SDimitry Andric return NSDecimalNumberSummaryProvider(valobj, stream, options); 4720b57cec5SDimitry Andric 473349cc55cSDimitry Andric if (class_name == "NSConstantIntegerNumber") { 474349cc55cSDimitry Andric Status error; 475349cc55cSDimitry Andric int64_t value = process_sp->ReadSignedIntegerFromMemory( 476349cc55cSDimitry Andric valobj_addr + 2 * ptr_size, 8, 0, error); 477349cc55cSDimitry Andric if (error.Fail()) 478349cc55cSDimitry Andric return false; 479349cc55cSDimitry Andric uint64_t encoding_addr = process_sp->ReadUnsignedIntegerFromMemory( 480349cc55cSDimitry Andric valobj_addr + ptr_size, ptr_size, 0, error); 481349cc55cSDimitry Andric if (error.Fail()) 482349cc55cSDimitry Andric return false; 483349cc55cSDimitry Andric char encoding = 484349cc55cSDimitry Andric process_sp->ReadUnsignedIntegerFromMemory(encoding_addr, 1, 0, error); 485349cc55cSDimitry Andric if (error.Fail()) 486349cc55cSDimitry Andric return false; 487349cc55cSDimitry Andric 488349cc55cSDimitry Andric switch (encoding) { 489349cc55cSDimitry Andric case _C_CHR: 490349cc55cSDimitry Andric NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 491349cc55cSDimitry Andric return true; 492349cc55cSDimitry Andric case _C_SHT: 493349cc55cSDimitry Andric NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage()); 494349cc55cSDimitry Andric return true; 495349cc55cSDimitry Andric case _C_INT: 496349cc55cSDimitry Andric NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 497349cc55cSDimitry Andric return true; 498349cc55cSDimitry Andric case _C_LNG: 499349cc55cSDimitry Andric case _C_LNG_LNG: 500349cc55cSDimitry Andric NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 501349cc55cSDimitry Andric return true; 502349cc55cSDimitry Andric 503349cc55cSDimitry Andric case _C_UCHR: 504349cc55cSDimitry Andric case _C_USHT: 505349cc55cSDimitry Andric case _C_UINT: 506349cc55cSDimitry Andric case _C_ULNG: 507349cc55cSDimitry Andric case _C_ULNG_LNG: 508349cc55cSDimitry Andric stream.Printf("%" PRIu64, value); 509349cc55cSDimitry Andric return true; 510349cc55cSDimitry Andric } 511349cc55cSDimitry Andric 512349cc55cSDimitry Andric return false; 513349cc55cSDimitry Andric } 514349cc55cSDimitry Andric 515349cc55cSDimitry Andric if (class_name == "NSConstantFloatNumber") { 516349cc55cSDimitry Andric Status error; 517349cc55cSDimitry Andric uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( 518349cc55cSDimitry Andric valobj_addr + ptr_size, 4, 0, error); 519349cc55cSDimitry Andric if (error.Fail()) 520349cc55cSDimitry Andric return false; 521349cc55cSDimitry Andric float flt_value = 0.0f; 522349cc55cSDimitry Andric memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); 523349cc55cSDimitry Andric NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 524349cc55cSDimitry Andric return true; 525349cc55cSDimitry Andric } 526349cc55cSDimitry Andric 527349cc55cSDimitry Andric if (class_name == "NSConstantDoubleNumber") { 528349cc55cSDimitry Andric Status error; 529349cc55cSDimitry Andric uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( 530349cc55cSDimitry Andric valobj_addr + ptr_size, 8, 0, error); 531349cc55cSDimitry Andric if (error.Fail()) 532349cc55cSDimitry Andric return false; 533349cc55cSDimitry Andric double dbl_value = 0.0; 534349cc55cSDimitry Andric memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); 535349cc55cSDimitry Andric NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 536349cc55cSDimitry Andric return true; 537349cc55cSDimitry Andric } 538349cc55cSDimitry Andric 5390b57cec5SDimitry Andric if (class_name == "NSNumber" || class_name == "__NSCFNumber") { 540fe6060f1SDimitry Andric int64_t value = 0; 5410b57cec5SDimitry Andric uint64_t i_bits = 0; 542fe6060f1SDimitry Andric if (descriptor->GetTaggedPointerInfoSigned(&i_bits, &value)) { 543fe6060f1SDimitry Andric // Check for "preserved" numbers. We still don't support them yet. 544fe6060f1SDimitry Andric if (i_bits & 0x8) { 545fe6060f1SDimitry Andric if (log) 546fe6060f1SDimitry Andric log->Printf( 547fe6060f1SDimitry Andric "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64, 548fe6060f1SDimitry Andric valobj_addr); 549fe6060f1SDimitry Andric return false; 550fe6060f1SDimitry Andric } 551fe6060f1SDimitry Andric 5520b57cec5SDimitry Andric switch (i_bits) { 5530b57cec5SDimitry Andric case 0: 5540b57cec5SDimitry Andric NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 5550b57cec5SDimitry Andric break; 5560b57cec5SDimitry Andric case 1: 5570b57cec5SDimitry Andric case 4: 5580b57cec5SDimitry Andric NSNumber_FormatShort(valobj, stream, (short)value, 5590b57cec5SDimitry Andric options.GetLanguage()); 5600b57cec5SDimitry Andric break; 5610b57cec5SDimitry Andric case 2: 5620b57cec5SDimitry Andric case 8: 5630b57cec5SDimitry Andric NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 5640b57cec5SDimitry Andric break; 5650b57cec5SDimitry Andric case 3: 5660b57cec5SDimitry Andric case 12: 5670b57cec5SDimitry Andric NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 5680b57cec5SDimitry Andric break; 5690b57cec5SDimitry Andric default: 5700b57cec5SDimitry Andric return false; 5710b57cec5SDimitry Andric } 5720b57cec5SDimitry Andric return true; 5730b57cec5SDimitry Andric } else { 5740b57cec5SDimitry Andric Status error; 5750b57cec5SDimitry Andric 5760b57cec5SDimitry Andric AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 5770b57cec5SDimitry Andric ObjCLanguageRuntime::Get(*process_sp)); 5780b57cec5SDimitry Andric 5790b57cec5SDimitry Andric const bool new_format = 5800b57cec5SDimitry Andric (runtime && runtime->GetFoundationVersion() >= 1400); 5810b57cec5SDimitry Andric 5820b57cec5SDimitry Andric enum class TypeCodes : int { 5830b57cec5SDimitry Andric sint8 = 0x0, 5840b57cec5SDimitry Andric sint16 = 0x1, 5850b57cec5SDimitry Andric sint32 = 0x2, 5860b57cec5SDimitry Andric sint64 = 0x3, 5870b57cec5SDimitry Andric f32 = 0x4, 5880b57cec5SDimitry Andric f64 = 0x5, 5890b57cec5SDimitry Andric sint128 = 0x6 5900b57cec5SDimitry Andric }; 5910b57cec5SDimitry Andric 5920b57cec5SDimitry Andric uint64_t data_location = valobj_addr + 2 * ptr_size; 5930b57cec5SDimitry Andric TypeCodes type_code; 5940b57cec5SDimitry Andric 5950b57cec5SDimitry Andric if (new_format) { 596fe6060f1SDimitry Andric uint64_t cfinfoa = process_sp->ReadUnsignedIntegerFromMemory( 597fe6060f1SDimitry Andric valobj_addr + ptr_size, ptr_size, 0, error); 5980b57cec5SDimitry Andric 5990b57cec5SDimitry Andric if (error.Fail()) 6000b57cec5SDimitry Andric return false; 6010b57cec5SDimitry Andric 6020b57cec5SDimitry Andric bool is_preserved_number = cfinfoa & 0x8; 6030b57cec5SDimitry Andric if (is_preserved_number) { 604fe6060f1SDimitry Andric if (log) 605fe6060f1SDimitry Andric log->Printf( 606fe6060f1SDimitry Andric "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64, 607fe6060f1SDimitry Andric valobj_addr); 6080b57cec5SDimitry Andric return false; 6090b57cec5SDimitry Andric } 6100b57cec5SDimitry Andric 6110b57cec5SDimitry Andric type_code = static_cast<TypeCodes>(cfinfoa & 0x7); 6120b57cec5SDimitry Andric } else { 613fe6060f1SDimitry Andric uint8_t data_type = process_sp->ReadUnsignedIntegerFromMemory( 614fe6060f1SDimitry Andric valobj_addr + ptr_size, 1, 0, error) & 615fe6060f1SDimitry Andric 0x1F; 6160b57cec5SDimitry Andric 6170b57cec5SDimitry Andric if (error.Fail()) 6180b57cec5SDimitry Andric return false; 6190b57cec5SDimitry Andric 6200b57cec5SDimitry Andric switch (data_type) { 621fe6060f1SDimitry Andric case 1: 622fe6060f1SDimitry Andric type_code = TypeCodes::sint8; 623fe6060f1SDimitry Andric break; 624fe6060f1SDimitry Andric case 2: 625fe6060f1SDimitry Andric type_code = TypeCodes::sint16; 626fe6060f1SDimitry Andric break; 627fe6060f1SDimitry Andric case 3: 628fe6060f1SDimitry Andric type_code = TypeCodes::sint32; 629fe6060f1SDimitry Andric break; 630fe6060f1SDimitry Andric case 17: 631fe6060f1SDimitry Andric data_location += 8; 632bdd1243dSDimitry Andric [[fallthrough]]; 633fe6060f1SDimitry Andric case 4: 634fe6060f1SDimitry Andric type_code = TypeCodes::sint64; 635fe6060f1SDimitry Andric break; 636fe6060f1SDimitry Andric case 5: 637fe6060f1SDimitry Andric type_code = TypeCodes::f32; 638fe6060f1SDimitry Andric break; 639fe6060f1SDimitry Andric case 6: 640fe6060f1SDimitry Andric type_code = TypeCodes::f64; 641fe6060f1SDimitry Andric break; 642fe6060f1SDimitry Andric default: 643fe6060f1SDimitry Andric return false; 6440b57cec5SDimitry Andric } 6450b57cec5SDimitry Andric } 6460b57cec5SDimitry Andric 6470b57cec5SDimitry Andric uint64_t value = 0; 6480b57cec5SDimitry Andric bool success = false; 6490b57cec5SDimitry Andric switch (type_code) { 6500b57cec5SDimitry Andric case TypeCodes::sint8: 6510b57cec5SDimitry Andric value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, 6520b57cec5SDimitry Andric error); 6530b57cec5SDimitry Andric if (error.Fail()) 6540b57cec5SDimitry Andric return false; 6550b57cec5SDimitry Andric NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); 6560b57cec5SDimitry Andric success = true; 6570b57cec5SDimitry Andric break; 6580b57cec5SDimitry Andric case TypeCodes::sint16: 6590b57cec5SDimitry Andric value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, 6600b57cec5SDimitry Andric error); 6610b57cec5SDimitry Andric if (error.Fail()) 6620b57cec5SDimitry Andric return false; 6630b57cec5SDimitry Andric NSNumber_FormatShort(valobj, stream, (short)value, 6640b57cec5SDimitry Andric options.GetLanguage()); 6650b57cec5SDimitry Andric success = true; 6660b57cec5SDimitry Andric break; 6670b57cec5SDimitry Andric case TypeCodes::sint32: 6680b57cec5SDimitry Andric value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, 6690b57cec5SDimitry Andric error); 6700b57cec5SDimitry Andric if (error.Fail()) 6710b57cec5SDimitry Andric return false; 6720b57cec5SDimitry Andric NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); 6730b57cec5SDimitry Andric success = true; 6740b57cec5SDimitry Andric break; 6750b57cec5SDimitry Andric case TypeCodes::sint64: 6760b57cec5SDimitry Andric value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, 6770b57cec5SDimitry Andric error); 6780b57cec5SDimitry Andric if (error.Fail()) 6790b57cec5SDimitry Andric return false; 6800b57cec5SDimitry Andric NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); 6810b57cec5SDimitry Andric success = true; 6820b57cec5SDimitry Andric break; 683fe6060f1SDimitry Andric case TypeCodes::f32: { 6840b57cec5SDimitry Andric uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( 6850b57cec5SDimitry Andric data_location, 4, 0, error); 6860b57cec5SDimitry Andric if (error.Fail()) 6870b57cec5SDimitry Andric return false; 6880b57cec5SDimitry Andric float flt_value = 0.0f; 6890b57cec5SDimitry Andric memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); 6900b57cec5SDimitry Andric NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); 6910b57cec5SDimitry Andric success = true; 6920b57cec5SDimitry Andric break; 6930b57cec5SDimitry Andric } 694fe6060f1SDimitry Andric case TypeCodes::f64: { 6950b57cec5SDimitry Andric uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( 6960b57cec5SDimitry Andric data_location, 8, 0, error); 6970b57cec5SDimitry Andric if (error.Fail()) 6980b57cec5SDimitry Andric return false; 6990b57cec5SDimitry Andric double dbl_value = 0.0; 7000b57cec5SDimitry Andric memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); 7010b57cec5SDimitry Andric NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); 7020b57cec5SDimitry Andric success = true; 7030b57cec5SDimitry Andric break; 7040b57cec5SDimitry Andric } 7050b57cec5SDimitry Andric case TypeCodes::sint128: // internally, this is the same 7060b57cec5SDimitry Andric { 7070b57cec5SDimitry Andric uint64_t words[2]; 708fe6060f1SDimitry Andric words[1] = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 709fe6060f1SDimitry Andric 0, error); 7100b57cec5SDimitry Andric if (error.Fail()) 7110b57cec5SDimitry Andric return false; 712fe6060f1SDimitry Andric words[0] = process_sp->ReadUnsignedIntegerFromMemory(data_location + 8, 713fe6060f1SDimitry Andric 8, 0, error); 7140b57cec5SDimitry Andric if (error.Fail()) 7150b57cec5SDimitry Andric return false; 7160b57cec5SDimitry Andric llvm::APInt i128_value(128, words); 717fe6060f1SDimitry Andric NSNumber_FormatInt128(valobj, stream, i128_value, 718fe6060f1SDimitry Andric options.GetLanguage()); 7190b57cec5SDimitry Andric success = true; 7200b57cec5SDimitry Andric break; 7210b57cec5SDimitry Andric } 7220b57cec5SDimitry Andric } 7230b57cec5SDimitry Andric return success; 7240b57cec5SDimitry Andric } 7250b57cec5SDimitry Andric } 7260b57cec5SDimitry Andric 7270b57cec5SDimitry Andric return false; 7280b57cec5SDimitry Andric } 7290b57cec5SDimitry Andric 7300b57cec5SDimitry Andric bool lldb_private::formatters::NSDecimalNumberSummaryProvider( 7310b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 7320b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 7330b57cec5SDimitry Andric if (!process_sp) 7340b57cec5SDimitry Andric return false; 7350b57cec5SDimitry Andric 7360b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 7370b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 7380b57cec5SDimitry Andric 7390b57cec5SDimitry Andric Status error; 7400b57cec5SDimitry Andric int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory( 7410b57cec5SDimitry Andric valobj_addr + ptr_size, 1, 0, error); 7420b57cec5SDimitry Andric if (error.Fail()) 7430b57cec5SDimitry Andric return false; 7440b57cec5SDimitry Andric 7450b57cec5SDimitry Andric uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory( 7460b57cec5SDimitry Andric valobj_addr + ptr_size + 1, 1, 0, error); 7470b57cec5SDimitry Andric if (error.Fail()) 7480b57cec5SDimitry Andric return false; 7490b57cec5SDimitry Andric 7500b57cec5SDimitry Andric // Fifth bit marks negativity. 7510b57cec5SDimitry Andric const bool is_negative = (length_and_negative >> 4) & 1; 7520b57cec5SDimitry Andric 7530b57cec5SDimitry Andric // Zero length and negative means NaN. 7540b57cec5SDimitry Andric uint8_t length = length_and_negative & 0xf; 7550b57cec5SDimitry Andric const bool is_nan = is_negative && (length == 0); 7560b57cec5SDimitry Andric 7570b57cec5SDimitry Andric if (is_nan) { 7580b57cec5SDimitry Andric stream.Printf("NaN"); 7590b57cec5SDimitry Andric return true; 7600b57cec5SDimitry Andric } 7610b57cec5SDimitry Andric 7620b57cec5SDimitry Andric if (length == 0) { 7630b57cec5SDimitry Andric stream.Printf("0"); 7640b57cec5SDimitry Andric return true; 7650b57cec5SDimitry Andric } 7660b57cec5SDimitry Andric 7670b57cec5SDimitry Andric uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory( 7680b57cec5SDimitry Andric valobj_addr + ptr_size + 4, 8, 0, error); 7690b57cec5SDimitry Andric if (error.Fail()) 7700b57cec5SDimitry Andric return false; 7710b57cec5SDimitry Andric 7720b57cec5SDimitry Andric if (is_negative) 7730b57cec5SDimitry Andric stream.Printf("-"); 7740b57cec5SDimitry Andric 7750b57cec5SDimitry Andric stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent); 7760b57cec5SDimitry Andric return true; 7770b57cec5SDimitry Andric } 7780b57cec5SDimitry Andric 7790b57cec5SDimitry Andric bool lldb_private::formatters::NSURLSummaryProvider( 7800b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 7810b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 7820b57cec5SDimitry Andric if (!process_sp) 7830b57cec5SDimitry Andric return false; 7840b57cec5SDimitry Andric 7850b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 7860b57cec5SDimitry Andric 7870b57cec5SDimitry Andric if (!runtime) 7880b57cec5SDimitry Andric return false; 7890b57cec5SDimitry Andric 7900b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 7910b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 7920b57cec5SDimitry Andric 7930b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 7940b57cec5SDimitry Andric return false; 7950b57cec5SDimitry Andric 7960b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 7970b57cec5SDimitry Andric 7980b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 7990b57cec5SDimitry Andric 8000b57cec5SDimitry Andric if (!valobj_addr) 8010b57cec5SDimitry Andric return false; 8020b57cec5SDimitry Andric 8030b57cec5SDimitry Andric llvm::StringRef class_name = descriptor->GetClassName().GetStringRef(); 8040b57cec5SDimitry Andric 805*0fca6ea1SDimitry Andric if (class_name != "NSURL") 8060b57cec5SDimitry Andric return false; 8070b57cec5SDimitry Andric 8080b57cec5SDimitry Andric uint64_t offset_text = ptr_size + ptr_size + 8090b57cec5SDimitry Andric 8; // ISA + pointer + 8 bytes of data (even on 32bit) 8100b57cec5SDimitry Andric uint64_t offset_base = offset_text + ptr_size; 8110b57cec5SDimitry Andric CompilerType type(valobj.GetCompilerType()); 8120b57cec5SDimitry Andric ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); 8130b57cec5SDimitry Andric ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); 814480093f4SDimitry Andric if (!text || text->GetValueAsUnsigned(0) == 0) 8150b57cec5SDimitry Andric return false; 8160b57cec5SDimitry Andric 8170b57cec5SDimitry Andric StreamString base_summary; 818480093f4SDimitry Andric if (base && base->GetValueAsUnsigned(0)) { 819480093f4SDimitry Andric if (!NSURLSummaryProvider(*base, base_summary, options)) 820480093f4SDimitry Andric base_summary.Clear(); 8210b57cec5SDimitry Andric } 822480093f4SDimitry Andric if (base_summary.Empty()) 823480093f4SDimitry Andric return NSStringSummaryProvider(*text, stream, options); 824480093f4SDimitry Andric 825480093f4SDimitry Andric StreamString summary; 826480093f4SDimitry Andric if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty()) 827480093f4SDimitry Andric return false; 828480093f4SDimitry Andric 82906c3fb27SDimitry Andric static constexpr llvm::StringLiteral quote_char("\""); 83006c3fb27SDimitry Andric static constexpr llvm::StringLiteral g_TypeHint("NSString"); 83106c3fb27SDimitry Andric llvm::StringRef prefix, suffix; 83206c3fb27SDimitry Andric if (Language *language = Language::FindPlugin(options.GetLanguage())) 83306c3fb27SDimitry Andric std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); 83406c3fb27SDimitry Andric 835480093f4SDimitry Andric // @"A" -> @"A 836480093f4SDimitry Andric llvm::StringRef summary_str = summary.GetString(); 83706c3fb27SDimitry Andric bool back_consumed = 83806c3fb27SDimitry Andric summary_str.consume_back(suffix) && summary_str.consume_back(quote_char); 839480093f4SDimitry Andric assert(back_consumed); 840480093f4SDimitry Andric UNUSED_IF_ASSERT_DISABLED(back_consumed); 841480093f4SDimitry Andric // @"B" -> B" 842480093f4SDimitry Andric llvm::StringRef base_summary_str = base_summary.GetString(); 84306c3fb27SDimitry Andric bool front_consumed = base_summary_str.consume_front(prefix) && 84406c3fb27SDimitry Andric base_summary_str.consume_front(quote_char); 845480093f4SDimitry Andric assert(front_consumed); 846480093f4SDimitry Andric UNUSED_IF_ASSERT_DISABLED(front_consumed); 847480093f4SDimitry Andric // @"A -- B" 848480093f4SDimitry Andric if (!summary_str.empty() && !base_summary_str.empty()) { 84906c3fb27SDimitry Andric stream << summary_str << " -- " << base_summary_str; 8500b57cec5SDimitry Andric return true; 8510b57cec5SDimitry Andric } 8520b57cec5SDimitry Andric 8530b57cec5SDimitry Andric return false; 8540b57cec5SDimitry Andric } 8550b57cec5SDimitry Andric 8560b57cec5SDimitry Andric /// Bias value for tagged pointer exponents. 8570b57cec5SDimitry Andric /// Recommended values: 8580b57cec5SDimitry Andric /// 0x3e3: encodes all dates between distantPast and distantFuture 8590b57cec5SDimitry Andric /// except for the range within about 1e-28 second of the reference date. 8600b57cec5SDimitry Andric /// 0x3ef: encodes all dates for a few million years beyond distantPast and 8610b57cec5SDimitry Andric /// distantFuture, except within about 1e-25 second of the reference date. 8620b57cec5SDimitry Andric const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef; 8630b57cec5SDimitry Andric 8640b57cec5SDimitry Andric struct DoubleBits { 8650b57cec5SDimitry Andric uint64_t fraction : 52; // unsigned 8660b57cec5SDimitry Andric uint64_t exponent : 11; // signed 8670b57cec5SDimitry Andric uint64_t sign : 1; 8680b57cec5SDimitry Andric }; 8690b57cec5SDimitry Andric 8700b57cec5SDimitry Andric struct TaggedDoubleBits { 8710b57cec5SDimitry Andric uint64_t fraction : 52; // unsigned 8720b57cec5SDimitry Andric uint64_t exponent : 7; // signed 8730b57cec5SDimitry Andric uint64_t sign : 1; 8740b57cec5SDimitry Andric uint64_t unused : 4; // placeholder for pointer tag bits 8750b57cec5SDimitry Andric }; 8760b57cec5SDimitry Andric 8770b57cec5SDimitry Andric static uint64_t decodeExponent(uint64_t exp) { 8780b57cec5SDimitry Andric // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits 8790b57cec5SDimitry Andric // before performing arithmetic. 8800b57cec5SDimitry Andric return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS; 8810b57cec5SDimitry Andric } 8820b57cec5SDimitry Andric 8830b57cec5SDimitry Andric static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) { 8840b57cec5SDimitry Andric if (encodedTimeInterval == 0) 8850b57cec5SDimitry Andric return 0.0; 8860b57cec5SDimitry Andric if (encodedTimeInterval == std::numeric_limits<uint64_t>::max()) 8870b57cec5SDimitry Andric return (uint64_t)-0.0; 8880b57cec5SDimitry Andric 8890b57cec5SDimitry Andric TaggedDoubleBits encodedBits = 8900b57cec5SDimitry Andric llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval); 8910b57cec5SDimitry Andric assert(encodedBits.unused == 0); 8920b57cec5SDimitry Andric 8930b57cec5SDimitry Andric // Sign and fraction are represented exactly. 8940b57cec5SDimitry Andric // Exponent is encoded. 8950b57cec5SDimitry Andric DoubleBits decodedBits; 8960b57cec5SDimitry Andric decodedBits.sign = encodedBits.sign; 8970b57cec5SDimitry Andric decodedBits.fraction = encodedBits.fraction; 8980b57cec5SDimitry Andric decodedBits.exponent = decodeExponent(encodedBits.exponent); 8990b57cec5SDimitry Andric 9000b57cec5SDimitry Andric return llvm::bit_cast<double>(decodedBits); 9010b57cec5SDimitry Andric } 9020b57cec5SDimitry Andric 9030b57cec5SDimitry Andric bool lldb_private::formatters::NSDateSummaryProvider( 9040b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 9050b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 9060b57cec5SDimitry Andric if (!process_sp) 9070b57cec5SDimitry Andric return false; 9080b57cec5SDimitry Andric 9090b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 9100b57cec5SDimitry Andric 9110b57cec5SDimitry Andric if (!runtime) 9120b57cec5SDimitry Andric return false; 9130b57cec5SDimitry Andric 9140b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 9150b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 9160b57cec5SDimitry Andric 9170b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 9180b57cec5SDimitry Andric return false; 9190b57cec5SDimitry Andric 9200b57cec5SDimitry Andric uint32_t ptr_size = process_sp->GetAddressByteSize(); 9210b57cec5SDimitry Andric 9220b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 9230b57cec5SDimitry Andric 9240b57cec5SDimitry Andric if (!valobj_addr) 9250b57cec5SDimitry Andric return false; 9260b57cec5SDimitry Andric 9270b57cec5SDimitry Andric uint64_t date_value_bits = 0; 9280b57cec5SDimitry Andric double date_value = 0.0; 9290b57cec5SDimitry Andric 9300b57cec5SDimitry Andric ConstString class_name = descriptor->GetClassName(); 9310b57cec5SDimitry Andric 9320b57cec5SDimitry Andric static const ConstString g_NSDate("NSDate"); 93381ad6265SDimitry Andric static const ConstString g_dunder_NSDate("__NSDate"); 93481ad6265SDimitry Andric static const ConstString g_NSTaggedDate("__NSTaggedDate"); 9350b57cec5SDimitry Andric static const ConstString g_NSCalendarDate("NSCalendarDate"); 9365ffd83dbSDimitry Andric static const ConstString g_NSConstantDate("NSConstantDate"); 9370b57cec5SDimitry Andric 9380b57cec5SDimitry Andric if (class_name.IsEmpty()) 9390b57cec5SDimitry Andric return false; 9400b57cec5SDimitry Andric 9410b57cec5SDimitry Andric uint64_t info_bits = 0, value_bits = 0; 94281ad6265SDimitry Andric if ((class_name == g_NSDate) || (class_name == g_dunder_NSDate) || 94381ad6265SDimitry Andric (class_name == g_NSTaggedDate) || (class_name == g_NSConstantDate)) { 9440b57cec5SDimitry Andric if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { 9450b57cec5SDimitry Andric date_value_bits = ((value_bits << 8) | (info_bits << 4)); 9460b57cec5SDimitry Andric memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 9470b57cec5SDimitry Andric } else { 9480b57cec5SDimitry Andric llvm::Triple triple( 9490b57cec5SDimitry Andric process_sp->GetTarget().GetArchitecture().GetTriple()); 9500b57cec5SDimitry Andric uint32_t delta = 9510b57cec5SDimitry Andric (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; 9520b57cec5SDimitry Andric Status error; 9530b57cec5SDimitry Andric date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 9540b57cec5SDimitry Andric valobj_addr + delta, 8, 0, error); 9550b57cec5SDimitry Andric memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 9560b57cec5SDimitry Andric if (error.Fail()) 9570b57cec5SDimitry Andric return false; 9580b57cec5SDimitry Andric } 9590b57cec5SDimitry Andric } else if (class_name == g_NSCalendarDate) { 9600b57cec5SDimitry Andric Status error; 9610b57cec5SDimitry Andric date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( 9620b57cec5SDimitry Andric valobj_addr + 2 * ptr_size, 8, 0, error); 9630b57cec5SDimitry Andric memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); 9640b57cec5SDimitry Andric if (error.Fail()) 9650b57cec5SDimitry Andric return false; 9660b57cec5SDimitry Andric } else 9670b57cec5SDimitry Andric return false; 9680b57cec5SDimitry Andric 9695ffd83dbSDimitry Andric // FIXME: It seems old dates are not formatted according to NSDate's calendar 9705ffd83dbSDimitry Andric // so we hardcode distantPast's value so that it looks like LLDB is doing 9715ffd83dbSDimitry Andric // the right thing. 9725ffd83dbSDimitry Andric 9735ffd83dbSDimitry Andric // The relative time in seconds from Cocoa Epoch to [NSDate distantPast]. 9745ffd83dbSDimitry Andric const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800; 9755ffd83dbSDimitry Andric if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) { 9765ffd83dbSDimitry Andric stream.Printf("0001-01-01 00:00:00 UTC"); 9770b57cec5SDimitry Andric return true; 9780b57cec5SDimitry Andric } 9790b57cec5SDimitry Andric 9800b57cec5SDimitry Andric // Accomodate for the __NSTaggedDate format introduced in Foundation 1600. 98181ad6265SDimitry Andric if (class_name == g_NSTaggedDate) { 9820b57cec5SDimitry Andric auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 9830b57cec5SDimitry Andric ObjCLanguageRuntime::Get(*process_sp)); 9840b57cec5SDimitry Andric if (runtime && runtime->GetFoundationVersion() >= 1600) 9850b57cec5SDimitry Andric date_value = decodeTaggedTimeInterval(value_bits << 4); 9860b57cec5SDimitry Andric } 9870b57cec5SDimitry Andric 9880b57cec5SDimitry Andric // this snippet of code assumes that time_t == seconds since Jan-1-1970 this 9890b57cec5SDimitry Andric // is generally true and POSIXly happy, but might break if a library vendor 9900b57cec5SDimitry Andric // decides to get creative 9910b57cec5SDimitry Andric time_t epoch = GetOSXEpoch(); 9925ffd83dbSDimitry Andric epoch = epoch + static_cast<time_t>(std::floor(date_value)); 9930b57cec5SDimitry Andric tm *tm_date = gmtime(&epoch); 9940b57cec5SDimitry Andric if (!tm_date) 9950b57cec5SDimitry Andric return false; 9960b57cec5SDimitry Andric std::string buffer(1024, 0); 9970b57cec5SDimitry Andric if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) 9980b57cec5SDimitry Andric return false; 9990b57cec5SDimitry Andric stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900, 10000b57cec5SDimitry Andric tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour, 10010b57cec5SDimitry Andric tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); 10020b57cec5SDimitry Andric return true; 10030b57cec5SDimitry Andric } 10040b57cec5SDimitry Andric 10050b57cec5SDimitry Andric bool lldb_private::formatters::ObjCClassSummaryProvider( 10060b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 10070b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 10080b57cec5SDimitry Andric if (!process_sp) 10090b57cec5SDimitry Andric return false; 10100b57cec5SDimitry Andric 10110b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 10120b57cec5SDimitry Andric 10130b57cec5SDimitry Andric if (!runtime) 10140b57cec5SDimitry Andric return false; 10150b57cec5SDimitry Andric 10160b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 10170b57cec5SDimitry Andric runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); 10180b57cec5SDimitry Andric 10190b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 10200b57cec5SDimitry Andric return false; 10210b57cec5SDimitry Andric 10220b57cec5SDimitry Andric ConstString class_name = descriptor->GetClassName(); 10230b57cec5SDimitry Andric 10240b57cec5SDimitry Andric if (class_name.IsEmpty()) 10250b57cec5SDimitry Andric return false; 10260b57cec5SDimitry Andric 10275ffd83dbSDimitry Andric if (ConstString cs = Mangled(class_name).GetDemangledName()) 10280b57cec5SDimitry Andric class_name = cs; 10290b57cec5SDimitry Andric 10300b57cec5SDimitry Andric stream.Printf("%s", class_name.AsCString("<unknown class>")); 10310b57cec5SDimitry Andric return true; 10320b57cec5SDimitry Andric } 10330b57cec5SDimitry Andric 10340b57cec5SDimitry Andric class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd { 10350b57cec5SDimitry Andric public: 10360b57cec5SDimitry Andric ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp) 10370b57cec5SDimitry Andric : SyntheticChildrenFrontEnd(*valobj_sp) {} 10380b57cec5SDimitry Andric 10390b57cec5SDimitry Andric ~ObjCClassSyntheticChildrenFrontEnd() override = default; 10400b57cec5SDimitry Andric 1041*0fca6ea1SDimitry Andric llvm::Expected<uint32_t> CalculateNumChildren() override { return 0; } 10420b57cec5SDimitry Andric 1043*0fca6ea1SDimitry Andric lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { 10440b57cec5SDimitry Andric return lldb::ValueObjectSP(); 10450b57cec5SDimitry Andric } 10460b57cec5SDimitry Andric 1047*0fca6ea1SDimitry Andric lldb::ChildCacheState Update() override { 1048*0fca6ea1SDimitry Andric return lldb::ChildCacheState::eRefetch; 1049*0fca6ea1SDimitry Andric } 10500b57cec5SDimitry Andric 10510b57cec5SDimitry Andric bool MightHaveChildren() override { return false; } 10520b57cec5SDimitry Andric 10530b57cec5SDimitry Andric size_t GetIndexOfChildWithName(ConstString name) override { 10540b57cec5SDimitry Andric return UINT32_MAX; 10550b57cec5SDimitry Andric } 10560b57cec5SDimitry Andric }; 10570b57cec5SDimitry Andric 10580b57cec5SDimitry Andric SyntheticChildrenFrontEnd * 10590b57cec5SDimitry Andric lldb_private::formatters::ObjCClassSyntheticFrontEndCreator( 10600b57cec5SDimitry Andric CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 10610b57cec5SDimitry Andric return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); 10620b57cec5SDimitry Andric } 10630b57cec5SDimitry Andric 10640b57cec5SDimitry Andric template <bool needs_at> 10650b57cec5SDimitry Andric bool lldb_private::formatters::NSDataSummaryProvider( 10660b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 10670b57cec5SDimitry Andric ProcessSP process_sp = valobj.GetProcessSP(); 10680b57cec5SDimitry Andric if (!process_sp) 10690b57cec5SDimitry Andric return false; 10700b57cec5SDimitry Andric 10710b57cec5SDimitry Andric ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 10720b57cec5SDimitry Andric 10730b57cec5SDimitry Andric if (!runtime) 10740b57cec5SDimitry Andric return false; 10750b57cec5SDimitry Andric 10760b57cec5SDimitry Andric ObjCLanguageRuntime::ClassDescriptorSP descriptor( 10770b57cec5SDimitry Andric runtime->GetClassDescriptor(valobj)); 10780b57cec5SDimitry Andric 10790b57cec5SDimitry Andric if (!descriptor || !descriptor->IsValid()) 10800b57cec5SDimitry Andric return false; 10810b57cec5SDimitry Andric 10820b57cec5SDimitry Andric bool is_64bit = (process_sp->GetAddressByteSize() == 8); 10830b57cec5SDimitry Andric lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 10840b57cec5SDimitry Andric 10850b57cec5SDimitry Andric if (!valobj_addr) 10860b57cec5SDimitry Andric return false; 10870b57cec5SDimitry Andric 10880b57cec5SDimitry Andric uint64_t value = 0; 10890b57cec5SDimitry Andric 10900b57cec5SDimitry Andric llvm::StringRef class_name = descriptor->GetClassName().GetCString(); 10910b57cec5SDimitry Andric 10920b57cec5SDimitry Andric if (class_name.empty()) 10930b57cec5SDimitry Andric return false; 10940b57cec5SDimitry Andric 10950b57cec5SDimitry Andric bool isNSConcreteData = class_name == "NSConcreteData"; 10960b57cec5SDimitry Andric bool isNSConcreteMutableData = class_name == "NSConcreteMutableData"; 10970b57cec5SDimitry Andric bool isNSCFData = class_name == "__NSCFData"; 10980b57cec5SDimitry Andric if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) { 10990b57cec5SDimitry Andric uint32_t offset; 11000b57cec5SDimitry Andric if (isNSConcreteData) 11010b57cec5SDimitry Andric offset = is_64bit ? 8 : 4; 11020b57cec5SDimitry Andric else 11030b57cec5SDimitry Andric offset = is_64bit ? 16 : 8; 11040b57cec5SDimitry Andric 11050b57cec5SDimitry Andric Status error; 11060b57cec5SDimitry Andric value = process_sp->ReadUnsignedIntegerFromMemory( 11070b57cec5SDimitry Andric valobj_addr + offset, is_64bit ? 8 : 4, 0, error); 11080b57cec5SDimitry Andric if (error.Fail()) 11090b57cec5SDimitry Andric return false; 11100b57cec5SDimitry Andric } else if (class_name == "_NSInlineData") { 11110b57cec5SDimitry Andric uint32_t offset = (is_64bit ? 8 : 4); 11120b57cec5SDimitry Andric Status error; 11130b57cec5SDimitry Andric value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2, 11140b57cec5SDimitry Andric 0, error); 11150b57cec5SDimitry Andric if (error.Fail()) 11160b57cec5SDimitry Andric return false; 11170b57cec5SDimitry Andric } else if (class_name == "_NSZeroData") { 11180b57cec5SDimitry Andric value = 0; 11190b57cec5SDimitry Andric } else 11200b57cec5SDimitry Andric return false; 11210b57cec5SDimitry Andric 11220b57cec5SDimitry Andric stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value, 11230b57cec5SDimitry Andric (value != 1 ? "s" : ""), (needs_at ? "\"" : "")); 11240b57cec5SDimitry Andric 11250b57cec5SDimitry Andric return true; 11260b57cec5SDimitry Andric } 11270b57cec5SDimitry Andric 11280b57cec5SDimitry Andric bool lldb_private::formatters::ObjCBOOLSummaryProvider( 11290b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 11300b57cec5SDimitry Andric const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); 11310b57cec5SDimitry Andric 11320b57cec5SDimitry Andric ValueObjectSP real_guy_sp = valobj.GetSP(); 11330b57cec5SDimitry Andric 11340b57cec5SDimitry Andric if (type_info & eTypeIsPointer) { 11350b57cec5SDimitry Andric Status err; 11360b57cec5SDimitry Andric real_guy_sp = valobj.Dereference(err); 11370b57cec5SDimitry Andric if (err.Fail() || !real_guy_sp) 11380b57cec5SDimitry Andric return false; 11390b57cec5SDimitry Andric } else if (type_info & eTypeIsReference) { 114006c3fb27SDimitry Andric real_guy_sp = valobj.GetChildAtIndex(0); 11410b57cec5SDimitry Andric if (!real_guy_sp) 11420b57cec5SDimitry Andric return false; 11430b57cec5SDimitry Andric } 1144e8d8bef9SDimitry Andric int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF); 11450b57cec5SDimitry Andric switch (value) { 11460b57cec5SDimitry Andric case 0: 11470b57cec5SDimitry Andric stream.Printf("NO"); 11480b57cec5SDimitry Andric break; 11490b57cec5SDimitry Andric case 1: 11500b57cec5SDimitry Andric stream.Printf("YES"); 11510b57cec5SDimitry Andric break; 11520b57cec5SDimitry Andric default: 1153e8d8bef9SDimitry Andric stream.Printf("%d", value); 11540b57cec5SDimitry Andric break; 11550b57cec5SDimitry Andric } 11560b57cec5SDimitry Andric return true; 11570b57cec5SDimitry Andric } 11580b57cec5SDimitry Andric 11590b57cec5SDimitry Andric bool lldb_private::formatters::ObjCBooleanSummaryProvider( 11600b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 11610b57cec5SDimitry Andric lldb::addr_t valobj_ptr_value = 11620b57cec5SDimitry Andric valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 11630b57cec5SDimitry Andric if (valobj_ptr_value == LLDB_INVALID_ADDRESS) 11640b57cec5SDimitry Andric return false; 11650b57cec5SDimitry Andric 11660b57cec5SDimitry Andric ProcessSP process_sp(valobj.GetProcessSP()); 11670b57cec5SDimitry Andric if (!process_sp) 11680b57cec5SDimitry Andric return false; 11690b57cec5SDimitry Andric 11700b57cec5SDimitry Andric if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( 11710b57cec5SDimitry Andric ObjCLanguageRuntime::Get(*process_sp))) { 11720b57cec5SDimitry Andric lldb::addr_t cf_true = LLDB_INVALID_ADDRESS, 11730b57cec5SDimitry Andric cf_false = LLDB_INVALID_ADDRESS; 11740b57cec5SDimitry Andric objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false); 11750b57cec5SDimitry Andric if (valobj_ptr_value == cf_true) { 11760b57cec5SDimitry Andric stream.PutCString("YES"); 11770b57cec5SDimitry Andric return true; 11780b57cec5SDimitry Andric } 11790b57cec5SDimitry Andric if (valobj_ptr_value == cf_false) { 11800b57cec5SDimitry Andric stream.PutCString("NO"); 11810b57cec5SDimitry Andric return true; 11820b57cec5SDimitry Andric } 11830b57cec5SDimitry Andric } 11840b57cec5SDimitry Andric 11850b57cec5SDimitry Andric return false; 11860b57cec5SDimitry Andric } 11870b57cec5SDimitry Andric 11880b57cec5SDimitry Andric template <bool is_sel_ptr> 11890b57cec5SDimitry Andric bool lldb_private::formatters::ObjCSELSummaryProvider( 11900b57cec5SDimitry Andric ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 11910b57cec5SDimitry Andric lldb::ValueObjectSP valobj_sp; 11920b57cec5SDimitry Andric 11930b57cec5SDimitry Andric CompilerType charstar(valobj.GetCompilerType() 11940b57cec5SDimitry Andric .GetBasicTypeFromAST(eBasicTypeChar) 11950b57cec5SDimitry Andric .GetPointerType()); 11960b57cec5SDimitry Andric 11970b57cec5SDimitry Andric if (!charstar) 11980b57cec5SDimitry Andric return false; 11990b57cec5SDimitry Andric 12000b57cec5SDimitry Andric ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); 12010b57cec5SDimitry Andric 12020b57cec5SDimitry Andric if (is_sel_ptr) { 12030b57cec5SDimitry Andric lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 12040b57cec5SDimitry Andric if (data_address == LLDB_INVALID_ADDRESS) 12050b57cec5SDimitry Andric return false; 12060b57cec5SDimitry Andric valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, 12070b57cec5SDimitry Andric exe_ctx, charstar); 12080b57cec5SDimitry Andric } else { 12090b57cec5SDimitry Andric DataExtractor data; 12100b57cec5SDimitry Andric Status error; 12110b57cec5SDimitry Andric valobj.GetData(data, error); 12120b57cec5SDimitry Andric if (error.Fail()) 12130b57cec5SDimitry Andric return false; 12140b57cec5SDimitry Andric valobj_sp = 12150b57cec5SDimitry Andric ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); 12160b57cec5SDimitry Andric } 12170b57cec5SDimitry Andric 12180b57cec5SDimitry Andric if (!valobj_sp) 12190b57cec5SDimitry Andric return false; 12200b57cec5SDimitry Andric 12210b57cec5SDimitry Andric stream.Printf("%s", valobj_sp->GetSummaryAsCString()); 12220b57cec5SDimitry Andric return true; 12230b57cec5SDimitry Andric } 12240b57cec5SDimitry Andric 12250b57cec5SDimitry Andric // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 12260b57cec5SDimitry Andric // this call gives the POSIX equivalent of the Cocoa epoch 12270b57cec5SDimitry Andric time_t lldb_private::formatters::GetOSXEpoch() { 12280b57cec5SDimitry Andric static time_t epoch = 0; 12290b57cec5SDimitry Andric if (!epoch) { 12300b57cec5SDimitry Andric #ifndef _WIN32 12310b57cec5SDimitry Andric tzset(); 12320b57cec5SDimitry Andric tm tm_epoch; 12330b57cec5SDimitry Andric tm_epoch.tm_sec = 0; 12340b57cec5SDimitry Andric tm_epoch.tm_hour = 0; 12350b57cec5SDimitry Andric tm_epoch.tm_min = 0; 12360b57cec5SDimitry Andric tm_epoch.tm_mon = 0; 12370b57cec5SDimitry Andric tm_epoch.tm_mday = 1; 12380b57cec5SDimitry Andric tm_epoch.tm_year = 2001 - 1900; 12390b57cec5SDimitry Andric tm_epoch.tm_isdst = -1; 12400b57cec5SDimitry Andric tm_epoch.tm_gmtoff = 0; 12410b57cec5SDimitry Andric tm_epoch.tm_zone = nullptr; 12420b57cec5SDimitry Andric epoch = timegm(&tm_epoch); 12430b57cec5SDimitry Andric #endif 12440b57cec5SDimitry Andric } 12450b57cec5SDimitry Andric return epoch; 12460b57cec5SDimitry Andric } 12470b57cec5SDimitry Andric 12480b57cec5SDimitry Andric template bool lldb_private::formatters::NSDataSummaryProvider<true>( 12490b57cec5SDimitry Andric ValueObject &, Stream &, const TypeSummaryOptions &); 12500b57cec5SDimitry Andric 12510b57cec5SDimitry Andric template bool lldb_private::formatters::NSDataSummaryProvider<false>( 12520b57cec5SDimitry Andric ValueObject &, Stream &, const TypeSummaryOptions &); 12530b57cec5SDimitry Andric 12540b57cec5SDimitry Andric template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( 12550b57cec5SDimitry Andric ValueObject &, Stream &, const TypeSummaryOptions &); 12560b57cec5SDimitry Andric 12570b57cec5SDimitry Andric template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( 12580b57cec5SDimitry Andric ValueObject &, Stream &, const TypeSummaryOptions &); 1259