1 | //===-- FormattersContainer.h -----------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLDB_DATAFORMATTERS_FORMATTERSCONTAINER_H |
10 | #define LLDB_DATAFORMATTERS_FORMATTERSCONTAINER_H |
11 | |
12 | #include <functional> |
13 | #include <map> |
14 | #include <memory> |
15 | #include <mutex> |
16 | #include <string> |
17 | |
18 | #include "lldb/lldb-public.h" |
19 | |
20 | #include "lldb/Core/ValueObject.h" |
21 | #include "lldb/DataFormatters/FormatClasses.h" |
22 | #include "lldb/DataFormatters/TypeFormat.h" |
23 | #include "lldb/DataFormatters/TypeSummary.h" |
24 | #include "lldb/DataFormatters/TypeSynthetic.h" |
25 | #include "lldb/Symbol/CompilerType.h" |
26 | #include "lldb/Utility/RegularExpression.h" |
27 | #include "lldb/Utility/StringLexer.h" |
28 | |
29 | namespace lldb_private { |
30 | |
31 | class IFormatChangeListener { |
32 | public: |
33 | virtual ~IFormatChangeListener() = default; |
34 | |
35 | virtual void Changed() = 0; |
36 | |
37 | virtual uint32_t GetCurrentRevision() = 0; |
38 | }; |
39 | |
40 | /// Class for matching type names. |
41 | class TypeMatcher { |
42 | /// Type name for exact match, or name of the python callback if m_match_type |
43 | /// is `eFormatterMatchCallback`. |
44 | ConstString m_name; |
45 | RegularExpression m_type_name_regex; |
46 | /// Indicates what kind of matching strategy should be used: |
47 | /// - eFormatterMatchExact: match the exact type name in m_name. |
48 | /// - eFormatterMatchRegex: match using the RegularExpression object |
49 | /// `m_type_name_regex` instead. |
50 | /// - eFormatterMatchCallback: run the function in m_name to decide if a type |
51 | /// matches or not. |
52 | lldb::FormatterMatchType m_match_type; |
53 | |
54 | // if the user tries to add formatters for, say, "struct Foo" those will not |
55 | // match any type because of the way we strip qualifiers from typenames this |
56 | // method looks for the case where the user is adding a |
57 | // "class","struct","enum" or "union" Foo and strips the unnecessary qualifier |
58 | static ConstString StripTypeName(ConstString type) { |
59 | if (type.IsEmpty()) |
60 | return type; |
61 | |
62 | std::string type_cstr(type.AsCString()); |
63 | StringLexer type_lexer(type_cstr); |
64 | |
65 | type_lexer.AdvanceIf(token: "class " ); |
66 | type_lexer.AdvanceIf(token: "enum " ); |
67 | type_lexer.AdvanceIf(token: "struct " ); |
68 | type_lexer.AdvanceIf(token: "union " ); |
69 | |
70 | while (type_lexer.NextIf(cs: {' ', '\t', '\v', '\f'}).first) |
71 | ; |
72 | |
73 | return ConstString(type_lexer.GetUnlexed()); |
74 | } |
75 | |
76 | public: |
77 | TypeMatcher() = delete; |
78 | /// Creates a matcher that accepts any type with exactly the given type name. |
79 | TypeMatcher(ConstString type_name) |
80 | : m_name(type_name), m_match_type(lldb::eFormatterMatchExact) {} |
81 | /// Creates a matcher that accepts any type matching the given regex. |
82 | TypeMatcher(RegularExpression regex) |
83 | : m_type_name_regex(std::move(regex)), |
84 | m_match_type(lldb::eFormatterMatchRegex) {} |
85 | /// Creates a matcher using the matching type and string from the given type |
86 | /// name specifier. |
87 | TypeMatcher(lldb::TypeNameSpecifierImplSP type_specifier) |
88 | : m_name(type_specifier->GetName()), |
89 | m_match_type(type_specifier->GetMatchType()) { |
90 | if (m_match_type == lldb::eFormatterMatchRegex) |
91 | m_type_name_regex = RegularExpression(type_specifier->GetName()); |
92 | } |
93 | |
94 | /// True iff this matches the given type. |
95 | bool Matches(FormattersMatchCandidate candidate_type) const { |
96 | ConstString type_name = candidate_type.GetTypeName(); |
97 | switch (m_match_type) { |
98 | case lldb::eFormatterMatchExact: |
99 | return m_name == type_name || |
100 | StripTypeName(type: m_name) == StripTypeName(type: type_name); |
101 | case lldb::eFormatterMatchRegex: |
102 | return m_type_name_regex.Execute(string: type_name.GetStringRef()); |
103 | case lldb::eFormatterMatchCallback: |
104 | // CommandObjectType{Synth,Filter}Add tries to prevent the user from |
105 | // creating both a synthetic child provider and a filter for the same type |
106 | // in the same category, but we don't have a type object at that point, so |
107 | // it creates a dummy candidate without type or script interpreter. |
108 | // Skip callback matching in these cases. |
109 | if (candidate_type.GetScriptInterpreter()) |
110 | return candidate_type.GetScriptInterpreter()->FormatterCallbackFunction( |
111 | function_name: m_name.AsCString(), |
112 | type_impl_sp: std::make_shared<TypeImpl>(args: candidate_type.GetType())); |
113 | } |
114 | return false; |
115 | } |
116 | |
117 | lldb::FormatterMatchType GetMatchType() const { return m_match_type; } |
118 | |
119 | /// Returns the underlying match string for this TypeMatcher. |
120 | ConstString GetMatchString() const { |
121 | if (m_match_type == lldb::eFormatterMatchExact) |
122 | return StripTypeName(type: m_name); |
123 | if (m_match_type == lldb::eFormatterMatchRegex) |
124 | return ConstString(m_type_name_regex.GetText()); |
125 | return m_name; |
126 | } |
127 | |
128 | /// Returns true if this TypeMatcher and the given one were most created by |
129 | /// the same match string. |
130 | /// The main purpose of this function is to find existing TypeMatcher |
131 | /// instances by the user input that created them. This is necessary as LLDB |
132 | /// allows referencing existing TypeMatchers in commands by the user input |
133 | /// that originally created them: |
134 | /// (lldb) type summary add --summary-string \"A\" -x TypeName |
135 | /// (lldb) type summary delete TypeName |
136 | bool CreatedBySameMatchString(TypeMatcher other) const { |
137 | return GetMatchString() == other.GetMatchString(); |
138 | } |
139 | }; |
140 | |
141 | template <typename ValueType> class FormattersContainer { |
142 | public: |
143 | typedef typename std::shared_ptr<ValueType> ValueSP; |
144 | typedef std::vector<std::pair<TypeMatcher, ValueSP>> MapType; |
145 | typedef std::function<bool(const TypeMatcher &, const ValueSP &)> |
146 | ForEachCallback; |
147 | typedef typename std::shared_ptr<FormattersContainer<ValueType>> |
148 | SharedPointer; |
149 | |
150 | friend class TypeCategoryImpl; |
151 | |
152 | FormattersContainer(IFormatChangeListener *lst) : listener(lst) {} |
153 | |
154 | void Add(TypeMatcher matcher, const ValueSP &entry) { |
155 | if (listener) |
156 | entry->GetRevision() = listener->GetCurrentRevision(); |
157 | else |
158 | entry->GetRevision() = 0; |
159 | |
160 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
161 | Delete(matcher); |
162 | m_map.emplace_back(std::move(matcher), std::move(entry)); |
163 | if (listener) |
164 | listener->Changed(); |
165 | } |
166 | |
167 | bool Delete(TypeMatcher matcher) { |
168 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
169 | for (auto iter = m_map.begin(); iter != m_map.end(); ++iter) |
170 | if (iter->first.CreatedBySameMatchString(matcher)) { |
171 | m_map.erase(iter); |
172 | if (listener) |
173 | listener->Changed(); |
174 | return true; |
175 | } |
176 | return false; |
177 | } |
178 | |
179 | // Finds the first formatter in the container that matches `candidate`. |
180 | bool Get(FormattersMatchCandidate candidate, ValueSP &entry) { |
181 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
182 | for (auto &formatter : llvm::reverse(m_map)) { |
183 | if (formatter.first.Matches(candidate)) { |
184 | entry = formatter.second; |
185 | return true; |
186 | } |
187 | } |
188 | return false; |
189 | } |
190 | |
191 | // Finds the first match between candidate types in `candidates` and |
192 | // formatters in this container. |
193 | bool Get(const FormattersMatchVector &candidates, ValueSP &entry) { |
194 | for (const FormattersMatchCandidate &candidate : candidates) { |
195 | if (Get(candidate, entry)) { |
196 | if (candidate.IsMatch(entry) == false) { |
197 | entry.reset(); |
198 | continue; |
199 | } else { |
200 | return true; |
201 | } |
202 | } |
203 | } |
204 | return false; |
205 | } |
206 | |
207 | bool GetExact(TypeMatcher matcher, ValueSP &entry) { |
208 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
209 | for (const auto &pos : m_map) |
210 | if (pos.first.CreatedBySameMatchString(matcher)) { |
211 | entry = pos.second; |
212 | return true; |
213 | } |
214 | return false; |
215 | } |
216 | |
217 | ValueSP GetAtIndex(size_t index) { |
218 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
219 | if (index >= m_map.size()) |
220 | return ValueSP(); |
221 | return m_map[index].second; |
222 | } |
223 | |
224 | lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex(size_t index) { |
225 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
226 | if (index >= m_map.size()) |
227 | return lldb::TypeNameSpecifierImplSP(); |
228 | TypeMatcher type_matcher = m_map[index].first; |
229 | return std::make_shared<TypeNameSpecifierImpl>( |
230 | args: type_matcher.GetMatchString().GetStringRef(), |
231 | args: type_matcher.GetMatchType()); |
232 | } |
233 | |
234 | void Clear() { |
235 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
236 | m_map.clear(); |
237 | if (listener) |
238 | listener->Changed(); |
239 | } |
240 | |
241 | void ForEach(ForEachCallback callback) { |
242 | if (callback) { |
243 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
244 | for (const auto &pos : m_map) { |
245 | const TypeMatcher &type = pos.first; |
246 | if (!callback(type, pos.second)) |
247 | break; |
248 | } |
249 | } |
250 | } |
251 | |
252 | uint32_t GetCount() { |
253 | std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
254 | return m_map.size(); |
255 | } |
256 | |
257 | void AutoComplete(CompletionRequest &request) { |
258 | ForEach(callback: [&request](const TypeMatcher &matcher, const ValueSP &value) { |
259 | request.TryCompleteCurrentArg(completion: matcher.GetMatchString().GetStringRef()); |
260 | return true; |
261 | }); |
262 | } |
263 | |
264 | protected: |
265 | FormattersContainer(const FormattersContainer &) = delete; |
266 | const FormattersContainer &operator=(const FormattersContainer &) = delete; |
267 | |
268 | MapType m_map; |
269 | std::recursive_mutex m_map_mutex; |
270 | IFormatChangeListener *listener; |
271 | }; |
272 | |
273 | } // namespace lldb_private |
274 | |
275 | #endif // LLDB_DATAFORMATTERS_FORMATTERSCONTAINER_H |
276 | |