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);
196   }
197 
198   void ForEach(ForEachCallback<TypeSummaryImpl> callback) {
199     m_summary_cont.ForEach(callback.callback);
200   }
201 
202   void ForEach(ForEachCallback<TypeFilterImpl> callback) {
203     m_filter_cont.ForEach(callback.callback);
204   }
205 
206   void ForEach(ForEachCallback<SyntheticChildren> callback) {
207     m_synth_cont.ForEach(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         std::make_shared<lldb_private::TypeNameSpecifierImpl>(name, 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, summary_sp);
237   }
238 
239   void AddTypeSummary(llvm::StringRef name, lldb::FormatterMatchType match_type,
240                       lldb::TypeSummaryImplSP summary_sp) {
241     AddTypeSummary(
242         std::make_shared<lldb_private::TypeNameSpecifierImpl>(name, 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, filter_sp);
249   }
250 
251   void AddTypeFilter(llvm::StringRef name, lldb::FormatterMatchType match_type,
252                      lldb::TypeFilterImplSP filter_sp) {
253     AddTypeFilter(
254         std::make_shared<lldb_private::TypeNameSpecifierImpl>(name, 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, synth_sp);
261   }
262 
263   void AddTypeSynthetic(llvm::StringRef name,
264                         lldb::FormatterMatchType match_type,
265                         lldb::SyntheticChildrenSP synth_sp) {
266     AddTypeSynthetic(
267         std::make_shared<lldb_private::TypeNameSpecifierImpl>(name, 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(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