1 //===-- NSDictionary.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 
24 #include "clang/AST/DeclCXX.h"
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 using namespace lldb_private::formatters;
29 
30 static ClangASTType
GetLLDBNSPairType(TargetSP target_sp)31 GetLLDBNSPairType (TargetSP target_sp)
32 {
33     ClangASTType clang_type;
34 
35     ClangASTContext *target_ast_context = target_sp->GetScratchClangASTContext();
36 
37     if (target_ast_context)
38     {
39         clang::ASTContext *ast = target_ast_context->getASTContext();
40 
41         if (ast)
42         {
43             const char* type_name = "__lldb_autogen_nspair";
44 
45             clang::IdentifierInfo &myIdent = ast->Idents.get(type_name);
46             clang::DeclarationName myName = ast->DeclarationNames.getIdentifier(&myIdent);
47 
48             clang::DeclContext::lookup_const_result result = ast->getTranslationUnitDecl()->lookup(myName);
49 
50             for (clang::NamedDecl *named_decl : result)
51             {
52                 if (const clang::CXXRecordDecl *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(named_decl))
53                 {
54                     clang_type.SetClangType(ast, clang::QualType(record_decl->getTypeForDecl(), 0));
55                     break;
56                 }
57                 else
58                 {
59                     // somebody else (the user?) has defined a type with the magic name already - fail!!!
60                     return clang_type;
61                 }
62             }
63 
64             if (!clang_type)
65             {
66                 clang_type = target_ast_context->CreateRecordType(NULL, lldb::eAccessPublic, type_name, clang::TTK_Struct, lldb::eLanguageTypeC);
67 
68                 if (clang_type)
69                 {
70                     clang_type.StartTagDeclarationDefinition();
71                     ClangASTType id_clang_type = target_ast_context->GetBasicType (eBasicTypeObjCID);
72                     clang_type.AddFieldToRecordType("key", id_clang_type, lldb::eAccessPublic, 0);
73                     clang_type.AddFieldToRecordType("value", id_clang_type, lldb::eAccessPublic, 0);
74                     clang_type.CompleteTagDeclarationDefinition();
75                 }
76             }
77         }
78     }
79     return clang_type;
80 }
81 
82 template<bool name_entries>
83 bool
NSDictionarySummaryProvider(ValueObject & valobj,Stream & stream)84 lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream)
85 {
86     ProcessSP process_sp = valobj.GetProcessSP();
87     if (!process_sp)
88         return false;
89 
90     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
91 
92     if (!runtime)
93         return false;
94 
95     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
96 
97     if (!descriptor.get() || !descriptor->IsValid())
98         return false;
99 
100     uint32_t ptr_size = process_sp->GetAddressByteSize();
101     bool is_64bit = (ptr_size == 8);
102 
103     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
104 
105     if (!valobj_addr)
106         return false;
107 
108     uint64_t value = 0;
109 
110     const char* class_name = descriptor->GetClassName().GetCString();
111 
112     if (!class_name || !*class_name)
113         return false;
114 
115     if (!strcmp(class_name,"__NSDictionaryI"))
116     {
117         Error error;
118         value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error);
119         if (error.Fail())
120             return false;
121         value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
122     }
123     else if (!strcmp(class_name,"__NSDictionaryM"))
124     {
125         Error error;
126         value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error);
127         if (error.Fail())
128             return false;
129         value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
130     }
131     /*else if (!strcmp(class_name,"__NSCFDictionary"))
132     {
133         Error error;
134         value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ? 20 : 12), 4, 0, error);
135         if (error.Fail())
136             return false;
137         if (is_64bit)
138             value &= ~0x0f1f000000000000UL;
139     }*/
140     else
141     {
142         if (!ExtractValueFromObjCExpression(valobj, "int", "count", value))
143             return false;
144     }
145 
146     stream.Printf("%s%" PRIu64 " %s%s",
147                   (name_entries ? "@\"" : ""),
148                   value,
149                   (name_entries ? (value == 1 ? "entry" : "entries") : (value == 1 ? "key/value pair" : "key/value pairs")),
150                   (name_entries ? "\"" : ""));
151     return true;
152 }
153 
NSDictionarySyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)154 SyntheticChildrenFrontEnd* lldb_private::formatters::NSDictionarySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
155 {
156 
157     lldb::ProcessSP process_sp (valobj_sp->GetProcessSP());
158     if (!process_sp)
159         return NULL;
160     ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
161     if (!runtime)
162         return NULL;
163 
164     if (!valobj_sp->IsPointerType())
165     {
166         Error error;
167         valobj_sp = valobj_sp->AddressOf(error);
168         if (error.Fail() || !valobj_sp)
169             return NULL;
170     }
171 
172     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get()));
173 
174     if (!descriptor.get() || !descriptor->IsValid())
175         return NULL;
176 
177     const char* class_name = descriptor->GetClassName().GetCString();
178 
179     if (!class_name || !*class_name)
180         return NULL;
181 
182     if (!strcmp(class_name,"__NSDictionaryI"))
183     {
184         return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
185     }
186     else if (!strcmp(class_name,"__NSDictionaryM"))
187     {
188         return (new NSDictionaryMSyntheticFrontEnd(valobj_sp));
189     }
190     else
191     {
192         return (new NSDictionaryCodeRunningSyntheticFrontEnd(valobj_sp));
193     }
194 }
195 
NSDictionaryCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)196 lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::NSDictionaryCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
197 SyntheticChildrenFrontEnd(*valobj_sp.get())
198 {}
199 
200 size_t
CalculateNumChildren()201 lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::CalculateNumChildren ()
202 {
203     uint64_t count = 0;
204     if (ExtractValueFromObjCExpression(m_backend, "int", "count", count))
205         return count;
206     return 0;
207 }
208 
209 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)210 lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::GetChildAtIndex (size_t idx)
211 {
212     StreamString idx_name;
213     idx_name.Printf("[%zu]",idx);
214     StreamString key_fetcher_expr;
215     key_fetcher_expr.Printf("(id)[(NSArray*)[(id)0x%" PRIx64 " allKeys] objectAtIndex:%zu]",m_backend.GetPointerValue(),idx);
216     StreamString value_fetcher_expr;
217     value_fetcher_expr.Printf("(id)[(id)0x%" PRIx64 " objectForKey:(%s)]",m_backend.GetPointerValue(),key_fetcher_expr.GetData());
218     StreamString object_fetcher_expr;
219     object_fetcher_expr.Printf("struct __lldb_autogen_nspair { id key; id value; } _lldb_valgen_item; _lldb_valgen_item.key = %s; _lldb_valgen_item.value = %s; _lldb_valgen_item;",key_fetcher_expr.GetData(),value_fetcher_expr.GetData());
220     lldb::ValueObjectSP child_sp;
221     m_backend.GetTargetSP()->EvaluateExpression(object_fetcher_expr.GetData(), m_backend.GetFrameSP().get(), child_sp,
222                                                 EvaluateExpressionOptions().SetKeepInMemory(true));
223     if (child_sp)
224         child_sp->SetName(ConstString(idx_name.GetData()));
225     return child_sp;
226 }
227 
228 bool
Update()229 lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::Update()
230 {
231     return false;
232 }
233 
234 bool
MightHaveChildren()235 lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::MightHaveChildren ()
236 {
237     return true;
238 }
239 
240 size_t
GetIndexOfChildWithName(const ConstString & name)241 lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
242 {
243     return 0;
244 }
245 
~NSDictionaryCodeRunningSyntheticFrontEnd()246 lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::~NSDictionaryCodeRunningSyntheticFrontEnd ()
247 {}
248 
NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)249 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::NSDictionaryISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
250 SyntheticChildrenFrontEnd(*valobj_sp.get()),
251 m_exe_ctx_ref(),
252 m_ptr_size(8),
253 m_order(lldb::eByteOrderInvalid),
254 m_data_32(NULL),
255 m_data_64(NULL),
256 m_pair_type()
257 {
258 }
259 
~NSDictionaryISyntheticFrontEnd()260 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::~NSDictionaryISyntheticFrontEnd ()
261 {
262     delete m_data_32;
263     m_data_32 = NULL;
264     delete m_data_64;
265     m_data_64 = NULL;
266 }
267 
268 size_t
GetIndexOfChildWithName(const ConstString & name)269 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
270 {
271     const char* item_name = name.GetCString();
272     uint32_t idx = ExtractIndexFromString(item_name);
273     if (idx < UINT32_MAX && idx >= CalculateNumChildren())
274         return UINT32_MAX;
275     return idx;
276 }
277 
278 size_t
CalculateNumChildren()279 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::CalculateNumChildren ()
280 {
281     if (!m_data_32 && !m_data_64)
282         return 0;
283     return (m_data_32 ? m_data_32->_used : m_data_64->_used);
284 }
285 
286 bool
Update()287 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update()
288 {
289     m_children.clear();
290     delete m_data_32;
291     m_data_32 = NULL;
292     delete m_data_64;
293     m_data_64 = NULL;
294     m_ptr_size = 0;
295     ValueObjectSP valobj_sp = m_backend.GetSP();
296     if (!valobj_sp)
297         return false;
298     m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
299     Error error;
300     error.Clear();
301     lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
302     if (!process_sp)
303         return false;
304     m_ptr_size = process_sp->GetAddressByteSize();
305     m_order = process_sp->GetByteOrder();
306     uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
307     if (m_ptr_size == 4)
308     {
309         m_data_32 = new DataDescriptor_32();
310         process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error);
311     }
312     else
313     {
314         m_data_64 = new DataDescriptor_64();
315         process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error);
316     }
317     if (error.Fail())
318         return false;
319     m_data_ptr = data_location + m_ptr_size;
320     return false;
321 }
322 
323 bool
MightHaveChildren()324 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::MightHaveChildren ()
325 {
326     return true;
327 }
328 
329 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)330 lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex (size_t idx)
331 {
332     uint32_t num_children = CalculateNumChildren();
333 
334     if (idx >= num_children)
335         return lldb::ValueObjectSP();
336 
337     if (m_children.empty())
338     {
339         // do the scan phase
340         lldb::addr_t key_at_idx = 0, val_at_idx = 0;
341 
342         uint32_t tries = 0;
343         uint32_t test_idx = 0;
344 
345         while(tries < num_children)
346         {
347             key_at_idx = m_data_ptr + (2*test_idx * m_ptr_size);
348             val_at_idx = key_at_idx + m_ptr_size;
349             ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
350             if (!process_sp)
351                 return lldb::ValueObjectSP();
352             Error error;
353             key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
354             if (error.Fail())
355                 return lldb::ValueObjectSP();
356             val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
357             if (error.Fail())
358                 return lldb::ValueObjectSP();
359 
360             test_idx++;
361 
362             if (!key_at_idx || !val_at_idx)
363                 continue;
364             tries++;
365 
366             DictionaryItemDescriptor descriptor = {key_at_idx,val_at_idx,lldb::ValueObjectSP()};
367 
368             m_children.push_back(descriptor);
369         }
370     }
371 
372     if (idx >= m_children.size()) // should never happen
373         return lldb::ValueObjectSP();
374 
375     DictionaryItemDescriptor &dict_item = m_children[idx];
376     if (!dict_item.valobj_sp)
377     {
378         if (!m_pair_type.IsValid())
379         {
380             TargetSP target_sp(m_backend.GetTargetSP());
381             if (!target_sp)
382                 return ValueObjectSP();
383             m_pair_type = GetLLDBNSPairType(target_sp);
384         }
385         if (!m_pair_type.IsValid())
386             return ValueObjectSP();
387 
388         DataBufferSP buffer_sp(new DataBufferHeap(2*m_ptr_size,0));
389 
390         if (m_ptr_size == 8)
391         {
392             uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
393             *data_ptr = dict_item.key_ptr;
394             *(data_ptr+1) = dict_item.val_ptr;
395         }
396         else
397         {
398             uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
399             *data_ptr = dict_item.key_ptr;
400             *(data_ptr+1) = dict_item.val_ptr;
401         }
402 
403         StreamString idx_name;
404         idx_name.Printf("[%zu]",idx);
405         DataExtractor data(buffer_sp, m_order, m_ptr_size);
406         dict_item.valobj_sp = ValueObject::CreateValueObjectFromData(idx_name.GetData(), data, m_exe_ctx_ref, m_pair_type);
407     }
408     return dict_item.valobj_sp;
409 }
410 
NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)411 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::NSDictionaryMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
412 SyntheticChildrenFrontEnd(*valobj_sp.get()),
413 m_exe_ctx_ref(),
414 m_ptr_size(8),
415 m_order(lldb::eByteOrderInvalid),
416 m_data_32(NULL),
417 m_data_64(NULL),
418 m_pair_type()
419 {
420 }
421 
~NSDictionaryMSyntheticFrontEnd()422 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd ()
423 {
424     delete m_data_32;
425     m_data_32 = NULL;
426     delete m_data_64;
427     m_data_64 = NULL;
428 }
429 
430 size_t
GetIndexOfChildWithName(const ConstString & name)431 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
432 {
433     const char* item_name = name.GetCString();
434     uint32_t idx = ExtractIndexFromString(item_name);
435     if (idx < UINT32_MAX && idx >= CalculateNumChildren())
436         return UINT32_MAX;
437     return idx;
438 }
439 
440 size_t
CalculateNumChildren()441 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::CalculateNumChildren ()
442 {
443     if (!m_data_32 && !m_data_64)
444         return 0;
445     return (m_data_32 ? m_data_32->_used : m_data_64->_used);
446 }
447 
448 bool
Update()449 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::Update()
450 {
451     m_children.clear();
452     ValueObjectSP valobj_sp = m_backend.GetSP();
453     m_ptr_size = 0;
454     delete m_data_32;
455     m_data_32 = NULL;
456     delete m_data_64;
457     m_data_64 = NULL;
458     if (!valobj_sp)
459         return false;
460     m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
461     Error error;
462     error.Clear();
463     lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
464     if (!process_sp)
465         return false;
466     m_ptr_size = process_sp->GetAddressByteSize();
467     m_order = process_sp->GetByteOrder();
468     uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
469     if (m_ptr_size == 4)
470     {
471         m_data_32 = new DataDescriptor_32();
472         process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error);
473     }
474     else
475     {
476         m_data_64 = new DataDescriptor_64();
477         process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error);
478     }
479     if (error.Fail())
480         return false;
481     return false;
482 }
483 
484 bool
MightHaveChildren()485 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::MightHaveChildren ()
486 {
487     return true;
488 }
489 
490 lldb::ValueObjectSP
GetChildAtIndex(size_t idx)491 lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetChildAtIndex (size_t idx)
492 {
493     lldb::addr_t m_keys_ptr = (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
494     lldb::addr_t m_values_ptr = (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
495 
496     uint32_t num_children = CalculateNumChildren();
497 
498     if (idx >= num_children)
499         return lldb::ValueObjectSP();
500 
501     if (m_children.empty())
502     {
503         // do the scan phase
504         lldb::addr_t key_at_idx = 0, val_at_idx = 0;
505 
506         uint32_t tries = 0;
507         uint32_t test_idx = 0;
508 
509         while(tries < num_children)
510         {
511             key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
512             val_at_idx = m_values_ptr + (test_idx * m_ptr_size);;
513             ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
514             if (!process_sp)
515                 return lldb::ValueObjectSP();
516             Error error;
517             key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
518             if (error.Fail())
519                 return lldb::ValueObjectSP();
520             val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
521             if (error.Fail())
522                 return lldb::ValueObjectSP();
523 
524             test_idx++;
525 
526             if (!key_at_idx || !val_at_idx)
527                 continue;
528             tries++;
529 
530             DictionaryItemDescriptor descriptor = {key_at_idx,val_at_idx,lldb::ValueObjectSP()};
531 
532             m_children.push_back(descriptor);
533         }
534     }
535 
536     if (idx >= m_children.size()) // should never happen
537         return lldb::ValueObjectSP();
538 
539     DictionaryItemDescriptor &dict_item = m_children[idx];
540     if (!dict_item.valobj_sp)
541     {
542         if (!m_pair_type.IsValid())
543         {
544             TargetSP target_sp(m_backend.GetTargetSP());
545             if (!target_sp)
546                 return ValueObjectSP();
547             m_pair_type = GetLLDBNSPairType(target_sp);
548         }
549         if (!m_pair_type.IsValid())
550             return ValueObjectSP();
551 
552         DataBufferSP buffer_sp(new DataBufferHeap(2*m_ptr_size,0));
553 
554         if (m_ptr_size == 8)
555         {
556             uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
557             *data_ptr = dict_item.key_ptr;
558             *(data_ptr+1) = dict_item.val_ptr;
559         }
560         else
561         {
562             uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
563             *data_ptr = dict_item.key_ptr;
564             *(data_ptr+1) = dict_item.val_ptr;
565         }
566 
567         StreamString idx_name;
568         idx_name.Printf("[%zu]",idx);
569         DataExtractor data(buffer_sp, m_order, m_ptr_size);
570         dict_item.valobj_sp = ValueObject::CreateValueObjectFromData(idx_name.GetData(), data, m_exe_ctx_ref, m_pair_type);
571     }
572     return dict_item.valobj_sp;
573 }
574 
575 template bool
576 lldb_private::formatters::NSDictionarySummaryProvider<true> (ValueObject&, Stream&) ;
577 
578 template bool
579 lldb_private::formatters::NSDictionarySummaryProvider<false> (ValueObject&, Stream&) ;
580