1061da546Spatrick"""
2061da546SpatrickLLDB AppKit formatters
3061da546Spatrick
4061da546SpatrickPart of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5061da546SpatrickSee https://llvm.org/LICENSE.txt for license information.
6061da546SpatrickSPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7061da546Spatrick"""
8061da546Spatrick# example summary provider for NS(Mutable)IndexSet
9061da546Spatrick# the real summary is now C++ code built into LLDB
10061da546Spatrickimport lldb
11061da546Spatrickimport ctypes
12061da546Spatrickimport lldb.runtime.objc.objc_runtime
13061da546Spatrickimport lldb.formatters.metrics
14061da546Spatrickimport lldb.formatters.Logger
15061da546Spatrick
16061da546Spatrickstatistics = lldb.formatters.metrics.Metrics()
17061da546Spatrickstatistics.add_metric('invalid_isa')
18061da546Spatrickstatistics.add_metric('invalid_pointer')
19061da546Spatrickstatistics.add_metric('unknown_class')
20061da546Spatrickstatistics.add_metric('code_notrun')
21061da546Spatrick
22061da546Spatrick# despite the similary to synthetic children providers, these classes are not
23061da546Spatrick# trying to provide anything but the count of values for an NSIndexSet, so they need not
24061da546Spatrick# obey the interface specification for synthetic children providers
25061da546Spatrick
26061da546Spatrick
27061da546Spatrickclass NSIndexSetClass_SummaryProvider:
28061da546Spatrick
29061da546Spatrick    def adjust_for_architecture(self):
30061da546Spatrick        pass
31061da546Spatrick
32061da546Spatrick    def __init__(self, valobj, params):
33061da546Spatrick        logger = lldb.formatters.Logger.Logger()
34061da546Spatrick        self.valobj = valobj
35061da546Spatrick        self.sys_params = params
36061da546Spatrick        if not(self.sys_params.types_cache.NSUInteger):
37061da546Spatrick            if self.sys_params.is_64_bit:
38061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
39061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedLong)
40061da546Spatrick                self.sys_params.types_cache.uint32 = self.valobj.GetType(
41061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
42061da546Spatrick            else:
43061da546Spatrick                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
44061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
45061da546Spatrick                self.sys_params.types_cache.uint32 = self.valobj.GetType(
46061da546Spatrick                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
47061da546Spatrick        if not(self.sys_params.types_cache.uint32):
48061da546Spatrick            self.sys_params.types_cache.uint32 = self.valobj.GetType(
49061da546Spatrick            ).GetBasicType(lldb.eBasicTypeUnsignedInt)
50061da546Spatrick        self.update()
51061da546Spatrick
52061da546Spatrick    def update(self):
53061da546Spatrick        logger = lldb.formatters.Logger.Logger()
54061da546Spatrick        self.adjust_for_architecture()
55061da546Spatrick
56061da546Spatrick    # NS(Mutable)IndexSet works in one of two modes: when having a compact block of data (e.g. a Range)
57061da546Spatrick    # the count is stored in the set itself, 3 pointers into it
58061da546Spatrick    # otherwise, it will store a pointer to an additional data structure (2 pointers into itself) and this
59061da546Spatrick    # additional structure will contain the count two pointers deep
60061da546Spatrick    # a bunch of flags allow us to detect an empty set, vs. a one-range set,
61061da546Spatrick    # vs. a multi-range set
62061da546Spatrick    def count(self):
63061da546Spatrick        logger = lldb.formatters.Logger.Logger()
64061da546Spatrick        mode_chooser_vo = self.valobj.CreateChildAtOffset(
65061da546Spatrick            "mode_chooser",
66061da546Spatrick            self.sys_params.pointer_size,
67061da546Spatrick            self.sys_params.types_cache.uint32)
68061da546Spatrick        mode_chooser = mode_chooser_vo.GetValueAsUnsigned(0)
69061da546Spatrick        if self.sys_params.is_64_bit:
70061da546Spatrick            mode_chooser = mode_chooser & 0x00000000FFFFFFFF
71061da546Spatrick        # empty set
72061da546Spatrick        if mode_chooser & 0x01 == 1:
73061da546Spatrick            return 0
74061da546Spatrick        # single range
75061da546Spatrick        if mode_chooser & 0x02 == 2:
76061da546Spatrick            mode = 1
77061da546Spatrick        # multi range
78061da546Spatrick        else:
79061da546Spatrick            mode = 2
80061da546Spatrick        if mode == 1:
81061da546Spatrick            count_vo = self.valobj.CreateChildAtOffset(
82061da546Spatrick                "count",
83061da546Spatrick                3 * self.sys_params.pointer_size,
84061da546Spatrick                self.sys_params.types_cache.NSUInteger)
85061da546Spatrick        else:
86061da546Spatrick            count_ptr = self.valobj.CreateChildAtOffset(
87061da546Spatrick                "count_ptr",
88061da546Spatrick                2 * self.sys_params.pointer_size,
89061da546Spatrick                self.sys_params.types_cache.NSUInteger)
90061da546Spatrick            count_vo = self.valobj.CreateValueFromAddress(
91061da546Spatrick                "count",
92061da546Spatrick                count_ptr.GetValueAsUnsigned() +
93061da546Spatrick                2 *
94061da546Spatrick                self.sys_params.pointer_size,
95061da546Spatrick                self.sys_params.types_cache.NSUInteger)
96061da546Spatrick        return count_vo.GetValueAsUnsigned(0)
97061da546Spatrick
98061da546Spatrick
99061da546Spatrickclass NSIndexSetUnknown_SummaryProvider:
100061da546Spatrick
101061da546Spatrick    def adjust_for_architecture(self):
102061da546Spatrick        pass
103061da546Spatrick
104061da546Spatrick    def __init__(self, valobj, params):
105061da546Spatrick        logger = lldb.formatters.Logger.Logger()
106061da546Spatrick        self.valobj = valobj
107061da546Spatrick        self.sys_params = params
108061da546Spatrick        self.update()
109061da546Spatrick
110061da546Spatrick    def update(self):
111061da546Spatrick        logger = lldb.formatters.Logger.Logger()
112061da546Spatrick        self.adjust_for_architecture()
113061da546Spatrick
114061da546Spatrick    def count(self):
115061da546Spatrick        logger = lldb.formatters.Logger.Logger()
116061da546Spatrick        stream = lldb.SBStream()
117061da546Spatrick        self.valobj.GetExpressionPath(stream)
118061da546Spatrick        expr = "(int)[" + stream.GetData() + " count]"
119061da546Spatrick        num_children_vo = self.valobj.CreateValueFromExpression("count", expr)
120061da546Spatrick        if num_children_vo.IsValid():
121061da546Spatrick            return num_children_vo.GetValueAsUnsigned(0)
122061da546Spatrick        return '<variable is not NSIndexSet>'
123061da546Spatrick
124061da546Spatrick
125061da546Spatrickdef GetSummary_Impl(valobj):
126061da546Spatrick    logger = lldb.formatters.Logger.Logger()
127061da546Spatrick    global statistics
128061da546Spatrick    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
129061da546Spatrick        valobj, statistics)
130061da546Spatrick    if wrapper:
131061da546Spatrick        return wrapper
132061da546Spatrick
133061da546Spatrick    name_string = class_data.class_name()
134061da546Spatrick    logger >> "class name is: " + str(name_string)
135061da546Spatrick
136061da546Spatrick    if name_string == 'NSIndexSet' or name_string == 'NSMutableIndexSet':
137061da546Spatrick        wrapper = NSIndexSetClass_SummaryProvider(
138061da546Spatrick            valobj, class_data.sys_params)
139061da546Spatrick        statistics.metric_hit('code_notrun', valobj)
140061da546Spatrick    else:
141061da546Spatrick        wrapper = NSIndexSetUnknown_SummaryProvider(
142061da546Spatrick            valobj, class_data.sys_params)
143061da546Spatrick        statistics.metric_hit(
144061da546Spatrick            'unknown_class',
145061da546Spatrick            valobj.GetName() +
146061da546Spatrick            " seen as " +
147061da546Spatrick            name_string)
148061da546Spatrick    return wrapper
149061da546Spatrick
150061da546Spatrick
151061da546Spatrickdef NSIndexSet_SummaryProvider(valobj, dict):
152061da546Spatrick    logger = lldb.formatters.Logger.Logger()
153061da546Spatrick    provider = GetSummary_Impl(valobj)
154061da546Spatrick    if provider is not None:
155061da546Spatrick        if isinstance(
156061da546Spatrick                provider,
157061da546Spatrick                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
158061da546Spatrick            return provider.message()
159061da546Spatrick        try:
160061da546Spatrick            summary = provider.count()
161061da546Spatrick        except:
162061da546Spatrick            summary = None
163061da546Spatrick        logger >> "got summary " + str(summary)
164061da546Spatrick        if summary is None:
165061da546Spatrick            summary = '<variable is not NSIndexSet>'
166*f6aab3d8Srobert        if isinstance(summary, str):
167061da546Spatrick            return summary
168061da546Spatrick        else:
169061da546Spatrick            summary = str(summary) + (' indexes' if summary != 1 else ' index')
170061da546Spatrick        return summary
171061da546Spatrick    return 'Summary Unavailable'
172061da546Spatrick
173061da546Spatrick
174061da546Spatrickdef __lldb_init_module(debugger, dict):
175061da546Spatrick    debugger.HandleCommand(
176061da546Spatrick        "type summary add -F NSIndexSet.NSIndexSet_SummaryProvider NSIndexSet NSMutableIndexSet")
177