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 CFBinaryHeap
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 length for an CFBinaryHeap, so they need not
29# obey the interface specification for synthetic children providers
30
31
32class CFBinaryHeapRef_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            else:
46                self.sys_params.types_cache.NSUInteger = self.valobj.GetType(
47                ).GetBasicType(lldb.eBasicTypeUnsignedInt)
48        self.update()
49
50    def update(self):
51        logger = lldb.formatters.Logger.Logger()
52        self.adjust_for_architecture()
53
54    # 8 bytes on i386
55    # 16 bytes on x64
56    # most probably 2 pointers
57    def offset(self):
58        logger = lldb.formatters.Logger.Logger()
59        return 2 * self.sys_params.pointer_size
60
61    def length(self):
62        logger = lldb.formatters.Logger.Logger()
63        size = self.valobj.CreateChildAtOffset(
64            "count", self.offset(), self.sys_params.types_cache.NSUInteger)
65        return size.GetValueAsUnsigned(0)
66
67
68class CFBinaryHeapUnknown_SummaryProvider:
69
70    def adjust_for_architecture(self):
71        pass
72
73    def __init__(self, valobj, params):
74        logger = lldb.formatters.Logger.Logger()
75        self.valobj = valobj
76        self.sys_params = params
77        self.update()
78
79    def update(self):
80        logger = lldb.formatters.Logger.Logger()
81        self.adjust_for_architecture()
82
83    def length(self):
84        logger = lldb.formatters.Logger.Logger()
85        stream = lldb.SBStream()
86        self.valobj.GetExpressionPath(stream)
87        num_children_vo = self.valobj.CreateValueFromExpression(
88            "count", "(int)CFBinaryHeapGetCount(" + stream.GetData() + " )")
89        if num_children_vo.IsValid():
90            return num_children_vo.GetValueAsUnsigned(0)
91        return '<variable is not CFBinaryHeap>'
92
93
94def GetSummary_Impl(valobj):
95    logger = lldb.formatters.Logger.Logger()
96    global statistics
97    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
98        valobj, statistics)
99    if wrapper:
100        return wrapper
101
102    name_string = class_data.class_name()
103    actual_name = class_data.class_name()
104
105    logger >> "name string got was " + \
106        str(name_string) + " but actual name is " + str(actual_name)
107
108    if class_data.is_cftype():
109        # CFBinaryHeap does not expose an actual NSWrapper type, so we have to check that this is
110        # an NSCFType and then check we are a pointer-to CFBinaryHeap
111        valobj_type = valobj.GetType()
112        if valobj_type.IsValid() and valobj_type.IsPointerType():
113            valobj_type = valobj_type.GetPointeeType()
114            if valobj_type.IsValid():
115                actual_name = valobj_type.GetName()
116        if actual_name == '__CFBinaryHeap':
117            wrapper = CFBinaryHeapRef_SummaryProvider(
118                valobj, class_data.sys_params)
119            statistics.metric_hit('code_notrun', valobj)
120            return wrapper
121    wrapper = CFBinaryHeapUnknown_SummaryProvider(
122        valobj, class_data.sys_params)
123    statistics.metric_hit(
124        'unknown_class',
125        valobj.GetName() +
126        " seen as " +
127        name_string)
128    return wrapper
129
130
131def CFBinaryHeap_SummaryProvider(valobj, dict):
132    logger = lldb.formatters.Logger.Logger()
133    provider = GetSummary_Impl(valobj)
134    if provider is not None:
135        if isinstance(
136                provider,
137                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
138            return provider.message()
139        try:
140            summary = provider.length()
141        except:
142            summary = None
143        logger >> "summary got from provider: " + str(summary)
144        # for some reason, one needs to clear some bits for the count
145        # to be correct when using CF(Mutable)BagRef on x64
146        # the bit mask was derived through experimentation
147        # (if counts start looking weird, then most probably
148        #  the mask needs to be changed)
149        if summary is None:
150            summary = '<variable is not CFBinaryHeap>'
151        elif isinstance(summary, basestring):
152            pass
153        else:
154            if provider.sys_params.is_64_bit:
155                summary = summary & ~0x1fff000000000000
156            if summary == 1:
157                return '@"1 item"'
158            else:
159                summary = '@"' + str(summary) + ' items"'
160        return summary
161    return 'Summary Unavailable'
162
163
164def __lldb_init_module(debugger, dict):
165    debugger.HandleCommand(
166        "type summary add -F CFBinaryHeap.CFBinaryHeap_SummaryProvider CFBinaryHeapRef")
167