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   RegularExpression m_type_name_regex;
43   ConstString m_type_name;
44   /// False if m_type_name_regex should be used for matching. False if this is
45   /// just matching by comparing with m_type_name string.
46   bool m_is_regex;
47 
48   // if the user tries to add formatters for, say, "struct Foo" those will not
49   // match any type because of the way we strip qualifiers from typenames this
50   // method looks for the case where the user is adding a
51   // "class","struct","enum" or "union" Foo and strips the unnecessary qualifier
52   static ConstString StripTypeName(ConstString type) {
53     if (type.IsEmpty())
54       return type;
55 
56     std::string type_cstr(type.AsCString());
57     StringLexer type_lexer(type_cstr);
58 
59     type_lexer.AdvanceIf("class ");
60     type_lexer.AdvanceIf("enum ");
61     type_lexer.AdvanceIf("struct ");
62     type_lexer.AdvanceIf("union ");
63 
64     while (type_lexer.NextIf({' ', '\t', '\v', '\f'}).first)
65       ;
66 
67     return ConstString(type_lexer.GetUnlexed());
68   }
69 
70 public:
71   TypeMatcher() = delete;
72   /// Creates a matcher that accepts any type with exactly the given type name.
73   TypeMatcher(ConstString type_name)
74       : m_type_name(type_name), m_is_regex(false) {}
75   /// Creates a matcher that accepts any type matching the given regex.
76   TypeMatcher(RegularExpression regex)
77       : m_type_name_regex(std::move(regex)), m_is_regex(true) {}
78 
79   /// True iff this matches the given type name.
80   bool Matches(ConstString type_name) const {
81     if (m_is_regex)
82       return m_type_name_regex.Execute(type_name.GetStringRef());
83     return m_type_name == type_name ||
84            StripTypeName(m_type_name) == StripTypeName(type_name);
85   }
86 
87   /// Returns the underlying match string for this TypeMatcher.
88   ConstString GetMatchString() const {
89     if (m_is_regex)
90       return ConstString(m_type_name_regex.GetText());
91     return StripTypeName(m_type_name);
92   }
93 
94   /// Returns true if this TypeMatcher and the given one were most created by
95   /// the same match string.
96   /// The main purpose of this function is to find existing TypeMatcher
97   /// instances by the user input that created them. This is necessary as LLDB
98   /// allows referencing existing TypeMatchers in commands by the user input
99   /// that originally created them:
100   /// (lldb) type summary add --summary-string \"A\" -x TypeName
101   /// (lldb) type summary delete TypeName
102   bool CreatedBySameMatchString(TypeMatcher other) const {
103     return GetMatchString() == other.GetMatchString();
104   }
105 };
106 
107 template <typename ValueType> class FormattersContainer {
108 public:
109   typedef typename std::shared_ptr<ValueType> ValueSP;
110   typedef std::vector<std::pair<TypeMatcher, ValueSP>> MapType;
111   typedef std::function<bool(const TypeMatcher &, const ValueSP &)>
112       ForEachCallback;
113   typedef typename std::shared_ptr<FormattersContainer<ValueType>>
114       SharedPointer;
115 
116   friend class TypeCategoryImpl;
117 
118   FormattersContainer(IFormatChangeListener *lst) : listener(lst) {}
119 
120   void Add(TypeMatcher matcher, const ValueSP &entry) {
121     if (listener)
122       entry->GetRevision() = listener->GetCurrentRevision();
123     else
124       entry->GetRevision() = 0;
125 
126     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
127     Delete(matcher);
128     m_map.emplace_back(std::move(matcher), std::move(entry));
129     if (listener)
130       listener->Changed();
131   }
132 
133   bool Delete(TypeMatcher matcher) {
134     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
135     for (auto iter = m_map.begin(); iter != m_map.end(); ++iter)
136       if (iter->first.CreatedBySameMatchString(matcher)) {
137         m_map.erase(iter);
138         if (listener)
139           listener->Changed();
140         return true;
141       }
142     return false;
143   }
144 
145   bool Get(ConstString type, ValueSP &entry) {
146     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
147     for (auto &formatter : llvm::reverse(m_map)) {
148       if (formatter.first.Matches(type)) {
149         entry = formatter.second;
150         return true;
151       }
152     }
153     return false;
154   }
155 
156   bool GetExact(TypeMatcher matcher, ValueSP &entry) {
157     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
158     for (const auto &pos : m_map)
159       if (pos.first.CreatedBySameMatchString(matcher)) {
160         entry = pos.second;
161         return true;
162       }
163     return false;
164   }
165 
166   ValueSP GetAtIndex(size_t index) {
167     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
168     if (index >= m_map.size())
169       return ValueSP();
170     return m_map[index].second;
171   }
172 
173   lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex(size_t index) {
174     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
175     if (index >= m_map.size())
176       return lldb::TypeNameSpecifierImplSP();
177     TypeMatcher type_matcher = m_map[index].first;
178     return std::make_shared<TypeNameSpecifierImpl>(
179         type_matcher.GetMatchString().GetStringRef(), true);
180   }
181 
182   void Clear() {
183     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
184     m_map.clear();
185     if (listener)
186       listener->Changed();
187   }
188 
189   void ForEach(ForEachCallback callback) {
190     if (callback) {
191       std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
192       for (const auto &pos : m_map) {
193         const TypeMatcher &type = pos.first;
194         if (!callback(type, pos.second))
195           break;
196       }
197     }
198   }
199 
200   uint32_t GetCount() {
201     std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
202     return m_map.size();
203   }
204 
205   void AutoComplete(CompletionRequest &request) {
206     ForEach([&request](const TypeMatcher &matcher, const ValueSP &value) {
207       request.TryCompleteCurrentArg(matcher.GetMatchString().GetStringRef());
208       return true;
209     });
210   }
211 
212 protected:
213   FormattersContainer(const FormattersContainer &) = delete;
214   const FormattersContainer &operator=(const FormattersContainer &) = delete;
215 
216   bool Get(const FormattersMatchVector &candidates, ValueSP &entry) {
217     for (const FormattersMatchCandidate &candidate : candidates) {
218       if (Get(candidate.GetTypeName(), entry)) {
219         if (candidate.IsMatch(entry) == false) {
220           entry.reset();
221           continue;
222         } else {
223           return true;
224         }
225       }
226     }
227     return false;
228   }
229 
230   MapType m_map;
231   std::recursive_mutex m_map_mutex;
232   IFormatChangeListener *listener;
233 };
234 
235 } // namespace lldb_private
236 
237 #endif // LLDB_DATAFORMATTERS_FORMATTERSCONTAINER_H
238