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
29namespace lldb_private {
30
31class IFormatChangeListener {
32public:
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.
41class 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
70public:
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
107template <typename ValueType> class FormattersContainer {
108public:
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
212protected:
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