1*061da546Spatrick"""
2*061da546SpatrickLLDB AppKit formatters
3*061da546Spatrick
4*061da546SpatrickPart of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5*061da546SpatrickSee https://llvm.org/LICENSE.txt for license information.
6*061da546SpatrickSPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7*061da546Spatrick"""
8*061da546Spatrick# example summary provider for NSNumber
9*061da546Spatrick# the real summary is now C++ code built into LLDB
10*061da546Spatrick
11*061da546Spatrickimport lldb
12*061da546Spatrickimport ctypes
13*061da546Spatrickimport lldb.runtime.objc.objc_runtime
14*061da546Spatrickimport lldb.formatters.metrics
15*061da546Spatrickimport struct
16*061da546Spatrickimport lldb.formatters.Logger
17*061da546Spatrick
18*061da546Spatrickstatistics = lldb.formatters.metrics.Metrics()
19*061da546Spatrickstatistics.add_metric('invalid_isa')
20*061da546Spatrickstatistics.add_metric('invalid_pointer')
21*061da546Spatrickstatistics.add_metric('unknown_class')
22*061da546Spatrickstatistics.add_metric('code_notrun')
23*061da546Spatrick
24*061da546Spatrick# despite the similary to synthetic children providers, these classes are not
25*061da546Spatrick# trying to provide anything but the port number of an NSNumber, so they need not
26*061da546Spatrick# obey the interface specification for synthetic children providers
27*061da546Spatrick
28*061da546Spatrick
29*061da546Spatrickclass NSTaggedNumber_SummaryProvider:
30*061da546Spatrick
31*061da546Spatrick    def adjust_for_architecture(self):
32*061da546Spatrick        pass
33*061da546Spatrick
34*061da546Spatrick    def __init__(self, valobj, info_bits, data, params):
35*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
36*061da546Spatrick        self.valobj = valobj
37*061da546Spatrick        self.sys_params = params
38*061da546Spatrick        self.info_bits = info_bits
39*061da546Spatrick        self.data = data
40*061da546Spatrick        self.update()
41*061da546Spatrick
42*061da546Spatrick    def update(self):
43*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
44*061da546Spatrick        self.adjust_for_architecture()
45*061da546Spatrick
46*061da546Spatrick    def value(self):
47*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
48*061da546Spatrick        # in spite of the plenty of types made available by the public NSNumber API
49*061da546Spatrick        # only a bunch of these are actually used in the internal implementation
50*061da546Spatrick        # unfortunately, the original type information appears to be lost
51*061da546Spatrick        # so we try to at least recover the proper magnitude of the data
52*061da546Spatrick        if self.info_bits == 0:
53*061da546Spatrick            return '(char)' + \
54*061da546Spatrick                str(ord(ctypes.c_char(chr(self.data % 256)).value))
55*061da546Spatrick        if self.info_bits == 4:
56*061da546Spatrick            return '(short)' + \
57*061da546Spatrick                str(ctypes.c_short(self.data % (256 * 256)).value)
58*061da546Spatrick        if self.info_bits == 8:
59*061da546Spatrick            return '(int)' + str(ctypes.c_int(self.data %
60*061da546Spatrick                                              (256 * 256 * 256 * 256)).value)
61*061da546Spatrick        if self.info_bits == 12:
62*061da546Spatrick            return '(long)' + str(ctypes.c_long(self.data).value)
63*061da546Spatrick        else:
64*061da546Spatrick            return 'unexpected value:(info=' + str(self.info_bits) + \
65*061da546Spatrick                ", value = " + str(self.data) + ')'
66*061da546Spatrick
67*061da546Spatrick
68*061da546Spatrickclass NSUntaggedNumber_SummaryProvider:
69*061da546Spatrick
70*061da546Spatrick    def adjust_for_architecture(self):
71*061da546Spatrick        pass
72*061da546Spatrick
73*061da546Spatrick    def __init__(self, valobj, params):
74*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
75*061da546Spatrick        self.valobj = valobj
76*061da546Spatrick        self.sys_params = params
77*061da546Spatrick        if not(self.sys_params.types_cache.char):
78*061da546Spatrick            self.sys_params.types_cache.char = self.valobj.GetType(
79*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeChar)
80*061da546Spatrick        if not(self.sys_params.types_cache.short):
81*061da546Spatrick            self.sys_params.types_cache.short = self.valobj.GetType(
82*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeShort)
83*061da546Spatrick        if not(self.sys_params.types_cache.ushort):
84*061da546Spatrick            self.sys_params.types_cache.ushort = self.valobj.GetType(
85*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeUnsignedShort)
86*061da546Spatrick        if not(self.sys_params.types_cache.int):
87*061da546Spatrick            self.sys_params.types_cache.int = self.valobj.GetType().GetBasicType(lldb.eBasicTypeInt)
88*061da546Spatrick        if not(self.sys_params.types_cache.long):
89*061da546Spatrick            self.sys_params.types_cache.long = self.valobj.GetType(
90*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeLong)
91*061da546Spatrick        if not(self.sys_params.types_cache.ulong):
92*061da546Spatrick            self.sys_params.types_cache.ulong = self.valobj.GetType(
93*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeUnsignedLong)
94*061da546Spatrick        if not(self.sys_params.types_cache.longlong):
95*061da546Spatrick            self.sys_params.types_cache.longlong = self.valobj.GetType(
96*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeLongLong)
97*061da546Spatrick        if not(self.sys_params.types_cache.ulonglong):
98*061da546Spatrick            self.sys_params.types_cache.ulonglong = self.valobj.GetType(
99*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeUnsignedLongLong)
100*061da546Spatrick        if not(self.sys_params.types_cache.float):
101*061da546Spatrick            self.sys_params.types_cache.float = self.valobj.GetType(
102*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeFloat)
103*061da546Spatrick        if not(self.sys_params.types_cache.double):
104*061da546Spatrick            self.sys_params.types_cache.double = self.valobj.GetType(
105*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeDouble)
106*061da546Spatrick        self.update()
107*061da546Spatrick
108*061da546Spatrick    def update(self):
109*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
110*061da546Spatrick        self.adjust_for_architecture()
111*061da546Spatrick
112*061da546Spatrick    def value(self):
113*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
114*061da546Spatrick        global statistics
115*061da546Spatrick        # we need to skip the ISA, then the next byte tells us what to read
116*061da546Spatrick        # we then skip one other full pointer worth of data and then fetch the contents
117*061da546Spatrick        # if we are fetching an int64 value, one more pointer must be skipped
118*061da546Spatrick        # to get at our data
119*061da546Spatrick        data_type_vo = self.valobj.CreateChildAtOffset(
120*061da546Spatrick            "dt", self.sys_params.pointer_size, self.sys_params.types_cache.char)
121*061da546Spatrick        data_type = ((data_type_vo.GetValueAsUnsigned(0) % 256) & 0x1F)
122*061da546Spatrick        data_offset = 2 * self.sys_params.pointer_size
123*061da546Spatrick        if data_type == 0B00001:
124*061da546Spatrick            data_vo = self.valobj.CreateChildAtOffset(
125*061da546Spatrick                "data", data_offset, self.sys_params.types_cache.char)
126*061da546Spatrick            statistics.metric_hit('code_notrun', self.valobj)
127*061da546Spatrick            return '(char)' + \
128*061da546Spatrick                str(ord(ctypes.c_char(chr(data_vo.GetValueAsUnsigned(0))).value))
129*061da546Spatrick        elif data_type == 0B0010:
130*061da546Spatrick            data_vo = self.valobj.CreateChildAtOffset(
131*061da546Spatrick                "data", data_offset, self.sys_params.types_cache.short)
132*061da546Spatrick            statistics.metric_hit('code_notrun', self.valobj)
133*061da546Spatrick            return '(short)' + str(
134*061da546Spatrick                ctypes.c_short(
135*061da546Spatrick                    data_vo.GetValueAsUnsigned(0) %
136*061da546Spatrick                    (256 * 256)).value)
137*061da546Spatrick        # IF tagged pointers are possible on 32bit+v2 runtime
138*061da546Spatrick        # (of which the only existing instance should be iOS)
139*061da546Spatrick        # then values of this type might be tagged
140*061da546Spatrick        elif data_type == 0B0011:
141*061da546Spatrick            data_vo = self.valobj.CreateChildAtOffset(
142*061da546Spatrick                "data", data_offset, self.sys_params.types_cache.int)
143*061da546Spatrick            statistics.metric_hit('code_notrun', self.valobj)
144*061da546Spatrick            return '(int)' + str(ctypes.c_int(data_vo.GetValueAsUnsigned(0) %
145*061da546Spatrick                                              (256 * 256 * 256 * 256)).value)
146*061da546Spatrick        # apparently, on is_64_bit architectures, these are the only values that will ever
147*061da546Spatrick        # be represented by a non tagged pointers
148*061da546Spatrick        elif data_type == 0B10001:
149*061da546Spatrick            data_offset = data_offset + 8  # 8 is needed even if we are on 32bit
150*061da546Spatrick            data_vo = self.valobj.CreateChildAtOffset(
151*061da546Spatrick                "data", data_offset, self.sys_params.types_cache.longlong)
152*061da546Spatrick            statistics.metric_hit('code_notrun', self.valobj)
153*061da546Spatrick            return '(long)' + \
154*061da546Spatrick                str(ctypes.c_long(data_vo.GetValueAsUnsigned(0)).value)
155*061da546Spatrick        elif data_type == 0B0100:
156*061da546Spatrick            if self.sys_params.is_64_bit:
157*061da546Spatrick                data_offset = data_offset + self.sys_params.pointer_size
158*061da546Spatrick            data_vo = self.valobj.CreateChildAtOffset(
159*061da546Spatrick                "data", data_offset, self.sys_params.types_cache.longlong)
160*061da546Spatrick            statistics.metric_hit('code_notrun', self.valobj)
161*061da546Spatrick            return '(long)' + \
162*061da546Spatrick                str(ctypes.c_long(data_vo.GetValueAsUnsigned(0)).value)
163*061da546Spatrick        elif data_type == 0B0101:
164*061da546Spatrick            data_vo = self.valobj.CreateChildAtOffset(
165*061da546Spatrick                "data", data_offset, self.sys_params.types_cache.longlong)
166*061da546Spatrick            data_plain = int(
167*061da546Spatrick                str(data_vo.GetValueAsUnsigned(0) & 0x00000000FFFFFFFF))
168*061da546Spatrick            packed = struct.pack('I', data_plain)
169*061da546Spatrick            data_float = struct.unpack('f', packed)[0]
170*061da546Spatrick            statistics.metric_hit('code_notrun', self.valobj)
171*061da546Spatrick            return '(float)' + str(data_float)
172*061da546Spatrick        elif data_type == 0B0110:
173*061da546Spatrick            data_vo = self.valobj.CreateChildAtOffset(
174*061da546Spatrick                "data", data_offset, self.sys_params.types_cache.longlong)
175*061da546Spatrick            data_plain = data_vo.GetValueAsUnsigned(0)
176*061da546Spatrick            data_double = struct.unpack('d', struct.pack('Q', data_plain))[0]
177*061da546Spatrick            statistics.metric_hit('code_notrun', self.valobj)
178*061da546Spatrick            return '(double)' + str(data_double)
179*061da546Spatrick        statistics.metric_hit(
180*061da546Spatrick            'unknown_class', str(
181*061da546Spatrick                valobj.GetName()) + " had unknown data_type " + str(data_type))
182*061da546Spatrick        return 'unexpected: dt = ' + str(data_type)
183*061da546Spatrick
184*061da546Spatrick
185*061da546Spatrickclass NSUnknownNumber_SummaryProvider:
186*061da546Spatrick
187*061da546Spatrick    def adjust_for_architecture(self):
188*061da546Spatrick        pass
189*061da546Spatrick
190*061da546Spatrick    def __init__(self, valobj, params):
191*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
192*061da546Spatrick        self.valobj = valobj
193*061da546Spatrick        self.sys_params = params
194*061da546Spatrick        self.update()
195*061da546Spatrick
196*061da546Spatrick    def update(self):
197*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
198*061da546Spatrick        self.adjust_for_architecture()
199*061da546Spatrick
200*061da546Spatrick    def value(self):
201*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
202*061da546Spatrick        stream = lldb.SBStream()
203*061da546Spatrick        self.valobj.GetExpressionPath(stream)
204*061da546Spatrick        expr = "(NSString*)[" + stream.GetData() + " stringValue]"
205*061da546Spatrick        num_children_vo = self.valobj.CreateValueFromExpression("str", expr)
206*061da546Spatrick        if num_children_vo.IsValid():
207*061da546Spatrick            return num_children_vo.GetSummary()
208*061da546Spatrick        return '<variable is not NSNumber>'
209*061da546Spatrick
210*061da546Spatrick
211*061da546Spatrickdef GetSummary_Impl(valobj):
212*061da546Spatrick    logger = lldb.formatters.Logger.Logger()
213*061da546Spatrick    global statistics
214*061da546Spatrick    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
215*061da546Spatrick        valobj, statistics)
216*061da546Spatrick    if wrapper:
217*061da546Spatrick        return wrapper
218*061da546Spatrick
219*061da546Spatrick    name_string = class_data.class_name()
220*061da546Spatrick    logger >> "class name is: " + str(name_string)
221*061da546Spatrick
222*061da546Spatrick    if name_string == 'NSNumber' or name_string == '__NSCFNumber':
223*061da546Spatrick        if class_data.is_tagged():
224*061da546Spatrick            wrapper = NSTaggedNumber_SummaryProvider(
225*061da546Spatrick                valobj, class_data.info_bits(), class_data.value(), class_data.sys_params)
226*061da546Spatrick            statistics.metric_hit('code_notrun', valobj)
227*061da546Spatrick        else:
228*061da546Spatrick            # the wrapper might be unable to decipher what is into the NSNumber
229*061da546Spatrick            # and then have to run code on it
230*061da546Spatrick            wrapper = NSUntaggedNumber_SummaryProvider(
231*061da546Spatrick                valobj, class_data.sys_params)
232*061da546Spatrick    else:
233*061da546Spatrick        wrapper = NSUnknownNumber_SummaryProvider(
234*061da546Spatrick            valobj, class_data.sys_params)
235*061da546Spatrick        statistics.metric_hit(
236*061da546Spatrick            'unknown_class',
237*061da546Spatrick            valobj.GetName() +
238*061da546Spatrick            " seen as " +
239*061da546Spatrick            name_string)
240*061da546Spatrick    return wrapper
241*061da546Spatrick
242*061da546Spatrick
243*061da546Spatrickdef NSNumber_SummaryProvider(valobj, dict):
244*061da546Spatrick    logger = lldb.formatters.Logger.Logger()
245*061da546Spatrick    provider = GetSummary_Impl(valobj)
246*061da546Spatrick    if provider is not None:
247*061da546Spatrick        if isinstance(
248*061da546Spatrick                provider,
249*061da546Spatrick                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
250*061da546Spatrick            return provider.message()
251*061da546Spatrick        try:
252*061da546Spatrick            summary = provider.value()
253*061da546Spatrick        except Exception as foo:
254*061da546Spatrick            print(foo)
255*061da546Spatrick#		except:
256*061da546Spatrick            summary = None
257*061da546Spatrick        logger >> "got summary " + str(summary)
258*061da546Spatrick        if summary is None:
259*061da546Spatrick            summary = '<variable is not NSNumber>'
260*061da546Spatrick        return str(summary)
261*061da546Spatrick    return 'Summary Unavailable'
262*061da546Spatrick
263*061da546Spatrick
264*061da546Spatrickdef __lldb_init_module(debugger, dict):
265*061da546Spatrick    debugger.HandleCommand(
266*061da546Spatrick        "type summary add -F NSNumber.NSNumber_SummaryProvider NSNumber")
267*061da546Spatrick    debugger.HandleCommand(
268*061da546Spatrick        "type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFBoolean")
269*061da546Spatrick    debugger.HandleCommand(
270*061da546Spatrick        "type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFNumber")
271