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 /// 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
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(token: "class ");
66 type_lexer.AdvanceIf(token: "enum ");
67 type_lexer.AdvanceIf(token: "struct ");
68 type_lexer.AdvanceIf(token: "union ");
69
70 while (type_lexer.NextIf(cs: {' ', '\t', '\v', '\f'}).first)
71 ;
72
73 return ConstString(type_lexer.GetUnlexed());
74 }
75
76public:
77 TypeMatcher() = delete;
78 /// Creates a matcher that accepts any type with exactly the given 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.
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.
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.
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(type: m_name) == StripTypeName(type: type_name);
101 case lldb::eFormatterMatchRegex:
102 return m_type_name_regex.Execute(string: 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 function_name: m_name.AsCString(),
112 type_impl_sp: std::make_shared<TypeImpl>(args: candidate_type.GetType()));
113 }
114 return false;
115 }
116
117 lldb::FormatterMatchType GetMatchType() const { return m_match_type; }
118
119 /// Returns the underlying match string for this TypeMatcher.
120 ConstString GetMatchString() const {
121 if (m_match_type == lldb::eFormatterMatchExact)
122 return StripTypeName(type: 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
136 bool CreatedBySameMatchString(TypeMatcher other) const {
137 return GetMatchString() == other.GetMatchString();
138 }
139};
140
141template <typename ValueType> class FormattersContainer {
142public:
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
152 FormattersContainer(IFormatChangeListener *lst) : listener(lst) {}
153
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
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`.
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.
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
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
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
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 args: type_matcher.GetMatchString().GetStringRef(),
231 args: type_matcher.GetMatchType());
232 }
233
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
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
252 uint32_t GetCount() {
253 std::lock_guard<std::recursive_mutex> guard(m_map_mutex);
254 return m_map.size();
255 }
256
257 void AutoComplete(CompletionRequest &request) {
258 ForEach(callback: [&request](const TypeMatcher &matcher, const ValueSP &value) {
259 request.TryCompleteCurrentArg(completion: matcher.GetMatchString().GetStringRef());
260 return true;
261 });
262 }
263
264protected:
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

source code of lldb/include/lldb/DataFormatters/FormattersContainer.h