Cocoa.cpp revision 3dc2a5b732ae161b8ae51d376a8f0060a7d9e2a8
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/DataFormatters/CXXFormatterFunctions.h"
11
12#include "lldb/Core/DataBufferHeap.h"
13#include "lldb/Core/Error.h"
14#include "lldb/Core/Stream.h"
15#include "lldb/Core/ValueObject.h"
16#include "lldb/Core/ValueObjectConstResult.h"
17#include "lldb/Host/Endian.h"
18#include "lldb/Symbol/ClangASTContext.h"
19#include "lldb/Target/ObjCLanguageRuntime.h"
20#include "lldb/Target/Target.h"
21
22using namespace lldb;
23using namespace lldb_private;
24using namespace lldb_private::formatters;
25
26bool
27lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream)
28{
29    ProcessSP process_sp = valobj.GetProcessSP();
30    if (!process_sp)
31        return false;
32
33    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
34
35    if (!runtime)
36        return false;
37
38    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
39
40    if (!descriptor.get() || !descriptor->IsValid())
41        return false;
42
43    uint32_t ptr_size = process_sp->GetAddressByteSize();
44
45    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
46
47    if (!valobj_addr)
48        return false;
49
50    const char* class_name = descriptor->GetClassName().GetCString();
51
52    if (!class_name || !*class_name)
53        return false;
54
55    if (!strcmp(class_name,"NSBundle"))
56    {
57        uint64_t offset = 5 * ptr_size;
58        ClangASTType type(valobj.GetClangAST(),ClangASTContext::GetBuiltInType_objc_id(valobj.GetClangAST()));
59        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, type, true));
60        valobj_addr = text->GetValueAsUnsigned(0);
61        StreamString summary_stream;
62        bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
63        if (was_nsstring_ok && summary_stream.GetSize() > 0)
64        {
65            stream.Printf("%s",summary_stream.GetData());
66            return true;
67        }
68    }
69    // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
70    // which is encoded differently and needs to be handled by running code
71    return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream);
72}
73
74bool
75lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream)
76{
77    ProcessSP process_sp = valobj.GetProcessSP();
78    if (!process_sp)
79        return false;
80
81    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
82
83    if (!runtime)
84        return false;
85
86    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
87
88    if (!descriptor.get() || !descriptor->IsValid())
89        return false;
90
91    uint32_t ptr_size = process_sp->GetAddressByteSize();
92
93    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
94
95    if (!valobj_addr)
96        return false;
97
98    const char* class_name = descriptor->GetClassName().GetCString();
99
100    if (!class_name || !*class_name)
101        return false;
102
103    if (!strcmp(class_name,"__NSTimeZone"))
104    {
105        uint64_t offset = ptr_size;
106        ClangASTType type(valobj.GetClangAST(),valobj.GetClangType());
107        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, type, 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        ClangASTType type(valobj.GetClangAST(),valobj.GetClangType());
152        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, type, true));
153        StreamString summary_stream;
154        bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
155        if (was_nsstring_ok && summary_stream.GetSize() > 0)
156        {
157            stream.Printf("%s",summary_stream.GetData());
158            return true;
159        }
160    }
161    // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
162    // which is encoded differently and needs to be handled by running code
163    return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
164}
165
166bool
167lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream)
168{
169    ProcessSP process_sp = valobj.GetProcessSP();
170    if (!process_sp)
171        return false;
172
173    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
174
175    if (!runtime)
176        return false;
177
178    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
179
180    if (!descriptor.get() || !descriptor->IsValid())
181        return false;
182
183    uint32_t ptr_size = process_sp->GetAddressByteSize();
184
185    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
186
187    if (!valobj_addr)
188        return false;
189
190    const char* class_name = descriptor->GetClassName().GetCString();
191
192    if (!class_name || !*class_name)
193        return false;
194
195    uint64_t port_number = 0;
196
197    do
198    {
199        if (!strcmp(class_name,"NSMachPort"))
200        {
201            uint64_t offset = (ptr_size == 4 ? 12 : 20);
202            Error error;
203            port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
204            if (error.Success())
205                break;
206        }
207        if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
208            return false;
209    } while (false);
210
211    stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
212    return true;
213}
214
215bool
216lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream)
217{
218    ProcessSP process_sp = valobj.GetProcessSP();
219    if (!process_sp)
220        return false;
221
222    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
223
224    if (!runtime)
225        return false;
226
227    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
228
229    if (!descriptor.get() || !descriptor->IsValid())
230        return false;
231
232    uint32_t ptr_size = process_sp->GetAddressByteSize();
233
234    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
235
236    if (!valobj_addr)
237        return false;
238
239    const char* class_name = descriptor->GetClassName().GetCString();
240
241    if (!class_name || !*class_name)
242        return false;
243
244    uint64_t count = 0;
245
246    do {
247        if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
248        {
249            Error error;
250            uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
251            if (error.Fail())
252                return false;
253            // this means the set is empty - count = 0
254            if ((mode & 1) == 1)
255            {
256                count = 0;
257                break;
258            }
259            if ((mode & 2) == 2)
260                mode = 1; // this means the set only has one range
261            else
262                mode = 2; // this means the set has multiple ranges
263            if (mode == 1)
264            {
265                count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
266                if (error.Fail())
267                    return false;
268            }
269            else
270            {
271                // read a pointer to the data at 2*ptr_size
272                count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
273                if (error.Fail())
274                    return false;
275                // read the data at 2*ptr_size from the first location
276                count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
277                if (error.Fail())
278                    return false;
279            }
280        }
281        else
282        {
283            if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
284                return false;
285        }
286    }  while (false);
287    stream.Printf("%llu index%s",
288                  count,
289                  (count == 1 ? "" : "es"));
290    return true;
291}
292
293bool
294lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream)
295{
296    ProcessSP process_sp = valobj.GetProcessSP();
297    if (!process_sp)
298        return false;
299
300    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
301
302    if (!runtime)
303        return false;
304
305    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
306
307    if (!descriptor.get() || !descriptor->IsValid())
308        return false;
309
310    uint32_t ptr_size = process_sp->GetAddressByteSize();
311
312    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
313
314    if (!valobj_addr)
315        return false;
316
317    const char* class_name = descriptor->GetClassName().GetCString();
318
319    if (!class_name || !*class_name)
320        return false;
321
322    if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
323    {
324        if (descriptor->IsTagged())
325        {
326            // we have a call to get info and value bits in the tagged descriptor. but we prefer not to cast and replicate them
327            int64_t value = (valobj_addr & ~0x0000000000000000FFL) >> 8;
328            uint64_t i_bits = (valobj_addr & 0xF0) >> 4;
329
330            switch (i_bits)
331            {
332                case 0:
333                    stream.Printf("(char)%hhd",(char)value);
334                    break;
335                case 4:
336                    stream.Printf("(short)%hd",(short)value);
337                    break;
338                case 8:
339                    stream.Printf("(int)%d",(int)value);
340                    break;
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("absurd: 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.GetClangAST(),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        if (descriptor->IsTagged())
519        {
520            uint64_t info_bits = (valobj_addr & 0xF0ULL) >> 4;
521            uint64_t value_bits = (valobj_addr & ~0x0000000000000000FFULL) >> 8;
522            date_value_bits = ((value_bits << 8) | (info_bits << 4));
523            date_value = *((double*)&date_value_bits);
524        }
525        else
526        {
527            Error error;
528            date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
529            date_value = *((double*)&date_value_bits);
530            if (error.Fail())
531                return false;
532        }
533    }
534    else if (!strcmp(class_name,"NSCalendarDate"))
535    {
536        Error error;
537        date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
538        date_value = *((double*)&date_value_bits);
539        if (error.Fail())
540            return false;
541    }
542    else
543    {
544        if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false)
545            return false;
546        date_value = *((double*)&date_value_bits);
547    }
548    if (date_value == -63114076800)
549    {
550        stream.Printf("0001-12-30 00:00:00 +0000");
551        return true;
552    }
553    // this snippet of code assumes that time_t == seconds since Jan-1-1970
554    // this is generally true and POSIXly happy, but might break if a library
555    // vendor decides to get creative
556    time_t epoch = GetOSXEpoch();
557    epoch = epoch + (time_t)date_value;
558    tm *tm_date = localtime(&epoch);
559    if (!tm_date)
560        return false;
561    std::string buffer(1024,0);
562    if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
563        return false;
564    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());
565    return true;
566}
567