xref: /llvm-project/lldb/examples/summaries/cocoa/NSIndexSet.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
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
16statistics = lldb.formatters.metrics.Metrics()
17statistics.add_metric("invalid_isa")
18statistics.add_metric("invalid_pointer")
19statistics.add_metric("unknown_class")
20statistics.add_metric("code_notrun")
21
22# despite the similary to synthetic children providers, these classes are not
23# trying to provide anything but the count of values for an NSIndexSet, so they need not
24# obey the interface specification for synthetic children providers
25
26
27class NSIndexSetClass_SummaryProvider:
28    def adjust_for_architecture(self):
29        pass
30
31    def __init__(self, valobj, params):
32        logger = lldb.formatters.Logger.Logger()
33        self.valobj = valobj
34        self.sys_params = params
35        if not (self.sys_params.types_cache.NSUInteger):
36            if self.sys_params.is_64_bit:
37                self.sys_params.types_cache.NSUInteger = (
38                    self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
39                )
40                self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType(
41                    lldb.eBasicTypeUnsignedInt
42                )
43            else:
44                self.sys_params.types_cache.NSUInteger = (
45                    self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedInt)
46                )
47                self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType(
48                    lldb.eBasicTypeUnsignedInt
49                )
50        if not (self.sys_params.types_cache.uint32):
51            self.sys_params.types_cache.uint32 = self.valobj.GetType().GetBasicType(
52                lldb.eBasicTypeUnsignedInt
53            )
54        self.update()
55
56    def update(self):
57        logger = lldb.formatters.Logger.Logger()
58        self.adjust_for_architecture()
59
60    # NS(Mutable)IndexSet works in one of two modes: when having a compact block of data (e.g. a Range)
61    # the count is stored in the set itself, 3 pointers into it
62    # otherwise, it will store a pointer to an additional data structure (2 pointers into itself) and this
63    # additional structure will contain the count two pointers deep
64    # a bunch of flags allow us to detect an empty set, vs. a one-range set,
65    # vs. a multi-range set
66    def count(self):
67        logger = lldb.formatters.Logger.Logger()
68        mode_chooser_vo = self.valobj.CreateChildAtOffset(
69            "mode_chooser",
70            self.sys_params.pointer_size,
71            self.sys_params.types_cache.uint32,
72        )
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            )
91        else:
92            count_ptr = self.valobj.CreateChildAtOffset(
93                "count_ptr",
94                2 * self.sys_params.pointer_size,
95                self.sys_params.types_cache.NSUInteger,
96            )
97            count_vo = self.valobj.CreateValueFromAddress(
98                "count",
99                count_ptr.GetValueAsUnsigned() + 2 * self.sys_params.pointer_size,
100                self.sys_params.types_cache.NSUInteger,
101            )
102        return count_vo.GetValueAsUnsigned(0)
103
104
105class NSIndexSetUnknown_SummaryProvider:
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    (
134        class_data,
135        wrapper,
136    ) = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
137        valobj, statistics
138    )
139    if wrapper:
140        return wrapper
141
142    name_string = class_data.class_name()
143    logger >> "class name is: " + str(name_string)
144
145    if name_string == "NSIndexSet" or name_string == "NSMutableIndexSet":
146        wrapper = NSIndexSetClass_SummaryProvider(valobj, class_data.sys_params)
147        statistics.metric_hit("code_notrun", valobj)
148    else:
149        wrapper = NSIndexSetUnknown_SummaryProvider(valobj, class_data.sys_params)
150        statistics.metric_hit(
151            "unknown_class", valobj.GetName() + " seen as " + name_string
152        )
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, lldb.runtime.objc.objc_runtime.SpecialSituation_Description
162        ):
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, str):
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    )
183