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    if (m_children.empty())
253    {
254        // do the scan phase
255        lldb::addr_t obj_at_idx = 0;
256
257        uint32_t tries = 0;
258        uint32_t test_idx = 0;
259
260        while(tries < num_children)
261        {
262            obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
263            ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
264            if (!process_sp)
265                return lldb::ValueObjectSP();
266            Error error;
267            obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
268            if (error.Fail())
269                return lldb::ValueObjectSP();
270
271            test_idx++;
272
273            if (!obj_at_idx)
274                continue;
275            tries++;
276
277            SetItemDescriptor descriptor = {obj_at_idx,lldb::ValueObjectSP()};
278
279            m_children.push_back(descriptor);
280        }
281    }
282
283    if (idx >= m_children.size()) // should never happen
284        return lldb::ValueObjectSP();
285
286    SetItemDescriptor &set_item = m_children[idx];
287    if (!set_item.valobj_sp)
288    {
289        // make the new ValueObject
290        StreamString expr;
291        expr.Printf("(id)%" PRIu64,set_item.item_ptr);
292        StreamString idx_name;
293        idx_name.Printf("[%zu]",idx);
294        set_item.valobj_sp = ValueObject::CreateValueObjectFromExpression(idx_name.GetData(), expr.GetData(), m_exe_ctx_ref);
295    }
296    return set_item.valobj_sp;
297}
298
299lldb_private::formatters::NSSetMSyntheticFrontEnd::NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
300SyntheticChildrenFrontEnd(*valobj_sp.get()),
301m_exe_ctx_ref(),
302m_ptr_size(8),
303m_data_32(NULL),
304m_data_64(NULL)
305{
306    if (valobj_sp)
307        Update ();
308}
309
310lldb_private::formatters::NSSetMSyntheticFrontEnd::~NSSetMSyntheticFrontEnd ()
311{
312    delete m_data_32;
313    m_data_32 = NULL;
314    delete m_data_64;
315    m_data_64 = NULL;
316}
317
318size_t
319lldb_private::formatters::NSSetMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
320{
321    const char* item_name = name.GetCString();
322    uint32_t idx = ExtractIndexFromString(item_name);
323    if (idx < UINT32_MAX && idx >= CalculateNumChildren())
324        return UINT32_MAX;
325    return idx;
326}
327
328size_t
329lldb_private::formatters::NSSetMSyntheticFrontEnd::CalculateNumChildren ()
330{
331    if (!m_data_32 && !m_data_64)
332        return 0;
333    return (m_data_32 ? m_data_32->_used : m_data_64->_used);
334}
335
336bool
337lldb_private::formatters::NSSetMSyntheticFrontEnd::Update()
338{
339    m_children.clear();
340    ValueObjectSP valobj_sp = m_backend.GetSP();
341    m_ptr_size = 0;
342    delete m_data_32;
343    m_data_32 = NULL;
344    delete m_data_64;
345    m_data_64 = NULL;
346    if (!valobj_sp)
347        return false;
348    if (!valobj_sp)
349        return false;
350    m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
351    Error error;
352    if (valobj_sp->IsPointerType())
353    {
354        valobj_sp = valobj_sp->Dereference(error);
355        if (error.Fail() || !valobj_sp)
356            return false;
357    }
358    error.Clear();
359    lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
360    if (!process_sp)
361        return false;
362    m_ptr_size = process_sp->GetAddressByteSize();
363    uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
364    if (m_ptr_size == 4)
365    {
366        m_data_32 = new DataDescriptor_32();
367        process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error);
368    }
369    else
370    {
371        m_data_64 = new DataDescriptor_64();
372        process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error);
373    }
374    if (error.Fail())
375        return false;
376    return false;
377}
378
379bool
380lldb_private::formatters::NSSetMSyntheticFrontEnd::MightHaveChildren ()
381{
382    return true;
383}
384
385lldb::ValueObjectSP
386lldb_private::formatters::NSSetMSyntheticFrontEnd::GetChildAtIndex (size_t idx)
387{
388    lldb::addr_t m_objs_addr = (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
389
390    uint32_t num_children = CalculateNumChildren();
391
392    if (idx >= num_children)
393        return lldb::ValueObjectSP();
394
395    if (m_children.empty())
396    {
397        // do the scan phase
398        lldb::addr_t obj_at_idx = 0;
399
400        uint32_t tries = 0;
401        uint32_t test_idx = 0;
402
403        while(tries < num_children)
404        {
405            obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
406            ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
407            if (!process_sp)
408                return lldb::ValueObjectSP();
409            Error error;
410            obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
411            if (error.Fail())
412                return lldb::ValueObjectSP();
413
414            test_idx++;
415
416            if (!obj_at_idx)
417                continue;
418            tries++;
419
420            SetItemDescriptor descriptor = {obj_at_idx,lldb::ValueObjectSP()};
421
422            m_children.push_back(descriptor);
423        }
424    }
425
426    if (idx >= m_children.size()) // should never happen
427        return lldb::ValueObjectSP();
428
429    SetItemDescriptor &set_item = m_children[idx];
430    if (!set_item.valobj_sp)
431    {
432        // make the new ValueObject
433        StreamString expr;
434        expr.Printf("(id)%" PRIu64,set_item.item_ptr);
435        StreamString idx_name;
436        idx_name.Printf("[%zu]",idx);
437        set_item.valobj_sp = ValueObject::CreateValueObjectFromExpression(idx_name.GetData(), expr.GetData(), m_exe_ctx_ref);
438    }
439    return set_item.valobj_sp;
440}
441
442lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::NSOrderedSetSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
443SyntheticChildrenFrontEnd(*valobj_sp.get()),
444m_count(UINT32_MAX),
445m_children()
446{}
447
448size_t
449lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::CalculateNumChildren ()
450{
451    if (m_count != UINT32_MAX)
452        return m_count;
453    uint64_t count_temp;
454    if (ExtractValueFromObjCExpression(m_backend,"unsigned int","count",count_temp))
455        return (m_count = count_temp);
456    return (m_count = 0);
457}
458
459lldb::ValueObjectSP
460lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::GetChildAtIndex (size_t idx)
461{
462    auto iter = m_children.find(idx);
463    if (iter == m_children.end())
464    {
465        lldb::ValueObjectSP retval_sp;
466        if (idx <= m_count)
467        {
468            retval_sp = CallSelectorOnObject(m_backend, "id", "objectAtIndex", idx);
469            if (retval_sp)
470            {
471                StreamString idx_name;
472                idx_name.Printf("[%zu]",idx);
473                retval_sp->SetName(ConstString(idx_name.GetData()));
474            }
475            m_children[idx] = retval_sp;
476        }
477        return retval_sp;
478    }
479    else
480        return iter->second;
481}
482
483bool
484lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::Update()
485{
486    return false;
487}
488
489bool
490lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::MightHaveChildren ()
491{
492    return true;
493}
494
495size_t
496lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name)
497{
498    const char* item_name = name.GetCString();
499    uint32_t idx = ExtractIndexFromString(item_name);
500    if (idx < UINT32_MAX && idx >= CalculateNumChildren())
501        return UINT32_MAX;
502    return idx;
503}
504
505lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::~NSOrderedSetSyntheticFrontEnd ()
506{
507}
508
509template bool
510lldb_private::formatters::NSSetSummaryProvider<true> (ValueObject& valobj, Stream& stream);
511
512template bool
513lldb_private::formatters::NSSetSummaryProvider<false> (ValueObject& valobj, Stream& stream);
514