1"""
2LLDB AppKit formatters
3
4Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5See https://llvm.org/LICENSE.txt for license information.
6SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7"""
8
9# summary provider for CF(Mutable)BitVector
10import lldb
11import ctypes
12import lldb.runtime.objc.objc_runtime
13import lldb.formatters.metrics
14import lldb.formatters.Logger
15
16# first define some utility functions
17
18
19def byte_index(abs_pos):
20    logger = lldb.formatters.Logger.Logger()
21    return abs_pos / 8
22
23
24def bit_index(abs_pos):
25    logger = lldb.formatters.Logger.Logger()
26    return abs_pos & 7
27
28
29def get_bit(byte, index):
30    logger = lldb.formatters.Logger.Logger()
31    if index < 0 or index > 7:
32        return None
33    return (byte >> (7 - index)) & 1
34
35
36def grab_array_item_data(pointer, index):
37    logger = lldb.formatters.Logger.Logger()
38    return pointer.GetPointeeData(index, 1)
39
40statistics = lldb.formatters.metrics.Metrics()
41statistics.add_metric('invalid_isa')
42statistics.add_metric('invalid_pointer')
43statistics.add_metric('unknown_class')
44statistics.add_metric('code_notrun')
45
46# despite the similary to synthetic children providers, these classes are not
47# trying to provide anything but a summary for a CF*BitVector, so they need not
48# obey the interface specification for synthetic children providers
49
50
51class CFBitVectorKnown_SummaryProvider:
52
53    def adjust_for_architecture(self):
54        logger = lldb.formatters.Logger.Logger()
55        self.uiint_size = self.sys_params.types_cache.NSUInteger.GetByteSize()
56        pass
57
58    def __init__(self, valobj, params):
59        logger = lldb.formatters.Logger.Logger()
60        self.valobj = valobj
61        self.sys_params = params
62        if not(self.sys_params.types_cache.NSUInteger):
63            if self.sys_params.is_64_bit:
64                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
65                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
66            else:
67                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
68                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
69        if not(self.sys_params.types_cache.charptr):
70            self.sys_params.types_cache.charptr = self.valobj.GetType(
71            ).GetBasicType(lldb.eBasicTypeChar).GetPointerType()
72        self.update()
73
74    def update(self):
75        logger = lldb.formatters.Logger.Logger()
76        self.adjust_for_architecture()
77
78    # we skip the CFRuntimeBase
79    # then the next CFIndex is the count
80    # then we skip another CFIndex and then we get at a byte array
81    # that wraps the individual bits
82
83    def contents(self):
84        logger = lldb.formatters.Logger.Logger()
85        count_vo = self.valobj.CreateChildAtOffset(
86            "count",
87            self.sys_params.cfruntime_size,
88            self.sys_params.types_cache.NSUInteger)
89        count = count_vo.GetValueAsUnsigned(0)
90        if count == 0:
91            return '(empty)'
92
93        array_vo = self.valobj.CreateChildAtOffset(
94            "data",
95            self.sys_params.cfruntime_size +
96            2 *
97            self.uiint_size,
98            self.sys_params.types_cache.charptr)
99
100        data_list = []
101        cur_byte_pos = None
102        for i in range(0, count):
103            if cur_byte_pos is None:
104                cur_byte_pos = byte_index(i)
105                cur_byte = grab_array_item_data(array_vo, cur_byte_pos)
106                cur_byte_val = cur_byte.uint8[0]
107            else:
108                byte_pos = byte_index(i)
109                # do not fetch the pointee data every single time through
110                if byte_pos != cur_byte_pos:
111                    cur_byte_pos = byte_pos
112                    cur_byte = grab_array_item_data(array_vo, cur_byte_pos)
113                    cur_byte_val = cur_byte.uint8[0]
114            bit = get_bit(cur_byte_val, bit_index(i))
115            if (i % 4) == 0:
116                data_list.append(' ')
117            if bit == 1:
118                data_list.append('1')
119            else:
120                data_list.append('0')
121        return ''.join(data_list)
122
123
124class CFBitVectorUnknown_SummaryProvider:
125
126    def adjust_for_architecture(self):
127        pass
128
129    def __init__(self, valobj, params):
130        logger = lldb.formatters.Logger.Logger()
131        self.valobj = valobj
132        self.sys_params = params
133        self.update()
134
135    def update(self):
136        logger = lldb.formatters.Logger.Logger()
137        self.adjust_for_architecture()
138
139    def contents(self):
140        logger = lldb.formatters.Logger.Logger()
141        return '<unable to summarize this CFBitVector>'
142
143
144def GetSummary_Impl(valobj):
145    logger = lldb.formatters.Logger.Logger()
146    global statistics
147    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
148        valobj, statistics)
149    if wrapper:
150        return wrapper
151
152    name_string = class_data.class_name()
153    actual_name = name_string
154
155    logger >> "name string got was " + \
156        str(name_string) + " but actual name is " + str(actual_name)
157
158    if class_data.is_cftype():
159        # CFBitVectorRef does not expose an actual NSWrapper type, so we have to check that this is
160        # an NSCFType and then check we are a pointer-to CFBitVectorRef
161        valobj_type = valobj.GetType()
162        if valobj_type.IsValid() and valobj_type.IsPointerType():
163            valobj_type = valobj_type.GetPointeeType()
164            if valobj_type.IsValid():
165                actual_name = valobj_type.GetName()
166        if actual_name == '__CFBitVector' or actual_name == '__CFMutableBitVector':
167            wrapper = CFBitVectorKnown_SummaryProvider(
168                valobj, class_data.sys_params)
169            statistics.metric_hit('code_notrun', valobj)
170        else:
171            wrapper = CFBitVectorUnknown_SummaryProvider(
172                valobj, class_data.sys_params)
173            print(actual_name)
174    else:
175        wrapper = CFBitVectorUnknown_SummaryProvider(
176            valobj, class_data.sys_params)
177        print(name_string)
178        statistics.metric_hit(
179            'unknown_class',
180            valobj.GetName() +
181            " seen as " +
182            name_string)
183    return wrapper
184
185
186def CFBitVector_SummaryProvider(valobj, dict):
187    logger = lldb.formatters.Logger.Logger()
188    provider = GetSummary_Impl(valobj)
189    if provider is not None:
190        if isinstance(
191                provider,
192                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
193            return provider.message()
194        try:
195            summary = provider.contents()
196        except:
197            summary = None
198        logger >> "summary got from provider: " + str(summary)
199        if summary is None or summary == '':
200            summary = '<variable is not CFBitVector>'
201        return summary
202    return 'Summary Unavailable'
203
204
205def __lldb_init_module(debugger, dict):
206    debugger.HandleCommand(
207        "type summary add -F CFBitVector.CFBitVector_SummaryProvider CFBitVectorRef CFMutableBitVectorRef")
208