1 | //===-- TypeCategory.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_TYPECATEGORY_H |
10 | #define LLDB_DATAFORMATTERS_TYPECATEGORY_H |
11 | |
12 | #include <array> |
13 | #include <initializer_list> |
14 | #include <memory> |
15 | #include <mutex> |
16 | #include <string> |
17 | #include <vector> |
18 | |
19 | #include "lldb/lldb-enumerations.h" |
20 | #include "lldb/lldb-public.h" |
21 | |
22 | #include "lldb/DataFormatters/FormatClasses.h" |
23 | #include "lldb/DataFormatters/FormattersContainer.h" |
24 | |
25 | namespace lldb_private { |
26 | |
27 | // A formatter container with sub-containers for different priority tiers, that |
28 | // also exposes a flat view of all formatters in it. |
29 | // |
30 | // Formatters have different priority during matching, depending on the type of |
31 | // matching specified at registration. Exact matchers are processed first, then |
32 | // regex, and finally callback matchers. However, the scripting API presents a |
33 | // flat view of formatters in a category, with methods like `GetNumFormats()` |
34 | // and `GetFormatAtIndex(i)`. So we need something that can behave like both |
35 | // representations. |
36 | template <typename FormatterImpl> class TieredFormatterContainer { |
37 | public: |
38 | using Subcontainer = FormattersContainer<FormatterImpl>; |
39 | using SubcontainerSP = std::shared_ptr<Subcontainer>; |
40 | using ForEachCallback = typename Subcontainer::ForEachCallback; |
41 | using MapValueType = typename Subcontainer::ValueSP; |
42 | |
43 | TieredFormatterContainer(IFormatChangeListener *change_listener) { |
44 | for (auto& sc : m_subcontainers) |
45 | sc = std::make_shared<Subcontainer>(change_listener); |
46 | } |
47 | |
48 | /// Clears all subcontainers. |
49 | void Clear() { |
50 | for (auto sc : m_subcontainers) |
51 | sc->Clear(); |
52 | } |
53 | |
54 | /// Adds a formatter to the right subcontainer depending on the matching type |
55 | /// specified by `type_sp`. |
56 | void Add(lldb::TypeNameSpecifierImplSP type_sp, |
57 | std::shared_ptr<FormatterImpl> format_sp) { |
58 | m_subcontainers[type_sp->GetMatchType()]->Add(TypeMatcher(type_sp), |
59 | format_sp); |
60 | } |
61 | |
62 | /// Deletes the formatter specified by `type_sp`. |
63 | bool Delete(lldb::TypeNameSpecifierImplSP type_sp) { |
64 | return m_subcontainers[type_sp->GetMatchType()]->Delete( |
65 | TypeMatcher(type_sp)); |
66 | } |
67 | |
68 | /// Deletes all formatters registered with the string `name`, in all |
69 | /// subcontainers. |
70 | bool Delete(ConstString name) { |
71 | bool success = false; |
72 | for (auto sc : m_subcontainers) |
73 | success = sc->Delete(name) || success; |
74 | return success; |
75 | } |
76 | |
77 | /// Returns the total count of elements across all subcontainers. |
78 | uint32_t GetCount() { |
79 | uint32_t result = 0; |
80 | for (auto sc : m_subcontainers) |
81 | result += sc->GetCount(); |
82 | return result; |
83 | } |
84 | |
85 | /// Returns the formatter at `index`, simulating a flattened view of all |
86 | /// subcontainers in priority order. |
87 | MapValueType GetAtIndex(size_t index) { |
88 | for (auto sc : m_subcontainers) { |
89 | if (index < sc->GetCount()) |
90 | return sc->GetAtIndex(index); |
91 | index -= sc->GetCount(); |
92 | } |
93 | return MapValueType(); |
94 | } |
95 | |
96 | /// Looks for a matching candidate across all priority tiers, in priority |
97 | /// order. If a match is found, returns `true` and puts the matching entry in |
98 | /// `entry`. |
99 | bool Get(const FormattersMatchVector &candidates, |
100 | std::shared_ptr<FormatterImpl> &entry) { |
101 | for (auto sc : m_subcontainers) { |
102 | if (sc->Get(candidates, entry)) |
103 | return true; |
104 | } |
105 | return false; |
106 | } |
107 | |
108 | bool AnyMatches(const FormattersMatchCandidate &candidate) { |
109 | std::shared_ptr<FormatterImpl> entry; |
110 | for (auto sc : m_subcontainers) { |
111 | if (sc->Get(FormattersMatchVector{candidate}, entry)) |
112 | return true; |
113 | } |
114 | return false; |
115 | } |
116 | |
117 | /// Returns a formatter that is an exact match for `type_specifier_sp`. It |
118 | /// looks for a formatter with the same matching type that was created from |
119 | /// the same string. This is useful so we can refer to a formatter using the |
120 | /// same string used to register it. |
121 | /// |
122 | /// For example, `type_specifier_sp` can be something like |
123 | /// {"std::vector<.*>", eFormatterMatchRegex}, and we'd look for a regex |
124 | /// matcher with that exact regex string, NOT try to match that string using |
125 | /// regex. |
126 | MapValueType |
127 | GetForTypeNameSpecifier(lldb::TypeNameSpecifierImplSP type_specifier_sp) { |
128 | MapValueType retval; |
129 | if (type_specifier_sp) { |
130 | m_subcontainers[type_specifier_sp->GetMatchType()]->GetExact( |
131 | ConstString(type_specifier_sp->GetName()), retval); |
132 | } |
133 | return retval; |
134 | } |
135 | |
136 | /// Returns the type name specifier at `index`, simulating a flattened view of |
137 | /// all subcontainers in priority order. |
138 | lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex(size_t index) { |
139 | for (auto sc : m_subcontainers) { |
140 | if (index < sc->GetCount()) |
141 | return sc->GetTypeNameSpecifierAtIndex(index); |
142 | index -= sc->GetCount(); |
143 | } |
144 | return lldb::TypeNameSpecifierImplSP(); |
145 | } |
146 | |
147 | /// Iterates through tiers in order, running `callback` on each element of |
148 | /// each tier. |
149 | void ForEach(std::function<bool(const TypeMatcher &, |
150 | const std::shared_ptr<FormatterImpl> &)> |
151 | callback) { |
152 | for (auto sc : m_subcontainers) { |
153 | sc->ForEach(callback); |
154 | } |
155 | } |
156 | |
157 | void AutoComplete(CompletionRequest &request) { |
158 | for (auto sc: m_subcontainers) |
159 | sc->AutoComplete(request); |
160 | } |
161 | |
162 | private: |
163 | std::array<std::shared_ptr<Subcontainer>, lldb::eLastFormatterMatchType + 1> |
164 | m_subcontainers; |
165 | }; |
166 | |
167 | class TypeCategoryImpl { |
168 | private: |
169 | typedef TieredFormatterContainer<TypeFormatImpl> FormatContainer; |
170 | typedef TieredFormatterContainer<TypeSummaryImpl> SummaryContainer; |
171 | typedef TieredFormatterContainer<TypeFilterImpl> FilterContainer; |
172 | typedef TieredFormatterContainer<SyntheticChildren> SynthContainer; |
173 | |
174 | public: |
175 | typedef uint16_t FormatCategoryItems; |
176 | static const uint16_t ALL_ITEM_TYPES = UINT16_MAX; |
177 | |
178 | // TypeFilterImpl inherits from SyntheticChildren, so we can't simply overload |
179 | // ForEach on the type of the callback because it would result in "call to |
180 | // member function 'ForEach' is ambiguous" errors. Instead we use this |
181 | // templated struct to hold the formatter type and the callback. |
182 | template<typename T> |
183 | struct ForEachCallback { |
184 | // Make it constructible from any callable that fits. This allows us to use |
185 | // lambdas a bit more easily at the call site. For example: |
186 | // ForEachCallback<TypeFormatImpl> callback = [](...) {...}; |
187 | template <typename Callable> ForEachCallback(Callable c) : callback(c) {} |
188 | std::function<bool(const TypeMatcher &, const std::shared_ptr<T> &)> |
189 | callback; |
190 | }; |
191 | |
192 | TypeCategoryImpl(IFormatChangeListener *clist, ConstString name); |
193 | |
194 | void ForEach(ForEachCallback<TypeFormatImpl> callback) { |
195 | m_format_cont.ForEach(callback: callback.callback); |
196 | } |
197 | |
198 | void ForEach(ForEachCallback<TypeSummaryImpl> callback) { |
199 | m_summary_cont.ForEach(callback: callback.callback); |
200 | } |
201 | |
202 | void ForEach(ForEachCallback<TypeFilterImpl> callback) { |
203 | m_filter_cont.ForEach(callback: callback.callback); |
204 | } |
205 | |
206 | void ForEach(ForEachCallback<SyntheticChildren> callback) { |
207 | m_synth_cont.ForEach(callback: callback.callback); |
208 | } |
209 | |
210 | FormatContainer::MapValueType |
211 | GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp); |
212 | |
213 | SummaryContainer::MapValueType |
214 | GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp); |
215 | |
216 | FilterContainer::MapValueType |
217 | GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp); |
218 | |
219 | SynthContainer::MapValueType |
220 | GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp); |
221 | |
222 | void AddTypeFormat(lldb::TypeNameSpecifierImplSP type_sp, |
223 | lldb::TypeFormatImplSP format_sp) { |
224 | m_format_cont.Add(type_sp, format_sp); |
225 | } |
226 | |
227 | void AddTypeFormat(llvm::StringRef name, lldb::FormatterMatchType match_type, |
228 | lldb::TypeFormatImplSP format_sp) { |
229 | AddTypeFormat( |
230 | type_sp: std::make_shared<lldb_private::TypeNameSpecifierImpl>(args&: name, args&: match_type), |
231 | format_sp); |
232 | } |
233 | |
234 | void AddTypeSummary(lldb::TypeNameSpecifierImplSP type_sp, |
235 | lldb::TypeSummaryImplSP summary_sp) { |
236 | m_summary_cont.Add(type_sp, format_sp: summary_sp); |
237 | } |
238 | |
239 | void AddTypeSummary(llvm::StringRef name, lldb::FormatterMatchType match_type, |
240 | lldb::TypeSummaryImplSP summary_sp) { |
241 | AddTypeSummary( |
242 | type_sp: std::make_shared<lldb_private::TypeNameSpecifierImpl>(args&: name, args&: match_type), |
243 | summary_sp); |
244 | } |
245 | |
246 | void AddTypeFilter(lldb::TypeNameSpecifierImplSP type_sp, |
247 | lldb::TypeFilterImplSP filter_sp) { |
248 | m_filter_cont.Add(type_sp, format_sp: filter_sp); |
249 | } |
250 | |
251 | void AddTypeFilter(llvm::StringRef name, lldb::FormatterMatchType match_type, |
252 | lldb::TypeFilterImplSP filter_sp) { |
253 | AddTypeFilter( |
254 | type_sp: std::make_shared<lldb_private::TypeNameSpecifierImpl>(args&: name, args&: match_type), |
255 | filter_sp); |
256 | } |
257 | |
258 | void AddTypeSynthetic(lldb::TypeNameSpecifierImplSP type_sp, |
259 | lldb::SyntheticChildrenSP synth_sp) { |
260 | m_synth_cont.Add(type_sp, format_sp: synth_sp); |
261 | } |
262 | |
263 | void AddTypeSynthetic(llvm::StringRef name, |
264 | lldb::FormatterMatchType match_type, |
265 | lldb::SyntheticChildrenSP synth_sp) { |
266 | AddTypeSynthetic( |
267 | type_sp: std::make_shared<lldb_private::TypeNameSpecifierImpl>(args&: name, args&: match_type), |
268 | synth_sp); |
269 | } |
270 | |
271 | bool DeleteTypeFormat(lldb::TypeNameSpecifierImplSP type_sp) { |
272 | return m_format_cont.Delete(type_sp); |
273 | } |
274 | |
275 | bool DeleteTypeSummary(lldb::TypeNameSpecifierImplSP type_sp) { |
276 | return m_summary_cont.Delete(type_sp); |
277 | } |
278 | |
279 | bool DeleteTypeFilter(lldb::TypeNameSpecifierImplSP type_sp) { |
280 | return m_filter_cont.Delete(type_sp); |
281 | } |
282 | |
283 | bool DeleteTypeSynthetic(lldb::TypeNameSpecifierImplSP type_sp) { |
284 | return m_synth_cont.Delete(type_sp); |
285 | } |
286 | |
287 | uint32_t GetNumFormats() { return m_format_cont.GetCount(); } |
288 | |
289 | uint32_t GetNumSummaries() { return m_summary_cont.GetCount(); } |
290 | |
291 | uint32_t GetNumFilters() { return m_filter_cont.GetCount(); } |
292 | |
293 | uint32_t GetNumSynthetics() { return m_synth_cont.GetCount(); } |
294 | |
295 | lldb::TypeNameSpecifierImplSP |
296 | GetTypeNameSpecifierForFormatAtIndex(size_t index); |
297 | |
298 | lldb::TypeNameSpecifierImplSP |
299 | GetTypeNameSpecifierForSummaryAtIndex(size_t index); |
300 | |
301 | lldb::TypeNameSpecifierImplSP |
302 | GetTypeNameSpecifierForFilterAtIndex(size_t index); |
303 | |
304 | lldb::TypeNameSpecifierImplSP |
305 | GetTypeNameSpecifierForSyntheticAtIndex(size_t index); |
306 | |
307 | FormatContainer::MapValueType GetFormatAtIndex(size_t index); |
308 | |
309 | SummaryContainer::MapValueType GetSummaryAtIndex(size_t index); |
310 | |
311 | FilterContainer::MapValueType GetFilterAtIndex(size_t index); |
312 | |
313 | SynthContainer::MapValueType GetSyntheticAtIndex(size_t index); |
314 | |
315 | bool IsEnabled() const { return m_enabled; } |
316 | |
317 | uint32_t GetEnabledPosition() { |
318 | if (!m_enabled) |
319 | return UINT32_MAX; |
320 | else |
321 | return m_enabled_position; |
322 | } |
323 | |
324 | bool Get(lldb::LanguageType lang, const FormattersMatchVector &candidates, |
325 | lldb::TypeFormatImplSP &entry); |
326 | |
327 | bool Get(lldb::LanguageType lang, const FormattersMatchVector &candidates, |
328 | lldb::TypeSummaryImplSP &entry); |
329 | |
330 | bool Get(lldb::LanguageType lang, const FormattersMatchVector &candidates, |
331 | lldb::SyntheticChildrenSP &entry); |
332 | |
333 | void Clear(FormatCategoryItems items = ALL_ITEM_TYPES); |
334 | |
335 | bool Delete(ConstString name, FormatCategoryItems items = ALL_ITEM_TYPES); |
336 | |
337 | uint32_t GetCount(FormatCategoryItems items = ALL_ITEM_TYPES); |
338 | |
339 | const char *GetName() { return m_name.GetCString(); } |
340 | |
341 | size_t GetNumLanguages(); |
342 | |
343 | lldb::LanguageType GetLanguageAtIndex(size_t idx); |
344 | |
345 | void AddLanguage(lldb::LanguageType lang); |
346 | |
347 | std::string GetDescription(); |
348 | |
349 | bool AnyMatches(const FormattersMatchCandidate &candidate_type, |
350 | FormatCategoryItems items = ALL_ITEM_TYPES, |
351 | bool only_enabled = true, |
352 | const char **matching_category = nullptr, |
353 | FormatCategoryItems *matching_type = nullptr); |
354 | |
355 | void AutoComplete(CompletionRequest &request, FormatCategoryItems items); |
356 | |
357 | typedef std::shared_ptr<TypeCategoryImpl> SharedPointer; |
358 | |
359 | private: |
360 | FormatContainer m_format_cont; |
361 | SummaryContainer m_summary_cont; |
362 | FilterContainer m_filter_cont; |
363 | SynthContainer m_synth_cont; |
364 | |
365 | bool m_enabled; |
366 | |
367 | IFormatChangeListener *m_change_listener; |
368 | |
369 | std::recursive_mutex m_mutex; |
370 | |
371 | ConstString m_name; |
372 | |
373 | std::vector<lldb::LanguageType> m_languages; |
374 | |
375 | uint32_t m_enabled_position = 0; |
376 | |
377 | void Enable(bool value, uint32_t position); |
378 | |
379 | void Disable() { Enable(value: false, UINT32_MAX); } |
380 | |
381 | bool IsApplicable(lldb::LanguageType lang); |
382 | |
383 | uint32_t GetLastEnabledPosition() { return m_enabled_position; } |
384 | |
385 | void SetEnabledPosition(uint32_t p) { m_enabled_position = p; } |
386 | |
387 | friend class FormatManager; |
388 | friend class LanguageCategory; |
389 | friend class TypeCategoryMap; |
390 | |
391 | friend class FormattersContainer<TypeFormatImpl>; |
392 | |
393 | friend class FormattersContainer<TypeSummaryImpl>; |
394 | |
395 | friend class FormattersContainer<TypeFilterImpl>; |
396 | |
397 | friend class FormattersContainer<ScriptedSyntheticChildren>; |
398 | }; |
399 | |
400 | } // namespace lldb_private |
401 | |
402 | #endif // LLDB_DATAFORMATTERS_TYPECATEGORY_H |
403 | |