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 "lldb/lldb-python.h"
11
12#include "lldb/DataFormatters/CXXFormatterFunctions.h"
13
14#include "lldb/Core/DataBufferHeap.h"
15#include "lldb/Core/Error.h"
16#include "lldb/Core/Stream.h"
17#include "lldb/Core/ValueObject.h"
18#include "lldb/Core/ValueObjectConstResult.h"
19#include "lldb/Host/Endian.h"
20#include "lldb/Symbol/ClangASTContext.h"
21#include "lldb/Target/ObjCLanguageRuntime.h"
22#include "lldb/Target/Target.h"
23
24using namespace lldb;
25using namespace lldb_private;
26using namespace lldb_private::formatters;
27
28bool
29lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream)
30{
31    ProcessSP process_sp = valobj.GetProcessSP();
32    if (!process_sp)
33        return false;
34
35    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
36
37    if (!runtime)
38        return false;
39
40    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
41
42    if (!descriptor.get() || !descriptor->IsValid())
43        return false;
44
45    uint32_t ptr_size = process_sp->GetAddressByteSize();
46
47    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
48
49    if (!valobj_addr)
50        return false;
51
52    const char* class_name = descriptor->GetClassName().GetCString();
53
54    if (!class_name || !*class_name)
55        return false;
56
57    if (!strcmp(class_name,"NSBundle"))
58    {
59        uint64_t offset = 5 * ptr_size;
60        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true));
61
62        StreamString summary_stream;
63        bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
64        if (was_nsstring_ok && summary_stream.GetSize() > 0)
65        {
66            stream.Printf("%s",summary_stream.GetData());
67            return true;
68        }
69    }
70    // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
71    // which is encoded differently and needs to be handled by running code
72    return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream);
73}
74
75bool
76lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream)
77{
78    ProcessSP process_sp = valobj.GetProcessSP();
79    if (!process_sp)
80        return false;
81
82    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
83
84    if (!runtime)
85        return false;
86
87    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
88
89    if (!descriptor.get() || !descriptor->IsValid())
90        return false;
91
92    uint32_t ptr_size = process_sp->GetAddressByteSize();
93
94    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
95
96    if (!valobj_addr)
97        return false;
98
99    const char* class_name = descriptor->GetClassName().GetCString();
100
101    if (!class_name || !*class_name)
102        return false;
103
104    if (!strcmp(class_name,"__NSTimeZone"))
105    {
106        uint64_t offset = ptr_size;
107        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true));
108        StreamString summary_stream;
109        bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
110        if (was_nsstring_ok && summary_stream.GetSize() > 0)
111        {
112            stream.Printf("%s",summary_stream.GetData());
113            return true;
114        }
115    }
116    return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
117}
118
119bool
120lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream)
121{
122    ProcessSP process_sp = valobj.GetProcessSP();
123    if (!process_sp)
124        return false;
125
126    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
127
128    if (!runtime)
129        return false;
130
131    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
132
133    if (!descriptor.get() || !descriptor->IsValid())
134        return false;
135
136    uint32_t ptr_size = process_sp->GetAddressByteSize();
137
138    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
139
140    if (!valobj_addr)
141        return false;
142
143    const char* class_name = descriptor->GetClassName().GetCString();
144
145    if (!class_name || !*class_name)
146        return false;
147
148    if (!strcmp(class_name,"NSConcreteNotification"))
149    {
150        uint64_t offset = ptr_size;
151        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true));
152        StreamString summary_stream;
153        bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
154        if (was_nsstring_ok && summary_stream.GetSize() > 0)
155        {
156            stream.Printf("%s",summary_stream.GetData());
157            return true;
158        }
159    }
160    // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
161    // which is encoded differently and needs to be handled by running code
162    return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
163}
164
165bool
166lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream)
167{
168    ProcessSP process_sp = valobj.GetProcessSP();
169    if (!process_sp)
170        return false;
171
172    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
173
174    if (!runtime)
175        return false;
176
177    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
178
179    if (!descriptor.get() || !descriptor->IsValid())
180        return false;
181
182    uint32_t ptr_size = process_sp->GetAddressByteSize();
183
184    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
185
186    if (!valobj_addr)
187        return false;
188
189    const char* class_name = descriptor->GetClassName().GetCString();
190
191    if (!class_name || !*class_name)
192        return false;
193
194    uint64_t port_number = 0;
195
196    do
197    {
198        if (!strcmp(class_name,"NSMachPort"))
199        {
200            uint64_t offset = (ptr_size == 4 ? 12 : 20);
201            Error error;
202            port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
203            if (error.Success())
204                break;
205        }
206        if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
207            return false;
208    } while (false);
209
210    stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
211    return true;
212}
213
214bool
215lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream)
216{
217    ProcessSP process_sp = valobj.GetProcessSP();
218    if (!process_sp)
219        return false;
220
221    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
222
223    if (!runtime)
224        return false;
225
226    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
227
228    if (!descriptor.get() || !descriptor->IsValid())
229        return false;
230
231    uint32_t ptr_size = process_sp->GetAddressByteSize();
232
233    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
234
235    if (!valobj_addr)
236        return false;
237
238    const char* class_name = descriptor->GetClassName().GetCString();
239
240    if (!class_name || !*class_name)
241        return false;
242
243    uint64_t count = 0;
244
245    do {
246        if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
247        {
248            Error error;
249            uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
250            if (error.Fail())
251                return false;
252            // this means the set is empty - count = 0
253            if ((mode & 1) == 1)
254            {
255                count = 0;
256                break;
257            }
258            if ((mode & 2) == 2)
259                mode = 1; // this means the set only has one range
260            else
261                mode = 2; // this means the set has multiple ranges
262            if (mode == 1)
263            {
264                count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
265                if (error.Fail())
266                    return false;
267            }
268            else
269            {
270                // read a pointer to the data at 2*ptr_size
271                count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
272                if (error.Fail())
273                    return false;
274                // read the data at 2*ptr_size from the first location
275                count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
276                if (error.Fail())
277                    return false;
278            }
279        }
280        else
281        {
282            if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
283                return false;
284        }
285    }  while (false);
286    stream.Printf("%" PRIu64 " index%s",
287                  count,
288                  (count == 1 ? "" : "es"));
289    return true;
290}
291
292bool
293lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream)
294{
295    ProcessSP process_sp = valobj.GetProcessSP();
296    if (!process_sp)
297        return false;
298
299    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
300
301    if (!runtime)
302        return false;
303
304    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
305
306    if (!descriptor.get() || !descriptor->IsValid())
307        return false;
308
309    uint32_t ptr_size = process_sp->GetAddressByteSize();
310
311    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
312
313    if (!valobj_addr)
314        return false;
315
316    const char* class_name = descriptor->GetClassName().GetCString();
317
318    if (!class_name || !*class_name)
319        return false;
320
321    if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
322    {
323        uint64_t value = 0;
324        uint64_t i_bits = 0;
325        if (descriptor->GetTaggedPointerInfo(&i_bits,&value))
326        {
327            switch (i_bits)
328            {
329                case 0:
330                    stream.Printf("(char)%hhd",(char)value);
331                    break;
332                case 1:
333                case 4:
334                    stream.Printf("(short)%hd",(short)value);
335                    break;
336                case 2:
337                case 8:
338                    stream.Printf("(int)%d",(int)value);
339                    break;
340                case 3:
341                case 12:
342                    stream.Printf("(long)%" PRId64,value);
343                    break;
344                default:
345                    stream.Printf("unexpected value:(info=%" PRIu64 ", value=%" PRIu64,i_bits,value);
346                    break;
347            }
348            return true;
349        }
350        else
351        {
352            Error error;
353            uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F);
354            uint64_t data_location = valobj_addr + 2*ptr_size;
355            uint64_t value = 0;
356            if (error.Fail())
357                return false;
358            switch (data_type)
359            {
360                case 1: // 0B00001
361                    value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error);
362                    if (error.Fail())
363                        return false;
364                    stream.Printf("(char)%hhd",(char)value);
365                    break;
366                case 2: // 0B0010
367                    value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error);
368                    if (error.Fail())
369                        return false;
370                    stream.Printf("(short)%hd",(short)value);
371                    break;
372                case 3: // 0B0011
373                    value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
374                    if (error.Fail())
375                        return false;
376                    stream.Printf("(int)%d",(int)value);
377                    break;
378                case 17: // 0B10001
379                    data_location += 8;
380                case 4: // 0B0100
381                    value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
382                    if (error.Fail())
383                        return false;
384                    stream.Printf("(long)%" PRId64,value);
385                    break;
386                case 5: // 0B0101
387                {
388                    uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
389                    if (error.Fail())
390                        return false;
391                    float flt_value = *((float*)&flt_as_int);
392                    stream.Printf("(float)%f",flt_value);
393                    break;
394                }
395                case 6: // 0B0110
396                {
397                    uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
398                    if (error.Fail())
399                        return false;
400                    double dbl_value = *((double*)&dbl_as_lng);
401                    stream.Printf("(double)%g",dbl_value);
402                    break;
403                }
404                default:
405                    stream.Printf("unexpected value: dt=%d",data_type);
406                    break;
407            }
408            return true;
409        }
410    }
411    else
412    {
413        return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream);
414    }
415}
416
417bool
418lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream)
419{
420    ProcessSP process_sp = valobj.GetProcessSP();
421    if (!process_sp)
422        return false;
423
424    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
425
426    if (!runtime)
427        return false;
428
429    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
430
431    if (!descriptor.get() || !descriptor->IsValid())
432        return false;
433
434    uint32_t ptr_size = process_sp->GetAddressByteSize();
435
436    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
437
438    if (!valobj_addr)
439        return false;
440
441    const char* class_name = descriptor->GetClassName().GetCString();
442
443    if (!class_name || !*class_name)
444        return false;
445
446    if (strcmp(class_name, "NSURL") == 0)
447    {
448        uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit)
449        uint64_t offset_base = offset_text + ptr_size;
450        ClangASTType type(valobj.GetClangType());
451        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
452        ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
453        if (!text)
454            return false;
455        if (text->GetValueAsUnsigned(0) == 0)
456            return false;
457        StreamString summary;
458        if (!NSStringSummaryProvider(*text, summary))
459            return false;
460        if (base && base->GetValueAsUnsigned(0))
461        {
462            if (summary.GetSize() > 0)
463                summary.GetString().resize(summary.GetSize()-1);
464            summary.Printf(" -- ");
465            StreamString base_summary;
466            if (NSURLSummaryProvider(*base, base_summary) && base_summary.GetSize() > 0)
467                summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData());
468        }
469        if (summary.GetSize())
470        {
471            stream.Printf("%s",summary.GetData());
472            return true;
473        }
474    }
475    else
476    {
477        return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream);
478    }
479    return false;
480}
481
482bool
483lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream)
484{
485    ProcessSP process_sp = valobj.GetProcessSP();
486    if (!process_sp)
487        return false;
488
489    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
490
491    if (!runtime)
492        return false;
493
494    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
495
496    if (!descriptor.get() || !descriptor->IsValid())
497        return false;
498
499    uint32_t ptr_size = process_sp->GetAddressByteSize();
500
501    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
502
503    if (!valobj_addr)
504        return false;
505
506    uint64_t date_value_bits = 0;
507    double date_value = 0.0;
508
509    const char* class_name = descriptor->GetClassName().GetCString();
510
511    if (!class_name || !*class_name)
512        return false;
513
514    if (strcmp(class_name,"NSDate") == 0 ||
515        strcmp(class_name,"__NSDate") == 0 ||
516        strcmp(class_name,"__NSTaggedDate") == 0)
517    {
518        uint64_t info_bits=0,value_bits = 0;
519        if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
520        {
521            date_value_bits = ((value_bits << 8) | (info_bits << 4));
522            date_value = *((double*)&date_value_bits);
523        }
524        else
525        {
526            Error error;
527            date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
528            date_value = *((double*)&date_value_bits);
529            if (error.Fail())
530                return false;
531        }
532    }
533    else if (!strcmp(class_name,"NSCalendarDate"))
534    {
535        Error error;
536        date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
537        date_value = *((double*)&date_value_bits);
538        if (error.Fail())
539            return false;
540    }
541    else
542    {
543        if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false)
544            return false;
545        date_value = *((double*)&date_value_bits);
546    }
547    if (date_value == -63114076800)
548    {
549        stream.Printf("0001-12-30 00:00:00 +0000");
550        return true;
551    }
552    // this snippet of code assumes that time_t == seconds since Jan-1-1970
553    // this is generally true and POSIXly happy, but might break if a library
554    // vendor decides to get creative
555    time_t epoch = GetOSXEpoch();
556    epoch = epoch + (time_t)date_value;
557    tm *tm_date = localtime(&epoch);
558    if (!tm_date)
559        return false;
560    std::string buffer(1024,0);
561    if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
562        return false;
563    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());
564    return true;
565}
566