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