1//===-- NSSet.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
28template<bool cf_style>
29bool
30lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream)
31{
32    ProcessSP process_sp = valobj.GetProcessSP();
33    if (!process_sp)
34        return false;
35
36    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
37
38    if (!runtime)
39        return false;
40
41    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
42
43    if (!descriptor.get() || !descriptor->IsValid())
44        return false;
45
46    uint32_t ptr_size = process_sp->GetAddressByteSize();
47    bool is_64bit = (ptr_size == 8);
48
49    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
50
51    if (!valobj_addr)
52        return false;
53
54    uint64_t value = 0;
55
56    const char* class_name = descriptor->GetClassName().GetCString();
57
58    if (!class_name || !*class_name)
59        return false;
60
61    if (!strcmp(class_name,"__NSSetI"))
62    {
63        Error error;
64        value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error);
65        if (error.Fail())
66            return false;
67        value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
68    }
69    else if (!strcmp(class_name,"__NSSetM"))
70    {
71        Error error;
72        value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error);
73        if (error.Fail())
74            return false;
75        value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
76    }
77    /*else if (!strcmp(class_name,"__NSCFSet"))
78    {
79        Error error;
80        value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ? 20 : 12), 4, 0, error);
81        if (error.Fail())
82            return false;
83        if (is_64bit)
84            value &= ~0x1fff000000000000UL;
85    }
86    else if (!strcmp(class_name,"NSCountedSet"))
87    {
88        Error error;
89        value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error);
90        if (error.Fail())
91            return false;
92        value = process_sp->ReadUnsignedIntegerFromMemory(value + (is_64bit ? 20 : 12), 4, 0, error);
93        if (error.Fail())
94            return false;
95        if (is_64bit)
96            value &= ~0x1fff000000000000UL;
97    }*/
98    else
99    {
100        if (!ExtractValueFromObjCExpression(valobj, "int", "count", value))
101            return false;
102    }
103
104    stream.Printf("%s%" PRIu64 " %s%s",
105                  (cf_style ? "@\"" : ""),
106                  value,
107                  (cf_style ? (value == 1 ? "value" : "values") : (value == 1 ? "object" : "objects")),
108                  (cf_style ? "\"" : ""));
109    return true;
110}
111
112SyntheticChildrenFrontEnd* lldb_private::formatters::NSSetSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
113{
114    lldb::ProcessSP process_sp (valobj_sp->GetProcessSP());
115    if (!process_sp)
116        return NULL;
117    ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
118    if (!runtime)
119        return NULL;
120
121    if (!valobj_sp->IsPointerType())
122    {
123        Error error;
124        valobj_sp = valobj_sp->AddressOf(error);
125        if (error.Fail() || !valobj_sp)
126            return NULL;
127    }
128
129    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get()));
130
131    if (!descriptor.get() || !descriptor->IsValid())
132        return NULL;
133
134    const char* class_name = descriptor->GetClassName().GetCString();
135
136    if (!class_name || !*class_name)
137        return NULL;
138
139    if (!strcmp(class_name,"__NSSetI"))
140    {
141        return (new NSSetISyntheticFrontEnd(valobj_sp));
142    }
143    else if (!strcmp(class_name,"__NSSetM"))
144    {
145        return (new NSSetMSyntheticFrontEnd(valobj_sp));
146    }
147    else if ((!strcmp(class_name,"__NSOrderedSetI")) || (!strcmp(class_name,"__NSOrderedSetM")))
148    {
149        return new NSOrderedSetSyntheticFrontEnd(valobj_sp); // this runs code
150    }
151    else
152    {
153        return /*(new NSSetCodeRunningSyntheticFrontEnd(valobj_sp))*/ NULL;
154    }
155}
156
157lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
158SyntheticChildrenFrontEnd(*valobj_sp.get()),
159m_exe_ctx_ref(),
160m_ptr_size(8),
161m_data_32(NULL),
162m_data_64(NULL)
163{
164    if (valobj_sp)
165        Update();
166}
167
168lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd ()
169{
170    delete m_data_32;
171    m_data_32 = NULL;
172    delete m_data_64;
173    m_data_64 = NULL;
174}
175
176size_t
177lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
178{
179    const char* item_name = name.GetCString();
180    uint32_t idx = ExtractIndexFromString(item_name);
181    if (idx < UINT32_MAX && idx >= CalculateNumChildren())
182        return UINT32_MAX;
183    return idx;
184}
185
186size_t
187lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren ()
188{
189    if (!m_data_32 && !m_data_64)
190        return 0;
191    return (m_data_32 ? m_data_32->_used : m_data_64->_used);
192}
193
194bool
195lldb_private::formatters::NSSetISyntheticFrontEnd::Update()
196{
197    m_children.clear();
198    delete m_data_32;
199    m_data_32 = NULL;
200    delete m_data_64;
201    m_data_64 = NULL;
202    m_ptr_size = 0;
203    ValueObjectSP valobj_sp = m_backend.GetSP();
204    if (!valobj_sp)
205        return false;
206    if (!valobj_sp)
207        return false;
208    m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
209    Error error;
210    if (valobj_sp->IsPointerType())
211    {
212        valobj_sp = valobj_sp->Dereference(error);
213        if (error.Fail() || !valobj_sp)
214            return false;
215    }
216    error.Clear();
217    lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
218    if (!process_sp)
219        return false;
220    m_ptr_size = process_sp->GetAddressByteSize();
221    uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
222    if (m_ptr_size == 4)
223    {
224        m_data_32 = new DataDescriptor_32();
225        process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error);
226    }
227    else
228    {
229        m_data_64 = new DataDescriptor_64();
230        process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error);
231    }
232    if (error.Fail())
233        return false;
234    m_data_ptr = data_location + m_ptr_size;
235    return false;
236}
237
238bool
239lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren ()
240{
241    return true;
242}
243
244lldb::ValueObjectSP
245lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex (size_t idx)
246{
247    uint32_t num_children = CalculateNumChildren();
248
249    if (idx >= num_children)
250        return lldb::ValueObjectSP();
251
252    ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
253    if (!process_sp)
254        return lldb::ValueObjectSP();
255
256    if (m_children.empty())
257    {
258        // do the scan phase
259        lldb::addr_t obj_at_idx = 0;
260
261        uint32_t tries = 0;
262        uint32_t test_idx = 0;
263
264        while(tries < num_children)
265        {
266            obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
267            if (!process_sp)
268                return lldb::ValueObjectSP();
269            Error error;
270            obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
271            if (error.Fail())
272                return lldb::ValueObjectSP();
273
274            test_idx++;
275
276            if (!obj_at_idx)
277                continue;
278            tries++;
279
280            SetItemDescriptor descriptor = {obj_at_idx,lldb::ValueObjectSP()};
281
282            m_children.push_back(descriptor);
283        }
284    }
285
286    if (idx >= m_children.size()) // should never happen
287        return lldb::ValueObjectSP();
288
289    SetItemDescriptor &set_item = m_children[idx];
290    if (!set_item.valobj_sp)
291    {
292        auto ptr_size = process_sp->GetAddressByteSize();
293        DataBufferHeap buffer(ptr_size,0);
294        switch (ptr_size)
295        {
296            case 0: // architecture has no clue?? - fail
297                return lldb::ValueObjectSP();
298            case 4:
299                *((uint32_t*)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
300                break;
301            case 8:
302                *((uint64_t*)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
303                break;
304            default:
305                assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
306        }
307        StreamString idx_name;
308        idx_name.Printf("[%zu]",idx);
309
310        DataExtractor data(buffer.GetBytes(),
311                           buffer.GetByteSize(),
312                           process_sp->GetByteOrder(),
313                           process_sp->GetAddressByteSize());
314
315        set_item.valobj_sp =
316            ValueObject::CreateValueObjectFromData(idx_name.GetData(),
317                                                   data,
318                                                   m_exe_ctx_ref,
319                                                   m_backend.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID));
320    }
321    return set_item.valobj_sp;
322}
323
324lldb_private::formatters::NSSetMSyntheticFrontEnd::NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
325SyntheticChildrenFrontEnd(*valobj_sp.get()),
326m_exe_ctx_ref(),
327m_ptr_size(8),
328m_data_32(NULL),
329m_data_64(NULL)
330{
331    if (valobj_sp)
332        Update ();
333}
334
335lldb_private::formatters::NSSetMSyntheticFrontEnd::~NSSetMSyntheticFrontEnd ()
336{
337    delete m_data_32;
338    m_data_32 = NULL;
339    delete m_data_64;
340    m_data_64 = NULL;
341}
342
343size_t
344lldb_private::formatters::NSSetMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
345{
346    const char* item_name = name.GetCString();
347    uint32_t idx = ExtractIndexFromString(item_name);
348    if (idx < UINT32_MAX && idx >= CalculateNumChildren())
349        return UINT32_MAX;
350    return idx;
351}
352
353size_t
354lldb_private::formatters::NSSetMSyntheticFrontEnd::CalculateNumChildren ()
355{
356    if (!m_data_32 && !m_data_64)
357        return 0;
358    return (m_data_32 ? m_data_32->_used : m_data_64->_used);
359}
360
361bool
362lldb_private::formatters::NSSetMSyntheticFrontEnd::Update()
363{
364    m_children.clear();
365    ValueObjectSP valobj_sp = m_backend.GetSP();
366    m_ptr_size = 0;
367    delete m_data_32;
368    m_data_32 = NULL;
369    delete m_data_64;
370    m_data_64 = NULL;
371    if (!valobj_sp)
372        return false;
373    if (!valobj_sp)
374        return false;
375    m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
376    Error error;
377    if (valobj_sp->IsPointerType())
378    {
379        valobj_sp = valobj_sp->Dereference(error);
380        if (error.Fail() || !valobj_sp)
381            return false;
382    }
383    error.Clear();
384    lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
385    if (!process_sp)
386        return false;
387    m_ptr_size = process_sp->GetAddressByteSize();
388    uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
389    if (m_ptr_size == 4)
390    {
391        m_data_32 = new DataDescriptor_32();
392        process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error);
393    }
394    else
395    {
396        m_data_64 = new DataDescriptor_64();
397        process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error);
398    }
399    if (error.Fail())
400        return false;
401    return false;
402}
403
404bool
405lldb_private::formatters::NSSetMSyntheticFrontEnd::MightHaveChildren ()
406{
407    return true;
408}
409
410lldb::ValueObjectSP
411lldb_private::formatters::NSSetMSyntheticFrontEnd::GetChildAtIndex (size_t idx)
412{
413    lldb::addr_t m_objs_addr = (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
414
415    uint32_t num_children = CalculateNumChildren();
416
417    if (idx >= num_children)
418        return lldb::ValueObjectSP();
419
420    ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
421    if (!process_sp)
422        return lldb::ValueObjectSP();
423
424    if (m_children.empty())
425    {
426        // do the scan phase
427        lldb::addr_t obj_at_idx = 0;
428
429        uint32_t tries = 0;
430        uint32_t test_idx = 0;
431
432        while(tries < num_children)
433        {
434            obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
435            if (!process_sp)
436                return lldb::ValueObjectSP();
437            Error error;
438            obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
439            if (error.Fail())
440                return lldb::ValueObjectSP();
441
442            test_idx++;
443
444            if (!obj_at_idx)
445                continue;
446            tries++;
447
448            SetItemDescriptor descriptor = {obj_at_idx,lldb::ValueObjectSP()};
449
450            m_children.push_back(descriptor);
451        }
452    }
453
454    if (idx >= m_children.size()) // should never happen
455        return lldb::ValueObjectSP();
456
457    SetItemDescriptor &set_item = m_children[idx];
458    if (!set_item.valobj_sp)
459    {
460        auto ptr_size = process_sp->GetAddressByteSize();
461        DataBufferHeap buffer(ptr_size,0);
462        switch (ptr_size)
463        {
464            case 0: // architecture has no clue?? - fail
465                return lldb::ValueObjectSP();
466            case 4:
467                *((uint32_t*)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
468                break;
469            case 8:
470                *((uint64_t*)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
471                break;
472            default:
473                assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
474        }
475        StreamString idx_name;
476        idx_name.Printf("[%zu]",idx);
477
478        DataExtractor data(buffer.GetBytes(),
479                           buffer.GetByteSize(),
480                           process_sp->GetByteOrder(),
481                           process_sp->GetAddressByteSize());
482
483        set_item.valobj_sp =
484            ValueObject::CreateValueObjectFromData(idx_name.GetData(),
485                                                   data,
486                                                   m_exe_ctx_ref,
487                                                   m_backend.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID));
488    }
489    return set_item.valobj_sp;
490}
491
492lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::NSOrderedSetSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
493SyntheticChildrenFrontEnd(*valobj_sp.get()),
494m_count(UINT32_MAX),
495m_children()
496{}
497
498size_t
499lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::CalculateNumChildren ()
500{
501    if (m_count != UINT32_MAX)
502        return m_count;
503    uint64_t count_temp;
504    if (ExtractValueFromObjCExpression(m_backend,"unsigned int","count",count_temp))
505        return (m_count = count_temp);
506    return (m_count = 0);
507}
508
509lldb::ValueObjectSP
510lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::GetChildAtIndex (size_t idx)
511{
512    auto iter = m_children.find(idx);
513    if (iter == m_children.end())
514    {
515        lldb::ValueObjectSP retval_sp;
516        if (idx <= m_count)
517        {
518            retval_sp = CallSelectorOnObject(m_backend, "id", "objectAtIndex", idx);
519            if (retval_sp)
520            {
521                StreamString idx_name;
522                idx_name.Printf("[%zu]",idx);
523                retval_sp->SetName(ConstString(idx_name.GetData()));
524            }
525            m_children[idx] = retval_sp;
526        }
527        return retval_sp;
528    }
529    else
530        return iter->second;
531}
532
533bool
534lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::Update()
535{
536    return false;
537}
538
539bool
540lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::MightHaveChildren ()
541{
542    return true;
543}
544
545size_t
546lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
547{
548    const char* item_name = name.GetCString();
549    uint32_t idx = ExtractIndexFromString(item_name);
550    if (idx < UINT32_MAX && idx >= CalculateNumChildren())
551        return UINT32_MAX;
552    return idx;
553}
554
555lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::~NSOrderedSetSyntheticFrontEnd ()
556{
557}
558
559template bool
560lldb_private::formatters::NSSetSummaryProvider<true> (ValueObject& valobj, Stream& stream);
561
562template bool
563lldb_private::formatters::NSSetSummaryProvider<false> (ValueObject& valobj, Stream& stream);
564