xref: /freebsd/contrib/llvm-project/lldb/include/lldb/DataFormatters/FormattersContainer.h (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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
StripTypeName(ConstString type)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("class ");
66     type_lexer.AdvanceIf("enum ");
67     type_lexer.AdvanceIf("struct ");
68     type_lexer.AdvanceIf("union ");
69 
70     while (type_lexer.NextIf({' ', '\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.
TypeMatcher(ConstString 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.
TypeMatcher(RegularExpression 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.
TypeMatcher(lldb::TypeNameSpecifierImplSP type_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.
Matches(FormattersMatchCandidate candidate_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(m_name) == StripTypeName(type_name);
101     case lldb::eFormatterMatchRegex:
102       return m_type_name_regex.Execute(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             m_name.AsCString(),
112             std::make_shared<TypeImpl>(candidate_type.GetType()));
113     }
114     return false;
115   }
116 
GetMatchType()117   lldb::FormatterMatchType GetMatchType() const { return m_match_type; }
118 
119   /// Returns the underlying match string for this TypeMatcher.
GetMatchString()120   ConstString GetMatchString() const {
121     if (m_match_type == lldb::eFormatterMatchExact)
122         return StripTypeName(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
CreatedBySameMatchString(TypeMatcher other)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 
FormattersContainer(IFormatChangeListener * lst)152   FormattersContainer(IFormatChangeListener *lst) : listener(lst) {}
153 
Add(TypeMatcher matcher,const ValueSP & entry)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 
Delete(TypeMatcher matcher)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`.
Get(FormattersMatchCandidate candidate,ValueSP & entry)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.
Get(const FormattersMatchVector & candidates,ValueSP & entry)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 
GetExact(TypeMatcher matcher,ValueSP & entry)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 
GetAtIndex(size_t index)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 
GetTypeNameSpecifierAtIndex(size_t index)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         type_matcher.GetMatchString().GetStringRef(),
231         type_matcher.GetMatchType());
232   }
233 
Clear()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 
ForEach(ForEachCallback callback)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 
GetCount()252   uint32_t GetCount() {
253     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
254     return m_map.size();
255   }
256 
AutoComplete(CompletionRequest & request)257   void AutoComplete(CompletionRequest &request) {
258     ForEach([&request](const TypeMatcher &matcher, const ValueSP &value) {
259       request.TryCompleteCurrentArg(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