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