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