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