blob: 6c55f5b8a61e29f8b67b9e890336f4116eaa3602 [file] [log] [blame]
Enrico Granata92373532013-03-19 22:58:48 +00001//===-- 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"
Enrico Granatad87cc312015-09-03 01:29:42 +000011#include "lldb/DataFormatters/StringPrinter.h"
12#include "lldb/DataFormatters/TypeSummary.h"
Enrico Granata92373532013-03-19 22:58:48 +000013
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
Enrico Granatad87cc312015-09-03 01:29:42 +000024#include "lldb/Utility/ProcessStructReader.h"
25
Enrico Granata92373532013-03-19 22:58:48 +000026using namespace lldb;
27using namespace lldb_private;
28using namespace lldb_private::formatters;
29
30bool
Enrico Granataf35bc632014-11-06 21:55:30 +000031lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
Enrico Granata92373532013-03-19 22:58:48 +000032{
33 ProcessSP process_sp = valobj.GetProcessSP();
34 if (!process_sp)
35 return false;
36
37 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
38
39 if (!runtime)
40 return false;
41
42 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
43
44 if (!descriptor.get() || !descriptor->IsValid())
45 return false;
46
47 uint32_t ptr_size = process_sp->GetAddressByteSize();
48
49 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
50
51 if (!valobj_addr)
52 return false;
53
54 const char* class_name = descriptor->GetClassName().GetCString();
55
56 if (!class_name || !*class_name)
57 return false;
58
59 if (!strcmp(class_name,"NSBundle"))
60 {
61 uint64_t offset = 5 * ptr_size;
Greg Clayton99558cc42015-08-24 23:46:31 +000062 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true));
Jim Ingham5c42d8a2013-05-15 18:27:08 +000063
Enrico Granata92373532013-03-19 22:58:48 +000064 StreamString summary_stream;
Enrico Granataf35bc632014-11-06 21:55:30 +000065 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
Enrico Granata92373532013-03-19 22:58:48 +000066 if (was_nsstring_ok && summary_stream.GetSize() > 0)
67 {
68 stream.Printf("%s",summary_stream.GetData());
69 return true;
70 }
71 }
72 // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
73 // which is encoded differently and needs to be handled by running code
74 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream);
75}
76
77bool
Enrico Granataf35bc632014-11-06 21:55:30 +000078lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
Enrico Granata92373532013-03-19 22:58:48 +000079{
80 ProcessSP process_sp = valobj.GetProcessSP();
81 if (!process_sp)
82 return false;
83
84 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
85
86 if (!runtime)
87 return false;
88
89 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
90
91 if (!descriptor.get() || !descriptor->IsValid())
92 return false;
93
94 uint32_t ptr_size = process_sp->GetAddressByteSize();
95
96 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
97
98 if (!valobj_addr)
99 return false;
100
101 const char* class_name = descriptor->GetClassName().GetCString();
102
103 if (!class_name || !*class_name)
104 return false;
105
106 if (!strcmp(class_name,"__NSTimeZone"))
107 {
108 uint64_t offset = ptr_size;
Greg Clayton99558cc42015-08-24 23:46:31 +0000109 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true));
Enrico Granata92373532013-03-19 22:58:48 +0000110 StreamString summary_stream;
Enrico Granataf35bc632014-11-06 21:55:30 +0000111 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
Enrico Granata92373532013-03-19 22:58:48 +0000112 if (was_nsstring_ok && summary_stream.GetSize() > 0)
113 {
114 stream.Printf("%s",summary_stream.GetData());
115 return true;
116 }
117 }
118 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
119}
120
121bool
Enrico Granataf35bc632014-11-06 21:55:30 +0000122lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
Enrico Granata92373532013-03-19 22:58:48 +0000123{
124 ProcessSP process_sp = valobj.GetProcessSP();
125 if (!process_sp)
126 return false;
127
128 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
129
130 if (!runtime)
131 return false;
132
133 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
134
135 if (!descriptor.get() || !descriptor->IsValid())
136 return false;
137
138 uint32_t ptr_size = process_sp->GetAddressByteSize();
139
140 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
141
142 if (!valobj_addr)
143 return false;
144
145 const char* class_name = descriptor->GetClassName().GetCString();
146
147 if (!class_name || !*class_name)
148 return false;
149
150 if (!strcmp(class_name,"NSConcreteNotification"))
151 {
152 uint64_t offset = ptr_size;
Greg Clayton99558cc42015-08-24 23:46:31 +0000153 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true));
Enrico Granata92373532013-03-19 22:58:48 +0000154 StreamString summary_stream;
Enrico Granataf35bc632014-11-06 21:55:30 +0000155 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options);
Enrico Granata92373532013-03-19 22:58:48 +0000156 if (was_nsstring_ok && summary_stream.GetSize() > 0)
157 {
158 stream.Printf("%s",summary_stream.GetData());
159 return true;
160 }
161 }
162 // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
163 // which is encoded differently and needs to be handled by running code
164 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
165}
166
167bool
Enrico Granataf35bc632014-11-06 21:55:30 +0000168lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
Enrico Granata92373532013-03-19 22:58:48 +0000169{
170 ProcessSP process_sp = valobj.GetProcessSP();
171 if (!process_sp)
172 return false;
173
174 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
175
176 if (!runtime)
177 return false;
178
179 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
180
181 if (!descriptor.get() || !descriptor->IsValid())
182 return false;
183
184 uint32_t ptr_size = process_sp->GetAddressByteSize();
185
186 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
187
188 if (!valobj_addr)
189 return false;
190
191 const char* class_name = descriptor->GetClassName().GetCString();
192
193 if (!class_name || !*class_name)
194 return false;
195
196 uint64_t port_number = 0;
197
198 do
199 {
200 if (!strcmp(class_name,"NSMachPort"))
201 {
202 uint64_t offset = (ptr_size == 4 ? 12 : 20);
203 Error error;
204 port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
205 if (error.Success())
206 break;
207 }
208 if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
209 return false;
210 } while (false);
211
212 stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
213 return true;
214}
215
216bool
Enrico Granataf35bc632014-11-06 21:55:30 +0000217lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
Enrico Granata92373532013-03-19 22:58:48 +0000218{
219 ProcessSP process_sp = valobj.GetProcessSP();
220 if (!process_sp)
221 return false;
222
223 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
224
225 if (!runtime)
226 return false;
227
228 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
229
230 if (!descriptor.get() || !descriptor->IsValid())
231 return false;
232
233 uint32_t ptr_size = process_sp->GetAddressByteSize();
234
235 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
236
237 if (!valobj_addr)
238 return false;
239
240 const char* class_name = descriptor->GetClassName().GetCString();
241
242 if (!class_name || !*class_name)
243 return false;
244
245 uint64_t count = 0;
246
247 do {
248 if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
249 {
250 Error error;
251 uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
252 if (error.Fail())
253 return false;
254 // this means the set is empty - count = 0
255 if ((mode & 1) == 1)
256 {
257 count = 0;
258 break;
259 }
260 if ((mode & 2) == 2)
261 mode = 1; // this means the set only has one range
262 else
263 mode = 2; // this means the set has multiple ranges
264 if (mode == 1)
265 {
266 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
267 if (error.Fail())
268 return false;
269 }
270 else
271 {
272 // read a pointer to the data at 2*ptr_size
273 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
274 if (error.Fail())
275 return false;
276 // read the data at 2*ptr_size from the first location
277 count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
278 if (error.Fail())
279 return false;
280 }
281 }
282 else
283 {
284 if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
285 return false;
286 }
287 } while (false);
Matt Kopecef143712013-06-03 18:00:07 +0000288 stream.Printf("%" PRIu64 " index%s",
Enrico Granata92373532013-03-19 22:58:48 +0000289 count,
290 (count == 1 ? "" : "es"));
291 return true;
292}
293
294bool
Enrico Granataf35bc632014-11-06 21:55:30 +0000295lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
Enrico Granata92373532013-03-19 22:58:48 +0000296{
297 ProcessSP process_sp = valobj.GetProcessSP();
298 if (!process_sp)
299 return false;
300
301 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
302
303 if (!runtime)
304 return false;
305
306 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
307
308 if (!descriptor.get() || !descriptor->IsValid())
309 return false;
310
311 uint32_t ptr_size = process_sp->GetAddressByteSize();
312
313 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
314
315 if (!valobj_addr)
316 return false;
317
318 const char* class_name = descriptor->GetClassName().GetCString();
319
320 if (!class_name || !*class_name)
321 return false;
322
323 if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
324 {
Enrico Granataf15ee4e2013-04-05 18:49:06 +0000325 uint64_t value = 0;
326 uint64_t i_bits = 0;
327 if (descriptor->GetTaggedPointerInfo(&i_bits,&value))
Enrico Granata92373532013-03-19 22:58:48 +0000328 {
Enrico Granata92373532013-03-19 22:58:48 +0000329 switch (i_bits)
330 {
331 case 0:
332 stream.Printf("(char)%hhd",(char)value);
333 break;
Enrico Granatab636be72013-04-24 17:49:08 +0000334 case 1:
Enrico Granata92373532013-03-19 22:58:48 +0000335 case 4:
336 stream.Printf("(short)%hd",(short)value);
337 break;
Enrico Granatab636be72013-04-24 17:49:08 +0000338 case 2:
Enrico Granata92373532013-03-19 22:58:48 +0000339 case 8:
340 stream.Printf("(int)%d",(int)value);
341 break;
Enrico Granatab636be72013-04-24 17:49:08 +0000342 case 3:
Enrico Granata92373532013-03-19 22:58:48 +0000343 case 12:
344 stream.Printf("(long)%" PRId64,value);
345 break;
346 default:
Enrico Granatabd4885f2014-04-10 18:17:30 +0000347 return false;
Enrico Granata92373532013-03-19 22:58:48 +0000348 }
349 return true;
350 }
351 else
352 {
353 Error error;
354 uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F);
355 uint64_t data_location = valobj_addr + 2*ptr_size;
356 uint64_t value = 0;
357 if (error.Fail())
358 return false;
359 switch (data_type)
360 {
361 case 1: // 0B00001
362 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error);
363 if (error.Fail())
364 return false;
365 stream.Printf("(char)%hhd",(char)value);
366 break;
367 case 2: // 0B0010
368 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error);
369 if (error.Fail())
370 return false;
371 stream.Printf("(short)%hd",(short)value);
372 break;
373 case 3: // 0B0011
374 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
375 if (error.Fail())
376 return false;
377 stream.Printf("(int)%d",(int)value);
378 break;
379 case 17: // 0B10001
380 data_location += 8;
381 case 4: // 0B0100
382 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
383 if (error.Fail())
384 return false;
385 stream.Printf("(long)%" PRId64,value);
386 break;
387 case 5: // 0B0101
388 {
389 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
390 if (error.Fail())
391 return false;
392 float flt_value = *((float*)&flt_as_int);
393 stream.Printf("(float)%f",flt_value);
394 break;
395 }
396 case 6: // 0B0110
397 {
398 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
399 if (error.Fail())
400 return false;
401 double dbl_value = *((double*)&dbl_as_lng);
402 stream.Printf("(double)%g",dbl_value);
403 break;
404 }
405 default:
Enrico Granatabd4885f2014-04-10 18:17:30 +0000406 return false;
Enrico Granata92373532013-03-19 22:58:48 +0000407 }
408 return true;
409 }
410 }
411 else
412 {
413 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream);
414 }
415}
416
417bool
Enrico Granataf35bc632014-11-06 21:55:30 +0000418lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
Enrico Granata92373532013-03-19 22:58:48 +0000419{
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;
Greg Clayton99558cc42015-08-24 23:46:31 +0000450 CompilerType type(valobj.GetCompilerType());
Enrico Granata92373532013-03-19 22:58:48 +0000451 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;
Enrico Granataf35bc632014-11-06 21:55:30 +0000458 if (!NSStringSummaryProvider(*text, summary, options))
Enrico Granata92373532013-03-19 22:58:48 +0000459 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;
Enrico Granataf35bc632014-11-06 21:55:30 +0000466 if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0)
Enrico Granata92373532013-03-19 22:58:48 +0000467 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
Enrico Granataf35bc632014-11-06 21:55:30 +0000483lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
Enrico Granata92373532013-03-19 22:58:48 +0000484{
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 {
Enrico Granataf15ee4e2013-04-05 18:49:06 +0000518 uint64_t info_bits=0,value_bits = 0;
519 if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
Enrico Granata92373532013-03-19 22:58:48 +0000520 {
Enrico Granata92373532013-03-19 22:58:48 +0000521 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;
Enrico Granatadb2ecad2014-10-15 20:18:58 +0000557 tm *tm_date = gmtime(&epoch);
Enrico Granata92373532013-03-19 22:58:48 +0000558 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}
Enrico Granatad87cc312015-09-03 01:29:42 +0000566
567bool
568lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
569{
570 ProcessSP process_sp = valobj.GetProcessSP();
571 if (!process_sp)
572 return false;
573
574 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
575
576 if (!runtime)
577 return false;
578
579 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
580
581 if (!descriptor.get() || !descriptor->IsValid())
582 return false;
583
584 const char* class_name = descriptor->GetClassName().GetCString();
585
586 if (!class_name || !*class_name)
587 return false;
588
589 stream.Printf("%s",class_name);
590 return true;
591}
592
593class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd
594{
595public:
596 ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) :
597 SyntheticChildrenFrontEnd(*valobj_sp.get())
598 {
599 }
600
601 virtual size_t
602 CalculateNumChildren ()
603 {
604 return 0;
605 }
606
607 virtual lldb::ValueObjectSP
608 GetChildAtIndex (size_t idx)
609 {
610 return lldb::ValueObjectSP();
611 }
612
613 virtual bool
614 Update()
615 {
616 return false;
617 }
618
619 virtual bool
620 MightHaveChildren ()
621 {
622 return false;
623 }
624
625 virtual size_t
626 GetIndexOfChildWithName (const ConstString &name)
627 {
628 return UINT32_MAX;
629 }
630
631 virtual
632 ~ObjCClassSyntheticChildrenFrontEnd ()
633 {
634 }
635};
636
637SyntheticChildrenFrontEnd*
638lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
639{
640 return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
641}
642
643template<bool needs_at>
644bool
645lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
646{
647 ProcessSP process_sp = valobj.GetProcessSP();
648 if (!process_sp)
649 return false;
650
651 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
652
653 if (!runtime)
654 return false;
655
656 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
657
658 if (!descriptor.get() || !descriptor->IsValid())
659 return false;
660
661 bool is_64bit = (process_sp->GetAddressByteSize() == 8);
662 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
663
664 if (!valobj_addr)
665 return false;
666
667 uint64_t value = 0;
668
669 const char* class_name = descriptor->GetClassName().GetCString();
670
671 if (!class_name || !*class_name)
672 return false;
673
674 if (!strcmp(class_name,"NSConcreteData") ||
675 !strcmp(class_name,"NSConcreteMutableData") ||
676 !strcmp(class_name,"__NSCFData"))
677 {
678 uint32_t offset = (is_64bit ? 16 : 8);
679 Error error;
680 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
681 if (error.Fail())
682 return false;
683 }
684 else
685 {
686 if (!ExtractValueFromObjCExpression(valobj, "int", "length", value))
687 return false;
688 }
689
690 stream.Printf("%s%" PRIu64 " byte%s%s",
691 (needs_at ? "@\"" : ""),
692 value,
693 (value != 1 ? "s" : ""),
694 (needs_at ? "\"" : ""));
695
696 return true;
697}
698
699bool
700lldb_private::formatters::NSTaggedString_SummaryProvider (ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream& stream)
701{
702 if (!descriptor)
703 return false;
704 uint64_t len_bits = 0, data_bits = 0;
705 if (!descriptor->GetTaggedPointerInfo(&len_bits,&data_bits,nullptr))
706 return false;
707
708 static const int g_MaxNonBitmaskedLen = 7; //TAGGED_STRING_UNPACKED_MAXLEN
709 static const int g_SixbitMaxLen = 9;
710 static const int g_fiveBitMaxLen = 11;
711
712 static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013" "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
713
714 if (len_bits > g_fiveBitMaxLen)
715 return false;
716
717 // this is a fairly ugly trick - pretend that the numeric value is actually a char*
718 // this works under a few assumptions:
719 // little endian architecture
720 // sizeof(uint64_t) > g_MaxNonBitmaskedLen
721 if (len_bits <= g_MaxNonBitmaskedLen)
722 {
723 stream.Printf("@\"%s\"",(const char*)&data_bits);
724 return true;
725 }
726
727 // if the data is bitmasked, we need to actually process the bytes
728 uint8_t bitmask = 0;
729 uint8_t shift_offset = 0;
730
731 if (len_bits <= g_SixbitMaxLen)
732 {
733 bitmask = 0x03f;
734 shift_offset = 6;
735 }
736 else
737 {
738 bitmask = 0x01f;
739 shift_offset = 5;
740 }
741
742 std::vector<uint8_t> bytes;
743 bytes.resize(len_bits);
744 for (; len_bits > 0; data_bits >>= shift_offset, --len_bits)
745 {
746 uint8_t packed = data_bits & bitmask;
747 bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
748 }
749
750 stream.Printf("@\"%s\"",&bytes[0]);
751 return true;
752}
753
754static CompilerType
755GetNSPathStore2Type (Target &target)
756{
757 static ConstString g_type_name("__lldb_autogen_nspathstore2");
758
759 ClangASTContext *ast_ctx = target.GetScratchClangASTContext();
760
761 if (!ast_ctx)
762 return CompilerType();
763
764 CompilerType voidstar = ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType();
765 CompilerType uint32 = ast_ctx->GetIntTypeFromBitSize(32, false);
766
767 return ast_ctx->GetOrCreateStructForIdentifier(g_type_name, {
768 {"isa",voidstar},
769 {"lengthAndRef",uint32},
770 {"buffer",voidstar}
771 });
772}
773
774bool
775lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& summary_options)
776{
777 ProcessSP process_sp = valobj.GetProcessSP();
778 if (!process_sp)
779 return false;
780
781 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
782
783 if (!runtime)
784 return false;
785
786 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
787
788 if (!descriptor.get() || !descriptor->IsValid())
789 return false;
790
791 uint32_t ptr_size = process_sp->GetAddressByteSize();
792
793 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
794
795 if (!valobj_addr)
796 return false;
797
798 const char* class_name = descriptor->GetClassName().GetCString();
799
800 if (!class_name || !*class_name)
801 return false;
802
803 bool is_tagged_ptr = (0 == strcmp(class_name,"NSTaggedPointerString")) && descriptor->GetTaggedPointerInfo();
804 // for a tagged pointer, the descriptor has everything we need
805 if (is_tagged_ptr)
806 return NSTaggedString_SummaryProvider(descriptor, stream);
807
808 // if not a tagged pointer that we know about, try the normal route
809 uint64_t info_bits_location = valobj_addr + ptr_size;
810 if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
811 info_bits_location += 3;
812
813 Error error;
814
815 uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(info_bits_location, 1, 0, error);
816 if (error.Fail())
817 return false;
818
819 bool is_mutable = (info_bits & 1) == 1;
820 bool is_inline = (info_bits & 0x60) == 0;
821 bool has_explicit_length = (info_bits & (1 | 4)) != 4;
822 bool is_unicode = (info_bits & 0x10) == 0x10;
823 bool is_path_store = strcmp(class_name,"NSPathStore2") == 0;
824 bool has_null = (info_bits & 8) == 8;
825
826 size_t explicit_length = 0;
827 if (!has_null && has_explicit_length && !is_path_store)
828 {
829 lldb::addr_t explicit_length_offset = 2*ptr_size;
830 if (is_mutable && !is_inline)
831 explicit_length_offset = explicit_length_offset + ptr_size; // notInlineMutable.length;
832 else if (is_inline)
833 explicit_length = explicit_length + 0; // inline1.length;
834 else if (!is_inline && !is_mutable)
835 explicit_length_offset = explicit_length_offset + ptr_size; // notInlineImmutable1.length;
836 else
837 explicit_length_offset = 0;
838
839 if (explicit_length_offset)
840 {
841 explicit_length_offset = valobj_addr + explicit_length_offset;
842 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(explicit_length_offset, 4, 0, error);
843 }
844 }
845
846 if (strcmp(class_name,"NSString") &&
847 strcmp(class_name,"CFStringRef") &&
848 strcmp(class_name,"CFMutableStringRef") &&
849 strcmp(class_name,"__NSCFConstantString") &&
850 strcmp(class_name,"__NSCFString") &&
851 strcmp(class_name,"NSCFConstantString") &&
852 strcmp(class_name,"NSCFString") &&
853 strcmp(class_name,"NSPathStore2"))
854 {
855 // not one of us - but tell me class name
856 stream.Printf("class name = %s",class_name);
857 return true;
858 }
859
860 if (is_mutable)
861 {
862 uint64_t location = 2 * ptr_size + valobj_addr;
863 location = process_sp->ReadPointerFromMemory(location, error);
864 if (error.Fail())
865 return false;
866 if (has_explicit_length && is_unicode)
867 {
868 ReadStringAndDumpToStreamOptions options(valobj);
869 options.SetLocation(location);
870 options.SetProcessSP(process_sp);
871 options.SetStream(&stream);
872 options.SetPrefixToken('@');
873 options.SetQuote('"');
874 options.SetSourceSize(explicit_length);
875 options.SetNeedsZeroTermination(false);
876 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
877 options.SetBinaryZeroIsTerminator(false);
878 return ReadStringAndDumpToStream<StringElementType::UTF16>(options);
879 }
880 else
881 {
882 ReadStringAndDumpToStreamOptions options(valobj);
883 options.SetLocation(location+1);
884 options.SetProcessSP(process_sp);
885 options.SetStream(&stream);
886 options.SetPrefixToken('@');
887 options.SetSourceSize(explicit_length);
888 options.SetNeedsZeroTermination(false);
889 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
890 options.SetBinaryZeroIsTerminator(false);
891 return ReadStringAndDumpToStream<StringElementType::ASCII>(options);
892 }
893 }
894 else if (is_inline && has_explicit_length && !is_unicode && !is_path_store && !is_mutable)
895 {
896 uint64_t location = 3 * ptr_size + valobj_addr;
897
898 ReadStringAndDumpToStreamOptions options(valobj);
899 options.SetLocation(location);
900 options.SetProcessSP(process_sp);
901 options.SetStream(&stream);
902 options.SetPrefixToken('@');
903 options.SetQuote('"');
904 options.SetSourceSize(explicit_length);
905 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
906 return ReadStringAndDumpToStream<StringElementType::ASCII> (options);
907 }
908 else if (is_unicode)
909 {
910 uint64_t location = valobj_addr + 2*ptr_size;
911 if (is_inline)
912 {
913 if (!has_explicit_length)
914 {
915 stream.Printf("found new combo");
916 return true;
917 }
918 else
919 location += ptr_size;
920 }
921 else
922 {
923 location = process_sp->ReadPointerFromMemory(location, error);
924 if (error.Fail())
925 return false;
926 }
927 ReadStringAndDumpToStreamOptions options(valobj);
928 options.SetLocation(location);
929 options.SetProcessSP(process_sp);
930 options.SetStream(&stream);
931 options.SetPrefixToken('@');
932 options.SetQuote('"');
933 options.SetSourceSize(explicit_length);
934 options.SetNeedsZeroTermination(has_explicit_length == false);
935 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
936 options.SetBinaryZeroIsTerminator(has_explicit_length == false);
937 return ReadStringAndDumpToStream<StringElementType::UTF16> (options);
938 }
939 else if (is_path_store)
940 {
941 ProcessStructReader reader(valobj.GetProcessSP().get(), valobj.GetValueAsUnsigned(0), GetNSPathStore2Type(*valobj.GetTargetSP()));
942 explicit_length = reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20;
943 lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
944
945 ReadStringAndDumpToStreamOptions options(valobj);
946 options.SetLocation(location);
947 options.SetProcessSP(process_sp);
948 options.SetStream(&stream);
949 options.SetPrefixToken('@');
950 options.SetQuote('"');
951 options.SetSourceSize(explicit_length);
952 options.SetNeedsZeroTermination(has_explicit_length == false);
953 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
954 options.SetBinaryZeroIsTerminator(has_explicit_length == false);
955 return ReadStringAndDumpToStream<StringElementType::UTF16> (options);
956 }
957 else if (is_inline)
958 {
959 uint64_t location = valobj_addr + 2*ptr_size;
960 if (!has_explicit_length)
961 {
962 // in this kind of string, the byte before the string content is a length byte
963 // so let's try and use it to handle the embedded NUL case
964 Error error;
965 explicit_length = process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
966 if (error.Fail() || explicit_length == 0)
967 has_explicit_length = false;
968 else
969 has_explicit_length = true;
970 location++;
971 }
972 ReadStringAndDumpToStreamOptions options(valobj);
973 options.SetLocation(location);
974 options.SetProcessSP(process_sp);
975 options.SetStream(&stream);
976 options.SetPrefixToken('@');
977 options.SetSourceSize(explicit_length);
978 options.SetNeedsZeroTermination(!has_explicit_length);
979 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
980 options.SetBinaryZeroIsTerminator(!has_explicit_length);
981 if (has_explicit_length)
982 return ReadStringAndDumpToStream<StringElementType::UTF8>(options);
983 else
984 return ReadStringAndDumpToStream<StringElementType::ASCII>(options);
985 }
986 else
987 {
988 uint64_t location = valobj_addr + 2*ptr_size;
989 location = process_sp->ReadPointerFromMemory(location, error);
990 if (error.Fail())
991 return false;
992 if (has_explicit_length && !has_null)
993 explicit_length++; // account for the fact that there is no NULL and we need to have one added
994 ReadStringAndDumpToStreamOptions options(valobj);
995 options.SetLocation(location);
996 options.SetProcessSP(process_sp);
997 options.SetPrefixToken('@');
998 options.SetStream(&stream);
999 options.SetSourceSize(explicit_length);
1000 options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped);
1001 return ReadStringAndDumpToStream<StringElementType::ASCII>(options);
1002 }
1003}
1004
1005bool
1006lldb_private::formatters::NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
1007{
1008 TargetSP target_sp(valobj.GetTargetSP());
1009 if (!target_sp)
1010 return false;
1011 uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
1012 uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
1013 if (!pointer_value)
1014 return false;
1015 pointer_value += addr_size;
1016 CompilerType type(valobj.GetCompilerType());
1017 ExecutionContext exe_ctx(target_sp,false);
1018 ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress("string_ptr", pointer_value, exe_ctx, type));
1019 if (!child_ptr_sp)
1020 return false;
1021 DataExtractor data;
1022 Error error;
1023 child_ptr_sp->GetData(data, error);
1024 if (error.Fail())
1025 return false;
1026 ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData("string_data", data, exe_ctx, type));
1027 child_sp->GetValueAsUnsigned(0);
1028 if (child_sp)
1029 return NSStringSummaryProvider(*child_sp, stream, options);
1030 return false;
1031}
1032
1033bool
1034lldb_private::formatters::NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
1035{
1036 return NSAttributedStringSummaryProvider(valobj, stream, options);
1037}
1038
1039bool
1040lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
1041{
1042 const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
1043
1044 ValueObjectSP real_guy_sp = valobj.GetSP();
1045
1046 if (type_info & eTypeIsPointer)
1047 {
1048 Error err;
1049 real_guy_sp = valobj.Dereference(err);
1050 if (err.Fail() || !real_guy_sp)
1051 return false;
1052 }
1053 else if (type_info & eTypeIsReference)
1054 {
1055 real_guy_sp = valobj.GetChildAtIndex(0, true);
1056 if (!real_guy_sp)
1057 return false;
1058 }
1059 uint64_t value = real_guy_sp->GetValueAsUnsigned(0);
1060 if (value == 0)
1061 {
1062 stream.Printf("NO");
1063 return true;
1064 }
1065 stream.Printf("YES");
1066 return true;
1067}
1068
1069template <bool is_sel_ptr>
1070bool
1071lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
1072{
1073 lldb::ValueObjectSP valobj_sp;
1074
1075 CompilerType charstar (valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeChar).GetPointerType());
1076
1077 if (!charstar)
1078 return false;
1079
1080 ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
1081
1082 if (is_sel_ptr)
1083 {
1084 lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
1085 if (data_address == LLDB_INVALID_ADDRESS)
1086 return false;
1087 valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, exe_ctx, charstar);
1088 }
1089 else
1090 {
1091 DataExtractor data;
1092 Error error;
1093 valobj.GetData(data, error);
1094 if (error.Fail())
1095 return false;
1096 valobj_sp = ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
1097 }
1098
1099 if (!valobj_sp)
1100 return false;
1101
1102 stream.Printf("%s",valobj_sp->GetSummaryAsCString());
1103 return true;
1104}
1105
1106// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
1107// this call gives the POSIX equivalent of the Cocoa epoch
1108time_t
1109lldb_private::formatters::GetOSXEpoch ()
1110{
1111 static time_t epoch = 0;
1112 if (!epoch)
1113 {
1114#ifndef _WIN32
1115 tzset();
1116 tm tm_epoch;
1117 tm_epoch.tm_sec = 0;
1118 tm_epoch.tm_hour = 0;
1119 tm_epoch.tm_min = 0;
1120 tm_epoch.tm_mon = 0;
1121 tm_epoch.tm_mday = 1;
1122 tm_epoch.tm_year = 2001-1900; // for some reason, we need to subtract 1900 from this field. not sure why.
1123 tm_epoch.tm_isdst = -1;
1124 tm_epoch.tm_gmtoff = 0;
1125 tm_epoch.tm_zone = NULL;
1126 epoch = timegm(&tm_epoch);
1127#endif
1128 }
1129 return epoch;
1130}
1131
1132template bool
1133lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&) ;
1134
1135template bool
1136lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&) ;
1137
1138template bool
1139lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&) ;
1140
1141template bool
1142lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&) ;