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# summary provider for NSURL
9import lldb
10import ctypes
11import lldb.runtime.objc.objc_runtime
12import lldb.formatters.metrics
13import CFString
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 a summary for an NSURL, so they need not
24# obey the interface specification for synthetic children providers
25
26
27class NSURLKnown_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.NSString):
37            self.sys_params.types_cache.NSString = self.valobj.GetTarget(
38            ).FindFirstType('NSString').GetPointerType()
39        if not(self.sys_params.types_cache.NSURL):
40            self.sys_params.types_cache.NSURL = self.valobj.GetTarget(
41            ).FindFirstType('NSURL').GetPointerType()
42        self.update()
43
44    def update(self):
45        logger = lldb.formatters.Logger.Logger()
46        self.adjust_for_architecture()
47
48    # one pointer is the ISA
49    # then there is one more pointer and 8 bytes of plain data
50    # (which are also present on a 32-bit system)
51    # then there is a pointer to an NSString which is the url text
52    # optionally, the next pointer is another NSURL which is the "base"
53    # of this one when doing NSURLs composition (incidentally, NSURLs can
54    # recurse the base+text mechanism to any desired depth)
55    def offset_text(self):
56        logger = lldb.formatters.Logger.Logger()
57        return 24 if self.sys_params.is_64_bit else 16
58
59    def offset_base(self):
60        logger = lldb.formatters.Logger.Logger()
61        return self.offset_text() + self.sys_params.pointer_size
62
63    def url_text(self):
64        logger = lldb.formatters.Logger.Logger()
65        text = self.valobj.CreateChildAtOffset(
66            "text", self.offset_text(), self.sys_params.types_cache.NSString)
67        base = self.valobj.CreateChildAtOffset(
68            "base", self.offset_base(), self.sys_params.types_cache.NSURL)
69        my_string = CFString.CFString_SummaryProvider(text, None)
70        if len(my_string) > 0 and base.GetValueAsUnsigned(0) != 0:
71            # remove final " from myself
72            my_string = my_string[0:len(my_string) - 1]
73            my_string = my_string + ' -- '
74            my_base_string = NSURL_SummaryProvider(base, None)
75            if len(my_base_string) > 2:
76                # remove @" marker from base URL string
77                my_base_string = my_base_string[2:]
78            my_string = my_string + my_base_string
79        return my_string
80
81
82class NSURLUnknown_SummaryProvider:
83
84    def adjust_for_architecture(self):
85        pass
86
87    def __init__(self, valobj, params):
88        logger = lldb.formatters.Logger.Logger()
89        self.valobj = valobj
90        self.sys_params = params
91        self.update()
92
93    def update(self):
94        logger = lldb.formatters.Logger.Logger()
95        self.adjust_for_architecture()
96
97    def url_text(self):
98        logger = lldb.formatters.Logger.Logger()
99        stream = lldb.SBStream()
100        self.valobj.GetExpressionPath(stream)
101        url_text_vo = self.valobj.CreateValueFromExpression(
102            "url", "(NSString*)[" + stream.GetData() + " description]")
103        if url_text_vo.IsValid():
104            return CFString.CFString_SummaryProvider(url_text_vo, None)
105        return '<variable is not NSURL>'
106
107
108def GetSummary_Impl(valobj):
109    logger = lldb.formatters.Logger.Logger()
110    global statistics
111    class_data, wrapper = lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(
112        valobj, statistics)
113    if wrapper:
114        return wrapper
115
116    name_string = class_data.class_name()
117    logger >> "class name is: " + str(name_string)
118
119    if name_string == 'NSURL':
120        wrapper = NSURLKnown_SummaryProvider(valobj, class_data.sys_params)
121        statistics.metric_hit('code_notrun', valobj)
122    else:
123        wrapper = NSURLUnknown_SummaryProvider(valobj, class_data.sys_params)
124        statistics.metric_hit(
125            'unknown_class',
126            valobj.GetName() +
127            " seen as " +
128            name_string)
129    return wrapper
130
131
132def NSURL_SummaryProvider(valobj, dict):
133    logger = lldb.formatters.Logger.Logger()
134    provider = GetSummary_Impl(valobj)
135    if provider is not None:
136        if isinstance(
137                provider,
138                lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
139            return provider.message()
140        try:
141            summary = provider.url_text()
142        except:
143            summary = None
144        logger >> "got summary " + str(summary)
145        if summary is None or summary == '':
146            summary = '<variable is not NSURL>'
147        return summary
148    return 'Summary Unavailable'
149
150
151def __lldb_init_module(debugger, dict):
152    debugger.HandleCommand(
153        "type summary add -F NSURL.NSURL_SummaryProvider NSURL CFURLRef")
154