1 //===-- Cocoa.cpp ---------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
11 #include "ObjCConstants.h"
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"
31 #include "llvm/ADT/APInt.h"
32 #include "llvm/ADT/bit.h"
36 using namespace lldb_private
;
37 using namespace lldb_private::formatters
;
39 bool lldb_private::formatters::NSBundleSummaryProvider(
40 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
41 ProcessSP process_sp
= valobj
.GetProcessSP();
45 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
50 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
51 runtime
->GetClassDescriptor(valobj
));
53 if (!descriptor
|| !descriptor
->IsValid())
56 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
58 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
63 llvm::StringRef
class_name(descriptor
->GetClassName().GetCString());
65 if (class_name
.empty())
68 if (class_name
== "NSBundle") {
69 uint64_t offset
= 5 * ptr_size
;
70 ValueObjectSP
text(valobj
.GetSyntheticChildAtOffset(
72 valobj
.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID
),
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());
90 bool lldb_private::formatters::NSTimeZoneSummaryProvider(
91 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
92 ProcessSP process_sp
= valobj
.GetProcessSP();
96 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
101 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
102 runtime
->GetClassDescriptor(valobj
));
104 if (!descriptor
|| !descriptor
->IsValid())
107 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
109 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
114 llvm::StringRef
class_name(descriptor
->GetClassName().GetCString());
116 if (class_name
.empty())
119 if (class_name
== "__NSTimeZone") {
120 uint64_t offset
= ptr_size
;
121 ValueObjectSP
text(valobj
.GetSyntheticChildAtOffset(
122 offset
, valobj
.GetCompilerType(), true));
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());
139 bool lldb_private::formatters::NSNotificationSummaryProvider(
140 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
141 ProcessSP process_sp
= valobj
.GetProcessSP();
145 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
150 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
151 runtime
->GetClassDescriptor(valobj
));
153 if (!descriptor
|| !descriptor
->IsValid())
156 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
158 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
163 llvm::StringRef
class_name(descriptor
->GetClassName().GetCString());
165 if (class_name
.empty())
168 if (class_name
== "NSConcreteNotification") {
169 uint64_t offset
= ptr_size
;
170 ValueObjectSP
text(valobj
.GetSyntheticChildAtOffset(
171 offset
, valobj
.GetCompilerType(), true));
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());
188 bool lldb_private::formatters::NSMachPortSummaryProvider(
189 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
190 ProcessSP process_sp
= valobj
.GetProcessSP();
194 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
199 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
200 runtime
->GetClassDescriptor(valobj
));
202 if (!descriptor
|| !descriptor
->IsValid())
205 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
207 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
212 llvm::StringRef
class_name(descriptor
->GetClassName().GetCString());
214 if (class_name
.empty())
217 uint64_t port_number
= 0;
219 if (class_name
== "NSMachPort") {
220 uint64_t offset
= (ptr_size
== 4 ? 12 : 20);
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));
234 bool lldb_private::formatters::NSIndexSetSummaryProvider(
235 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
236 ProcessSP process_sp
= valobj
.GetProcessSP();
240 AppleObjCRuntime
*runtime
= llvm::dyn_cast_or_null
<AppleObjCRuntime
>(
241 ObjCLanguageRuntime::Get(*process_sp
));
246 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
247 runtime
->GetClassDescriptor(valobj
));
249 if (!descriptor
|| !descriptor
->IsValid())
252 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
254 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
259 llvm::StringRef
class_name(descriptor
->GetClassName().GetCString());
261 if (class_name
.empty())
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.
273 // First check whether this is a tagged pointer. The bitmask will be in
274 // the payload of the tagged pointer.
276 if (runtime
->GetFoundationVersion() >= 2000
277 && descriptor
->GetTaggedPointerInfo(nullptr, nullptr, &payload
)) {
278 count
= llvm::popcount(payload
);
281 // The first 32 bits describe the index set in all cases:
283 uint32_t mode
= process_sp
->ReadUnsignedIntegerFromMemory(
284 valobj_addr
+ ptr_size
, 4, 0, error
);
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
);
298 count
= llvm::popcount(bitfield
);
301 // It wasn't a bitfield, so read the isSingleRange from its new loc:
303 mode
= 1; // this means the set only has one range
305 mode
= 2; // this means the set has multiple ranges
307 // this means the set is empty - count = 0
308 if ((mode
& 1) == 1) {
314 mode
= 1; // this means the set only has one range
316 mode
= 2; // this means the set has multiple ranges
319 count
= process_sp
->ReadUnsignedIntegerFromMemory(
320 valobj_addr
+ 3 * ptr_size
, ptr_size
, 0, error
);
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
);
329 // read the data at 2*ptr_size from the first location
330 count
= process_sp
->ReadUnsignedIntegerFromMemory(count
+ 2 * ptr_size
,
338 stream
.Printf("%" PRIu64
" index%s", count
, (count
== 1 ? "" : "es"));
342 static void NSNumber_FormatChar(ValueObject
&valobj
, Stream
&stream
, char value
,
343 lldb::LanguageType lang
) {
344 static constexpr llvm::StringLiteral
g_TypeHint("NSNumber:char");
346 llvm::StringRef prefix
, suffix
;
347 if (Language
*language
= Language::FindPlugin(lang
))
348 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
351 stream
.Printf("%hhd", value
);
355 static void NSNumber_FormatShort(ValueObject
&valobj
, Stream
&stream
,
356 short value
, lldb::LanguageType lang
) {
357 static constexpr llvm::StringLiteral
g_TypeHint("NSNumber:short");
359 llvm::StringRef prefix
, suffix
;
360 if (Language
*language
= Language::FindPlugin(lang
))
361 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
364 stream
.Printf("%hd", value
);
368 static void NSNumber_FormatInt(ValueObject
&valobj
, Stream
&stream
, int value
,
369 lldb::LanguageType lang
) {
370 static constexpr llvm::StringLiteral
g_TypeHint("NSNumber:int");
372 llvm::StringRef prefix
, suffix
;
373 if (Language
*language
= Language::FindPlugin(lang
))
374 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
377 stream
.Printf("%d", value
);
381 static void NSNumber_FormatLong(ValueObject
&valobj
, Stream
&stream
,
382 int64_t value
, lldb::LanguageType lang
) {
383 static constexpr llvm::StringLiteral
g_TypeHint("NSNumber:long");
385 llvm::StringRef prefix
, suffix
;
386 if (Language
*language
= Language::FindPlugin(lang
))
387 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
390 stream
.Printf("%" PRId64
"", value
);
394 static 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");
399 llvm::StringRef prefix
, suffix
;
400 if (Language
*language
= Language::FindPlugin(lang
))
401 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
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());
411 static void NSNumber_FormatFloat(ValueObject
&valobj
, Stream
&stream
,
412 float value
, lldb::LanguageType lang
) {
413 static constexpr llvm::StringLiteral
g_TypeHint("NSNumber:float");
415 llvm::StringRef prefix
, suffix
;
416 if (Language
*language
= Language::FindPlugin(lang
))
417 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
420 stream
.Printf("%f", value
);
424 static void NSNumber_FormatDouble(ValueObject
&valobj
, Stream
&stream
,
425 double value
, lldb::LanguageType lang
) {
426 static constexpr llvm::StringLiteral
g_TypeHint("NSNumber:double");
428 llvm::StringRef prefix
, suffix
;
429 if (Language
*language
= Language::FindPlugin(lang
))
430 std::tie(prefix
, suffix
) = language
->GetFormatterPrefixSuffix(g_TypeHint
);
433 stream
.Printf("%g", value
);
437 bool lldb_private::formatters::NSNumberSummaryProvider(
438 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
439 ProcessSP process_sp
= valobj
.GetProcessSP();
443 Log
*log
= GetLog(LLDBLog::DataFormatters
);
444 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
449 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
450 runtime
->GetClassDescriptor(valobj
));
452 if (!descriptor
|| !descriptor
->IsValid())
455 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
457 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
462 llvm::StringRef
class_name(descriptor
->GetClassName().GetCString());
464 if (class_name
.empty())
467 if (class_name
== "__NSCFBoolean")
468 return ObjCBooleanSummaryProvider(valobj
, stream
, options
);
470 if (class_name
== "NSDecimalNumber")
471 return NSDecimalNumberSummaryProvider(valobj
, stream
, options
);
473 if (class_name
== "NSConstantIntegerNumber") {
475 int64_t value
= process_sp
->ReadSignedIntegerFromMemory(
476 valobj_addr
+ 2 * ptr_size
, 8, 0, error
);
479 uint64_t encoding_addr
= process_sp
->ReadUnsignedIntegerFromMemory(
480 valobj_addr
+ ptr_size
, ptr_size
, 0, error
);
484 process_sp
->ReadUnsignedIntegerFromMemory(encoding_addr
, 1, 0, error
);
490 NSNumber_FormatChar(valobj
, stream
, (char)value
, options
.GetLanguage());
493 NSNumber_FormatShort(valobj
, stream
, (short)value
, options
.GetLanguage());
496 NSNumber_FormatInt(valobj
, stream
, (int)value
, options
.GetLanguage());
500 NSNumber_FormatLong(valobj
, stream
, value
, options
.GetLanguage());
508 stream
.Printf("%" PRIu64
, value
);
515 if (class_name
== "NSConstantFloatNumber") {
517 uint32_t flt_as_int
= process_sp
->ReadUnsignedIntegerFromMemory(
518 valobj_addr
+ ptr_size
, 4, 0, error
);
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());
527 if (class_name
== "NSConstantDoubleNumber") {
529 uint64_t dbl_as_lng
= process_sp
->ReadUnsignedIntegerFromMemory(
530 valobj_addr
+ ptr_size
, 8, 0, error
);
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());
539 if (class_name
== "NSNumber" || class_name
== "__NSCFNumber") {
542 if (descriptor
->GetTaggedPointerInfoSigned(&i_bits
, &value
)) {
543 // Check for "preserved" numbers. We still don't support them yet.
547 "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64
,
554 NSNumber_FormatChar(valobj
, stream
, (char)value
, options
.GetLanguage());
558 NSNumber_FormatShort(valobj
, stream
, (short)value
,
559 options
.GetLanguage());
563 NSNumber_FormatInt(valobj
, stream
, (int)value
, options
.GetLanguage());
567 NSNumber_FormatLong(valobj
, stream
, value
, options
.GetLanguage());
576 AppleObjCRuntime
*runtime
= llvm::dyn_cast_or_null
<AppleObjCRuntime
>(
577 ObjCLanguageRuntime::Get(*process_sp
));
579 const bool new_format
=
580 (runtime
&& runtime
->GetFoundationVersion() >= 1400);
582 enum class TypeCodes
: int {
592 uint64_t data_location
= valobj_addr
+ 2 * ptr_size
;
596 uint64_t cfinfoa
= process_sp
->ReadUnsignedIntegerFromMemory(
597 valobj_addr
+ ptr_size
, ptr_size
, 0, error
);
602 bool is_preserved_number
= cfinfoa
& 0x8;
603 if (is_preserved_number
) {
606 "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64
,
611 type_code
= static_cast<TypeCodes
>(cfinfoa
& 0x7);
613 uint8_t data_type
= process_sp
->ReadUnsignedIntegerFromMemory(
614 valobj_addr
+ ptr_size
, 1, 0, error
) &
622 type_code
= TypeCodes::sint8
;
625 type_code
= TypeCodes::sint16
;
628 type_code
= TypeCodes::sint32
;
634 type_code
= TypeCodes::sint64
;
637 type_code
= TypeCodes::f32
;
640 type_code
= TypeCodes::f64
;
648 bool success
= false;
650 case TypeCodes::sint8
:
651 value
= process_sp
->ReadUnsignedIntegerFromMemory(data_location
, 1, 0,
655 NSNumber_FormatChar(valobj
, stream
, (char)value
, options
.GetLanguage());
658 case TypeCodes::sint16
:
659 value
= process_sp
->ReadUnsignedIntegerFromMemory(data_location
, 2, 0,
663 NSNumber_FormatShort(valobj
, stream
, (short)value
,
664 options
.GetLanguage());
667 case TypeCodes::sint32
:
668 value
= process_sp
->ReadUnsignedIntegerFromMemory(data_location
, 4, 0,
672 NSNumber_FormatInt(valobj
, stream
, (int)value
, options
.GetLanguage());
675 case TypeCodes::sint64
:
676 value
= process_sp
->ReadUnsignedIntegerFromMemory(data_location
, 8, 0,
680 NSNumber_FormatLong(valobj
, stream
, value
, options
.GetLanguage());
683 case TypeCodes::f32
: {
684 uint32_t flt_as_int
= process_sp
->ReadUnsignedIntegerFromMemory(
685 data_location
, 4, 0, error
);
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());
694 case TypeCodes::f64
: {
695 uint64_t dbl_as_lng
= process_sp
->ReadUnsignedIntegerFromMemory(
696 data_location
, 8, 0, error
);
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());
705 case TypeCodes::sint128
: // internally, this is the same
708 words
[1] = process_sp
->ReadUnsignedIntegerFromMemory(data_location
, 8,
712 words
[0] = process_sp
->ReadUnsignedIntegerFromMemory(data_location
+ 8,
716 llvm::APInt
i128_value(128, words
);
717 NSNumber_FormatInt128(valobj
, stream
, i128_value
,
718 options
.GetLanguage());
730 bool lldb_private::formatters::NSDecimalNumberSummaryProvider(
731 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
732 ProcessSP process_sp
= valobj
.GetProcessSP();
736 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
737 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
740 int8_t exponent
= process_sp
->ReadUnsignedIntegerFromMemory(
741 valobj_addr
+ ptr_size
, 1, 0, error
);
745 uint8_t length_and_negative
= process_sp
->ReadUnsignedIntegerFromMemory(
746 valobj_addr
+ ptr_size
+ 1, 1, 0, error
);
750 // Fifth bit marks negativity.
751 const bool is_negative
= (length_and_negative
>> 4) & 1;
753 // Zero length and negative means NaN.
754 uint8_t length
= length_and_negative
& 0xf;
755 const bool is_nan
= is_negative
&& (length
== 0);
758 stream
.Printf("NaN");
767 uint64_t mantissa
= process_sp
->ReadUnsignedIntegerFromMemory(
768 valobj_addr
+ ptr_size
+ 4, 8, 0, error
);
775 stream
.Printf("%" PRIu64
" x 10^%" PRIi8
, mantissa
, exponent
);
779 bool lldb_private::formatters::NSURLSummaryProvider(
780 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
781 ProcessSP process_sp
= valobj
.GetProcessSP();
785 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
790 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
791 runtime
->GetClassDescriptor(valobj
));
793 if (!descriptor
|| !descriptor
->IsValid())
796 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
798 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
803 llvm::StringRef class_name
= descriptor
->GetClassName().GetStringRef();
805 if (!class_name
.equals("NSURL"))
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)
817 StreamString base_summary
;
818 if (base
&& base
->GetValueAsUnsigned(0)) {
819 if (!NSURLSummaryProvider(*base
, base_summary
, options
))
820 base_summary
.Clear();
822 if (base_summary
.Empty())
823 return NSStringSummaryProvider(*text
, stream
, options
);
825 StreamString summary
;
826 if (!NSStringSummaryProvider(*text
, summary
, options
) || summary
.Empty())
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
);
836 llvm::StringRef summary_str
= summary
.GetString();
838 summary_str
.consume_back(suffix
) && summary_str
.consume_back(quote_char
);
839 assert(back_consumed
);
840 UNUSED_IF_ASSERT_DISABLED(back_consumed
);
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
);
848 if (!summary_str
.empty() && !base_summary_str
.empty()) {
849 stream
<< summary_str
<< " -- " << base_summary_str
;
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.
862 const int TAGGED_DATE_EXPONENT_BIAS
= 0x3ef;
865 uint64_t fraction
: 52; // unsigned
866 uint64_t exponent
: 11; // signed
870 struct TaggedDoubleBits
{
871 uint64_t fraction
: 52; // unsigned
872 uint64_t exponent
: 7; // signed
874 uint64_t unused
: 4; // placeholder for pointer tag bits
877 static 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
;
883 static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval
) {
884 if (encodedTimeInterval
== 0)
886 if (encodedTimeInterval
== std::numeric_limits
<uint64_t>::max())
887 return (uint64_t)-0.0;
889 TaggedDoubleBits encodedBits
=
890 llvm::bit_cast
<TaggedDoubleBits
>(encodedTimeInterval
);
891 assert(encodedBits
.unused
== 0);
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
);
900 return llvm::bit_cast
<double>(decodedBits
);
903 bool lldb_private::formatters::NSDateSummaryProvider(
904 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
905 ProcessSP process_sp
= valobj
.GetProcessSP();
909 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
914 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
915 runtime
->GetClassDescriptor(valobj
));
917 if (!descriptor
|| !descriptor
->IsValid())
920 uint32_t ptr_size
= process_sp
->GetAddressByteSize();
922 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
927 uint64_t date_value_bits
= 0;
928 double date_value
= 0.0;
930 ConstString class_name
= descriptor
->GetClassName();
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");
938 if (class_name
.IsEmpty())
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
));
949 process_sp
->GetTarget().GetArchitecture().GetTriple());
951 (triple
.isWatchOS() && triple
.isWatchABI()) ? 8 : ptr_size
;
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
));
959 } else if (class_name
== g_NSCalendarDate
) {
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
));
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
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");
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);
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
);
996 std::string
buffer(1024, 0);
997 if (strftime(&buffer
[0], 1023, "%Z", tm_date
) == 0)
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());
1005 bool lldb_private::formatters::ObjCClassSummaryProvider(
1006 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
1007 ProcessSP process_sp
= valobj
.GetProcessSP();
1011 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
1016 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
1017 runtime
->GetClassDescriptorFromISA(valobj
.GetValueAsUnsigned(0)));
1019 if (!descriptor
|| !descriptor
->IsValid())
1022 ConstString class_name
= descriptor
->GetClassName();
1024 if (class_name
.IsEmpty())
1027 if (ConstString cs
= Mangled(class_name
).GetDemangledName())
1030 stream
.Printf("%s", class_name
.AsCString("<unknown class>"));
1034 class ObjCClassSyntheticChildrenFrontEnd
: public SyntheticChildrenFrontEnd
{
1036 ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp
)
1037 : SyntheticChildrenFrontEnd(*valobj_sp
) {}
1039 ~ObjCClassSyntheticChildrenFrontEnd() override
= default;
1041 size_t CalculateNumChildren() override
{ return 0; }
1043 lldb::ValueObjectSP
GetChildAtIndex(size_t idx
) override
{
1044 return lldb::ValueObjectSP();
1047 bool Update() override
{ return false; }
1049 bool MightHaveChildren() override
{ return false; }
1051 size_t GetIndexOfChildWithName(ConstString name
) override
{
1056 SyntheticChildrenFrontEnd
*
1057 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(
1058 CXXSyntheticChildren
*, lldb::ValueObjectSP valobj_sp
) {
1059 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp
);
1062 template <bool needs_at
>
1063 bool lldb_private::formatters::NSDataSummaryProvider(
1064 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
1065 ProcessSP process_sp
= valobj
.GetProcessSP();
1069 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
1074 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
1075 runtime
->GetClassDescriptor(valobj
));
1077 if (!descriptor
|| !descriptor
->IsValid())
1080 bool is_64bit
= (process_sp
->GetAddressByteSize() == 8);
1081 lldb::addr_t valobj_addr
= valobj
.GetValueAsUnsigned(0);
1088 llvm::StringRef class_name
= descriptor
->GetClassName().GetCString();
1090 if (class_name
.empty())
1093 bool isNSConcreteData
= class_name
== "NSConcreteData";
1094 bool isNSConcreteMutableData
= class_name
== "NSConcreteMutableData";
1095 bool isNSCFData
= class_name
== "__NSCFData";
1096 if (isNSConcreteData
|| isNSConcreteMutableData
|| isNSCFData
) {
1098 if (isNSConcreteData
)
1099 offset
= is_64bit
? 8 : 4;
1101 offset
= is_64bit
? 16 : 8;
1104 value
= process_sp
->ReadUnsignedIntegerFromMemory(
1105 valobj_addr
+ offset
, is_64bit
? 8 : 4, 0, error
);
1108 } else if (class_name
== "_NSInlineData") {
1109 uint32_t offset
= (is_64bit
? 8 : 4);
1111 value
= process_sp
->ReadUnsignedIntegerFromMemory(valobj_addr
+ offset
, 2,
1115 } else if (class_name
== "_NSZeroData") {
1120 stream
.Printf("%s%" PRIu64
" byte%s%s", (needs_at
? "@\"" : ""), value
,
1121 (value
!= 1 ? "s" : ""), (needs_at
? "\"" : ""));
1126 bool lldb_private::formatters::ObjCBOOLSummaryProvider(
1127 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
1128 const uint32_t type_info
= valobj
.GetCompilerType().GetTypeInfo();
1130 ValueObjectSP real_guy_sp
= valobj
.GetSP();
1132 if (type_info
& eTypeIsPointer
) {
1134 real_guy_sp
= valobj
.Dereference(err
);
1135 if (err
.Fail() || !real_guy_sp
)
1137 } else if (type_info
& eTypeIsReference
) {
1138 real_guy_sp
= valobj
.GetChildAtIndex(0);
1142 int8_t value
= (real_guy_sp
->GetValueAsSigned(0) & 0xFF);
1145 stream
.Printf("NO");
1148 stream
.Printf("YES");
1151 stream
.Printf("%d", value
);
1157 bool 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
)
1164 ProcessSP
process_sp(valobj
.GetProcessSP());
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");
1177 if (valobj_ptr_value
== cf_false
) {
1178 stream
.PutCString("NO");
1186 template <bool is_sel_ptr
>
1187 bool lldb_private::formatters::ObjCSELSummaryProvider(
1188 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
1189 lldb::ValueObjectSP valobj_sp
;
1191 CompilerType
charstar(valobj
.GetCompilerType()
1192 .GetBasicTypeFromAST(eBasicTypeChar
)
1198 ExecutionContext
exe_ctx(valobj
.GetExecutionContextRef());
1201 lldb::addr_t data_address
= valobj
.GetValueAsUnsigned(LLDB_INVALID_ADDRESS
);
1202 if (data_address
== LLDB_INVALID_ADDRESS
)
1204 valobj_sp
= ValueObject::CreateValueObjectFromAddress("text", data_address
,
1209 valobj
.GetData(data
, error
);
1213 ValueObject::CreateValueObjectFromData("text", data
, exe_ctx
, charstar
);
1219 stream
.Printf("%s", valobj_sp
->GetSummaryAsCString());
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
1225 time_t lldb_private::formatters::GetOSXEpoch() {
1226 static time_t epoch
= 0;
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
);
1246 template bool lldb_private::formatters::NSDataSummaryProvider
<true>(
1247 ValueObject
&, Stream
&, const TypeSummaryOptions
&);
1249 template bool lldb_private::formatters::NSDataSummaryProvider
<false>(
1250 ValueObject
&, Stream
&, const TypeSummaryOptions
&);
1252 template bool lldb_private::formatters::ObjCSELSummaryProvider
<true>(
1253 ValueObject
&, Stream
&, const TypeSummaryOptions
&);
1255 template bool lldb_private::formatters::ObjCSELSummaryProvider
<false>(
1256 ValueObject
&, Stream
&, const TypeSummaryOptions
&);