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