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# example summary provider for NS(Mutable)IndexSet
9# the real summary is now C++ code built into LLDB
10import lldb
11import ctypes
12import lldb.runtime.objc.objc_runtime
13import lldb.formatters.metrics
14import lldb.formatters.Logger
15
16try:
17    basestring
18except NameError:
19    basestring = str
20
21statistics = lldb.formatters.metrics.Metrics()
22statistics.add_metric('invalid_isa')
23statistics.add_metric('invalid_pointer')
24statistics.add_metric('unknown_class')
25statistics.add_metric('code_notrun')
26
27# despite the similary to synthetic children providers, these classes are not
28# trying to provide anything but the count of values for an NSIndexSet, so they need not
29# obey the interface specification for synthetic children providers
30
31
32class NSIndexSetClass_SummaryProvider:
33
34    def adjust_for_architecture(self):
35        pass
36
37    def __init__(self, valobj, params):
38        logger = lldb.formatters.Logger.Logger()
39        self.valobj = valobj
40        self.sys_params = params
41        if not(self.sys_params.types_cache.NSUInteger):
42            if self.sys_params.is_64_bit:
43                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
44                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
45                self.sys_params.types_cache.uint32 = self.valobj.GetType(
46                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
47            else:
48                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
49                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
50                self.sys_params.types_cache.uint32 = self.valobj.GetType(
51                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
52        if not(self.sys_params.types_cache.uint32):
53            self.sys_params.types_cache.uint32 = self.valobj.GetType(
54            ).GetBasicType(lldb.eBasicTypeUnsignedInt)
55        self.update()
56
57    def update(self):
58        logger = lldb.formatters.Logger.Logger()
59        self.adjust_for_architecture()
60
61    # NS(Mutable)IndexSet works in one of two modes: when having a compact block of data (e.g. a Range)
62    # the count is stored in the set itself, 3 pointers into it
63    # otherwise, it will store a pointer to an additional data structure (2 pointers into itself) and this
64    # additional structure will contain the count two pointers deep
65    # a bunch of flags allow us to detect an empty set, vs. a one-range set,
66    # vs. a multi-range set
67    def count(self):
68        logger = lldb.formatters.Logger.Logger()
69        mode_chooser_vo = self.valobj.CreateChildAtOffset(
70            "mode_chooser",
71            self.sys_params.pointer_size,
72            self.sys_params.types_cache.uint32)
73        mode_chooser = mode_chooser_vo.GetValueAsUnsigned(0)
74        if self.sys_params.is_64_bit:
75            mode_chooser = mode_chooser & 0x00000000FFFFFFFF
76        # empty set
77        if mode_chooser & 0x01 == 1:
78            return 0
79        # single range
80        if mode_chooser & 0x02 == 2:
81            mode = 1
82        # multi range
83        else:
84            mode = 2
85        if mode == 1:
86            count_vo = self.valobj.CreateChildAtOffset(
87                "count",
88                3 * self.sys_params.pointer_size,
89                self.sys_params.types_cache.NSUInteger)
90        else:
91            count_ptr = self.valobj.CreateChildAtOffset(
92                "count_ptr",
93                2 * self.sys_params.pointer_size,
94                self.sys_params.types_cache.NSUInteger)
95            count_vo = self.valobj.CreateValueFromAddress(
96                "count",
97                count_ptr.GetValueAsUnsigned() +
98                2 *
99                self.sys_params.pointer_size,
100                self.sys_params.types_cache.NSUInteger)
101        return count_vo.GetValueAsUnsigned(0)
102
103
104class NSIndexSetUnknown_SummaryProvider:
105
106    def adjust_for_architecture(self):
107        pass
108
109    def __init__(self, valobj, params):
110        logger = lldb.formatters.Logger.Logger()
111        self.valobj = valobj
112        self.sys_params = params
113        self.update()
114
115    def update(self):
116        logger = lldb.formatters.Logger.Logger()
117        self.adjust_for_architecture()
118
119    def count(self):
120        logger = lldb.formatters.Logger.Logger()
121        stream = lldb.SBStream()
122        self.valobj.GetExpressionPath(stream)
123        expr = "(int)[" + stream.GetData() + " count]"
124        num_children_vo = self.valobj.CreateValueFromExpression("count", expr)
125        if num_children_vo.IsValid():
126            return num_children_vo.GetValueAsUnsigned(0)
127        return '<variable is not NSIndexSet>'
128
129
130def GetSummary_Impl(valobj):
131    logger = lldb.formatters.Logger.Logger()
132    global statistics
133    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
134        valobj, statistics)
135    if wrapper:
136        return wrapper
137
138    name_string = class_data.class_name()
139    logger >> "class name is: " + str(name_string)
140
141    if name_string == 'NSIndexSet' or name_string == 'NSMutableIndexSet':
142        wrapper = NSIndexSetClass_SummaryProvider(
143            valobj, class_data.sys_params)
144        statistics.metric_hit('code_notrun', valobj)
145    else:
146        wrapper = NSIndexSetUnknown_SummaryProvider(
147            valobj, class_data.sys_params)
148        statistics.metric_hit(
149            'unknown_class',
150            valobj.GetName() +
151            " seen as " +
152            name_string)
153    return wrapper
154
155
156def NSIndexSet_SummaryProvider(valobj, dict):
157    logger = lldb.formatters.Logger.Logger()
158    provider = GetSummary_Impl(valobj)
159    if provider is not None:
160        if isinstance(
161                provider,
162                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
163            return provider.message()
164        try:
165            summary = provider.count()
166        except:
167            summary = None
168        logger >> "got summary " + str(summary)
169        if summary is None:
170            summary = '<variable is not NSIndexSet>'
171        if isinstance(summary, basestring):
172            return summary
173        else:
174            summary = str(summary) + (' indexes' if summary != 1 else ' index')
175        return summary
176    return 'Summary Unavailable'
177
178
179def __lldb_init_module(debugger, dict):
180    debugger.HandleCommand(
181        "type summary add -F NSIndexSet.NSIndexSet_SummaryProvider NSIndexSet NSMutableIndexSet")
182