1 //===-- Cocoa.cpp -------------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "Cocoa.h"
11 
12 #include "lldb/Core/DataBufferHeap.h"
13 #include "lldb/Core/Error.h"
14 #include "lldb/Core/Mangled.h"
15 #include "lldb/Core/Stream.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/Endian.h"
22 #include "lldb/Symbol/ClangASTContext.h"
23 #include "lldb/Target/ObjCLanguageRuntime.h"
24 #include "lldb/Target/Target.h"
25 #include "lldb/Target/Process.h"
26 #include "lldb/Utility/ProcessStructReader.h"
27 
28 #include "NSString.h"
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 using namespace lldb_private::formatters;
33 
34 bool
35 lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
36 {
37     ProcessSP process_sp = valobj.GetProcessSP();
38     if (!process_sp)
39         return false;
40 
41     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
42 
43     if (!runtime)
44         return false;
45 
46     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
47 
48     if (!descriptor.get() || !descriptor->IsValid())
49         return false;
50 
51     uint32_t ptr_size = process_sp->GetAddressByteSize();
52 
53     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
54 
55     if (!valobj_addr)
56         return false;
57 
58     const char* class_name = descriptor->GetClassName().GetCString();
59 
60     if (!class_name || !*class_name)
61         return false;
62 
63     if (!strcmp(class_name,"NSBundle"))
64     {
65         uint64_t offset = 5 * ptr_size;
66         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true));
67 
68         StreamString summary_stream;
69         bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
70         if (was_nsstring_ok && summary_stream.GetSize() > 0)
71         {
72             stream.Printf("%s",summary_stream.GetData());
73             return true;
74         }
75     }
76     // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
77     // which is encoded differently and needs to be handled by running code
78     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream);
79 }
80 
81 bool
82 lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
83 {
84     ProcessSP process_sp = valobj.GetProcessSP();
85     if (!process_sp)
86         return false;
87 
88     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
89 
90     if (!runtime)
91         return false;
92 
93     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
94 
95     if (!descriptor.get() || !descriptor->IsValid())
96         return false;
97 
98     uint32_t ptr_size = process_sp->GetAddressByteSize();
99 
100     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
101 
102     if (!valobj_addr)
103         return false;
104 
105     const char* class_name = descriptor->GetClassName().GetCString();
106 
107     if (!class_name || !*class_name)
108         return false;
109 
110     if (!strcmp(class_name,"__NSTimeZone"))
111     {
112         uint64_t offset = ptr_size;
113         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true));
114         StreamString summary_stream;
115         bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
116         if (was_nsstring_ok && summary_stream.GetSize() > 0)
117         {
118             stream.Printf("%s",summary_stream.GetData());
119             return true;
120         }
121     }
122     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
123 }
124 
125 bool
126 lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
127 {
128     ProcessSP process_sp = valobj.GetProcessSP();
129     if (!process_sp)
130         return false;
131 
132     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
133 
134     if (!runtime)
135         return false;
136 
137     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
138 
139     if (!descriptor.get() || !descriptor->IsValid())
140         return false;
141 
142     uint32_t ptr_size = process_sp->GetAddressByteSize();
143 
144     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
145 
146     if (!valobj_addr)
147         return false;
148 
149     const char* class_name = descriptor->GetClassName().GetCString();
150 
151     if (!class_name || !*class_name)
152         return false;
153 
154     if (!strcmp(class_name,"NSConcreteNotification"))
155     {
156         uint64_t offset = ptr_size;
157         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true));
158         StreamString summary_stream;
159         bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
160         if (was_nsstring_ok && summary_stream.GetSize() > 0)
161         {
162             stream.Printf("%s",summary_stream.GetData());
163             return true;
164         }
165     }
166     // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
167     // which is encoded differently and needs to be handled by running code
168     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
169 }
170 
171 bool
172 lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
173 {
174     ProcessSP process_sp = valobj.GetProcessSP();
175     if (!process_sp)
176         return false;
177 
178     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
179 
180     if (!runtime)
181         return false;
182 
183     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
184 
185     if (!descriptor.get() || !descriptor->IsValid())
186         return false;
187 
188     uint32_t ptr_size = process_sp->GetAddressByteSize();
189 
190     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
191 
192     if (!valobj_addr)
193         return false;
194 
195     const char* class_name = descriptor->GetClassName().GetCString();
196 
197     if (!class_name || !*class_name)
198         return false;
199 
200     uint64_t port_number = 0;
201 
202     do
203     {
204         if (!strcmp(class_name,"NSMachPort"))
205         {
206             uint64_t offset = (ptr_size == 4 ? 12 : 20);
207             Error error;
208             port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
209             if (error.Success())
210                 break;
211         }
212         if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
213             return false;
214     } while (false);
215 
216     stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
217     return true;
218 }
219 
220 bool
221 lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
222 {
223     ProcessSP process_sp = valobj.GetProcessSP();
224     if (!process_sp)
225         return false;
226 
227     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
228 
229     if (!runtime)
230         return false;
231 
232     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
233 
234     if (!descriptor.get() || !descriptor->IsValid())
235         return false;
236 
237     uint32_t ptr_size = process_sp->GetAddressByteSize();
238 
239     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
240 
241     if (!valobj_addr)
242         return false;
243 
244     const char* class_name = descriptor->GetClassName().GetCString();
245 
246     if (!class_name || !*class_name)
247         return false;
248 
249     uint64_t count = 0;
250 
251     do {
252         if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
253         {
254             Error error;
255             uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
256             if (error.Fail())
257                 return false;
258             // this means the set is empty - count = 0
259             if ((mode & 1) == 1)
260             {
261                 count = 0;
262                 break;
263             }
264             if ((mode & 2) == 2)
265                 mode = 1; // this means the set only has one range
266             else
267                 mode = 2; // this means the set has multiple ranges
268             if (mode == 1)
269             {
270                 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
271                 if (error.Fail())
272                     return false;
273             }
274             else
275             {
276                 // read a pointer to the data at 2*ptr_size
277                 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
278                 if (error.Fail())
279                     return false;
280                 // read the data at 2*ptr_size from the first location
281                 count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
282                 if (error.Fail())
283                     return false;
284             }
285         }
286         else
287         {
288             if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
289                 return false;
290         }
291     }  while (false);
292     stream.Printf("%" PRIu64 " index%s",
293                   count,
294                   (count == 1 ? "" : "es"));
295     return true;
296 }
297 
298 bool
299 lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
300 {
301     ProcessSP process_sp = valobj.GetProcessSP();
302     if (!process_sp)
303         return false;
304 
305     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
306 
307     if (!runtime)
308         return false;
309 
310     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
311 
312     if (!descriptor.get() || !descriptor->IsValid())
313         return false;
314 
315     uint32_t ptr_size = process_sp->GetAddressByteSize();
316 
317     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
318 
319     if (!valobj_addr)
320         return false;
321 
322     const char* class_name = descriptor->GetClassName().GetCString();
323 
324     if (!class_name || !*class_name)
325         return false;
326 
327     if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
328     {
329         uint64_t value = 0;
330         uint64_t i_bits = 0;
331         if (descriptor->GetTaggedPointerInfo(&i_bits,&value))
332         {
333             switch (i_bits)
334             {
335                 case 0:
336                     stream.Printf("(char)%hhd",(char)value);
337                     break;
338                 case 1:
339                 case 4:
340                     stream.Printf("(short)%hd",(short)value);
341                     break;
342                 case 2:
343                 case 8:
344                     stream.Printf("(int)%d",(int)value);
345                     break;
346                 case 3:
347                 case 12:
348                     stream.Printf("(long)%" PRId64,value);
349                     break;
350                 default:
351                     return false;
352             }
353             return true;
354         }
355         else
356         {
357             Error error;
358             uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F);
359             uint64_t data_location = valobj_addr + 2*ptr_size;
360             uint64_t value = 0;
361             if (error.Fail())
362                 return false;
363             switch (data_type)
364             {
365                 case 1: // 0B00001
366                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error);
367                     if (error.Fail())
368                         return false;
369                     stream.Printf("(char)%hhd",(char)value);
370                     break;
371                 case 2: // 0B0010
372                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error);
373                     if (error.Fail())
374                         return false;
375                     stream.Printf("(short)%hd",(short)value);
376                     break;
377                 case 3: // 0B0011
378                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
379                     if (error.Fail())
380                         return false;
381                     stream.Printf("(int)%d",(int)value);
382                     break;
383                 case 17: // 0B10001
384                     data_location += 8;
385                 case 4: // 0B0100
386                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
387                     if (error.Fail())
388                         return false;
389                     stream.Printf("(long)%" PRId64,value);
390                     break;
391                 case 5: // 0B0101
392                 {
393                     uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
394                     if (error.Fail())
395                         return false;
396                     float flt_value = *((float*)&flt_as_int);
397                     stream.Printf("(float)%f",flt_value);
398                     break;
399                 }
400                 case 6: // 0B0110
401                 {
402                     uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
403                     if (error.Fail())
404                         return false;
405                     double dbl_value = *((double*)&dbl_as_lng);
406                     stream.Printf("(double)%g",dbl_value);
407                     break;
408                 }
409                 default:
410                     return false;
411             }
412             return true;
413         }
414     }
415     else
416     {
417         return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream);
418     }
419 }
420 
421 bool
422 lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
423 {
424     ProcessSP process_sp = valobj.GetProcessSP();
425     if (!process_sp)
426         return false;
427 
428     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
429 
430     if (!runtime)
431         return false;
432 
433     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
434 
435     if (!descriptor.get() || !descriptor->IsValid())
436         return false;
437 
438     uint32_t ptr_size = process_sp->GetAddressByteSize();
439 
440     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
441 
442     if (!valobj_addr)
443         return false;
444 
445     const char* class_name = descriptor->GetClassName().GetCString();
446 
447     if (!class_name || !*class_name)
448         return false;
449 
450     if (strcmp(class_name, "NSURL") == 0)
451     {
452         uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit)
453         uint64_t offset_base = offset_text + ptr_size;
454         CompilerType type(valobj.GetCompilerType());
455         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
456         ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
457         if (!text)
458             return false;
459         if (text->GetValueAsUnsigned(0) == 0)
460             return false;
461         StreamString summary;
462         if (!NSStringSummaryProvider(*text, summary, options))
463             return false;
464         if (base && base->GetValueAsUnsigned(0))
465         {
466             if (summary.GetSize() > 0)
467                 summary.GetString().resize(summary.GetSize()-1);
468             summary.Printf(" -- ");
469             StreamString base_summary;
470             if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0)
471                 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData());
472         }
473         if (summary.GetSize())
474         {
475             stream.Printf("%s",summary.GetData());
476             return true;
477         }
478     }
479     else
480     {
481         return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream);
482     }
483     return false;
484 }
485 
486 bool
487 lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
488 {
489     ProcessSP process_sp = valobj.GetProcessSP();
490     if (!process_sp)
491         return false;
492 
493     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
494 
495     if (!runtime)
496         return false;
497 
498     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
499 
500     if (!descriptor.get() || !descriptor->IsValid())
501         return false;
502 
503     uint32_t ptr_size = process_sp->GetAddressByteSize();
504 
505     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
506 
507     if (!valobj_addr)
508         return false;
509 
510     uint64_t date_value_bits = 0;
511     double date_value = 0.0;
512 
513     const char* class_name = descriptor->GetClassName().GetCString();
514 
515     if (!class_name || !*class_name)
516         return false;
517 
518     if (strcmp(class_name,"NSDate") == 0 ||
519         strcmp(class_name,"__NSDate") == 0 ||
520         strcmp(class_name,"__NSTaggedDate") == 0)
521     {
522         uint64_t info_bits=0,value_bits = 0;
523         if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
524         {
525             date_value_bits = ((value_bits << 8) | (info_bits << 4));
526             date_value = *((double*)&date_value_bits);
527         }
528         else
529         {
530             Error error;
531             date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
532             date_value = *((double*)&date_value_bits);
533             if (error.Fail())
534                 return false;
535         }
536     }
537     else if (!strcmp(class_name,"NSCalendarDate"))
538     {
539         Error error;
540         date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
541         date_value = *((double*)&date_value_bits);
542         if (error.Fail())
543             return false;
544     }
545     else
546     {
547         if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false)
548             return false;
549         date_value = *((double*)&date_value_bits);
550     }
551     if (date_value == -63114076800)
552     {
553         stream.Printf("0001-12-30 00:00:00 +0000");
554         return true;
555     }
556     // this snippet of code assumes that time_t == seconds since Jan-1-1970
557     // this is generally true and POSIXly happy, but might break if a library
558     // vendor decides to get creative
559     time_t epoch = GetOSXEpoch();
560     epoch = epoch + (time_t)date_value;
561     tm *tm_date = gmtime(&epoch);
562     if (!tm_date)
563         return false;
564     std::string buffer(1024,0);
565     if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
566         return false;
567     stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year+1900, tm_date->tm_mon+1, tm_date->tm_mday, tm_date->tm_hour, tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
568     return true;
569 }
570 
571 bool
572 lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
573 {
574     ProcessSP process_sp = valobj.GetProcessSP();
575     if (!process_sp)
576         return false;
577 
578     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
579 
580     if (!runtime)
581         return false;
582 
583     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
584 
585     if (!descriptor.get() || !descriptor->IsValid())
586         return false;
587 
588     ConstString class_name = descriptor->GetClassName();
589 
590     if (class_name.IsEmpty())
591         return false;
592 
593     if (ConstString cs = Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown))
594         class_name = cs;
595 
596     stream.Printf("%s",class_name.AsCString("<unknown class>"));
597     return true;
598 }
599 
600 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd
601 {
602 public:
603     ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) :
604     SyntheticChildrenFrontEnd(*valobj_sp.get())
605     {
606     }
607 
608     virtual size_t
609     CalculateNumChildren ()
610     {
611         return 0;
612     }
613 
614     virtual lldb::ValueObjectSP
615     GetChildAtIndex (size_t idx)
616     {
617         return lldb::ValueObjectSP();
618     }
619 
620     virtual bool
621     Update()
622     {
623         return false;
624     }
625 
626     virtual bool
627     MightHaveChildren ()
628     {
629         return false;
630     }
631 
632     virtual size_t
633     GetIndexOfChildWithName (const ConstString &name)
634     {
635         return UINT32_MAX;
636     }
637 
638     virtual
639     ~ObjCClassSyntheticChildrenFrontEnd ()
640     {
641     }
642 };
643 
644 SyntheticChildrenFrontEnd*
645 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
646 {
647     return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
648 }
649 
650 template<bool needs_at>
651 bool
652 lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
653 {
654     ProcessSP process_sp = valobj.GetProcessSP();
655     if (!process_sp)
656         return false;
657 
658     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
659 
660     if (!runtime)
661         return false;
662 
663     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
664 
665     if (!descriptor.get() || !descriptor->IsValid())
666         return false;
667 
668     bool is_64bit = (process_sp->GetAddressByteSize() == 8);
669     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
670 
671     if (!valobj_addr)
672         return false;
673 
674     uint64_t value = 0;
675 
676     const char* class_name = descriptor->GetClassName().GetCString();
677 
678     if (!class_name || !*class_name)
679         return false;
680 
681     if (!strcmp(class_name,"NSConcreteData") ||
682         !strcmp(class_name,"NSConcreteMutableData") ||
683         !strcmp(class_name,"__NSCFData"))
684     {
685         uint32_t offset = (is_64bit ? 16 : 8);
686         Error error;
687         value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
688         if (error.Fail())
689             return false;
690     }
691     else
692     {
693         if (!ExtractValueFromObjCExpression(valobj, "int", "length", value))
694             return false;
695     }
696 
697     stream.Printf("%s%" PRIu64 " byte%s%s",
698                   (needs_at ? "@\"" : ""),
699                   value,
700                   (value != 1 ? "s" : ""),
701                   (needs_at ? "\"" : ""));
702 
703     return true;
704 }
705 
706 bool
707 lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
708 {
709     const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
710 
711     ValueObjectSP real_guy_sp = valobj.GetSP();
712 
713     if (type_info & eTypeIsPointer)
714     {
715         Error err;
716         real_guy_sp = valobj.Dereference(err);
717         if (err.Fail() || !real_guy_sp)
718             return false;
719     }
720     else if (type_info & eTypeIsReference)
721     {
722         real_guy_sp =  valobj.GetChildAtIndex(0, true);
723         if (!real_guy_sp)
724             return false;
725     }
726     uint64_t value = real_guy_sp->GetValueAsUnsigned(0);
727     if (value == 0)
728     {
729         stream.Printf("NO");
730         return true;
731     }
732     stream.Printf("YES");
733     return true;
734 }
735 
736 template <bool is_sel_ptr>
737 bool
738 lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
739 {
740     lldb::ValueObjectSP valobj_sp;
741 
742     CompilerType charstar (valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeChar).GetPointerType());
743 
744     if (!charstar)
745         return false;
746 
747     ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
748 
749     if (is_sel_ptr)
750     {
751         lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
752         if (data_address == LLDB_INVALID_ADDRESS)
753             return false;
754         valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, exe_ctx, charstar);
755     }
756     else
757     {
758         DataExtractor data;
759         Error error;
760         valobj.GetData(data, error);
761         if (error.Fail())
762             return false;
763         valobj_sp = ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
764     }
765 
766     if (!valobj_sp)
767         return false;
768 
769     stream.Printf("%s",valobj_sp->GetSummaryAsCString());
770     return true;
771 }
772 
773 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
774 // this call gives the POSIX equivalent of the Cocoa epoch
775 time_t
776 lldb_private::formatters::GetOSXEpoch ()
777 {
778     static time_t epoch = 0;
779     if (!epoch)
780     {
781 #ifndef _WIN32
782         tzset();
783         tm tm_epoch;
784         tm_epoch.tm_sec = 0;
785         tm_epoch.tm_hour = 0;
786         tm_epoch.tm_min = 0;
787         tm_epoch.tm_mon = 0;
788         tm_epoch.tm_mday = 1;
789         tm_epoch.tm_year = 2001-1900; // for some reason, we need to subtract 1900 from this field. not sure why.
790         tm_epoch.tm_isdst = -1;
791         tm_epoch.tm_gmtoff = 0;
792         tm_epoch.tm_zone = NULL;
793         epoch = timegm(&tm_epoch);
794 #endif
795     }
796     return epoch;
797 }
798 
799 bool
800 lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
801 {
802     stream.Printf("%s",valobj.GetObjectDescription());
803     return true;
804 }
805 
806 template bool
807 lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&) ;
808 
809 template bool
810 lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&) ;
811 
812 template bool
813 lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&) ;
814 
815 template bool
816 lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&) ;
817