1 //===-- TypeCategory.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_TYPECATEGORY_H 10 #define LLDB_DATAFORMATTERS_TYPECATEGORY_H 11 12 #include <array> 13 #include <initializer_list> 14 #include <memory> 15 #include <mutex> 16 #include <string> 17 #include <vector> 18 19 #include "lldb/lldb-enumerations.h" 20 #include "lldb/lldb-public.h" 21 22 #include "lldb/DataFormatters/FormatClasses.h" 23 #include "lldb/DataFormatters/FormattersContainer.h" 24 25 namespace lldb_private { 26 27 // A formatter container with sub-containers for different priority tiers, that 28 // also exposes a flat view of all formatters in it. 29 // 30 // Formatters have different priority during matching, depending on the type of 31 // matching specified at registration. Exact matchers are processed first, then 32 // regex, and finally callback matchers. However, the scripting API presents a 33 // flat view of formatters in a category, with methods like `GetNumFormats()` 34 // and `GetFormatAtIndex(i)`. So we need something that can behave like both 35 // representations. 36 template <typename FormatterImpl> class TieredFormatterContainer { 37 public: 38 using Subcontainer = FormattersContainer<FormatterImpl>; 39 using SubcontainerSP = std::shared_ptr<Subcontainer>; 40 using ForEachCallback = typename Subcontainer::ForEachCallback; 41 using MapValueType = typename Subcontainer::ValueSP; 42 TieredFormatterContainer(IFormatChangeListener * change_listener)43 TieredFormatterContainer(IFormatChangeListener *change_listener) { 44 for (auto& sc : m_subcontainers) 45 sc = std::make_shared<Subcontainer>(change_listener); 46 } 47 48 /// Clears all subcontainers. Clear()49 void Clear() { 50 for (auto sc : m_subcontainers) 51 sc->Clear(); 52 } 53 54 /// Adds a formatter to the right subcontainer depending on the matching type 55 /// specified by `type_sp`. Add(lldb::TypeNameSpecifierImplSP type_sp,std::shared_ptr<FormatterImpl> format_sp)56 void Add(lldb::TypeNameSpecifierImplSP type_sp, 57 std::shared_ptr<FormatterImpl> format_sp) { 58 m_subcontainers[type_sp->GetMatchType()]->Add(TypeMatcher(type_sp), 59 format_sp); 60 } 61 62 /// Deletes the formatter specified by `type_sp`. Delete(lldb::TypeNameSpecifierImplSP type_sp)63 bool Delete(lldb::TypeNameSpecifierImplSP type_sp) { 64 return m_subcontainers[type_sp->GetMatchType()]->Delete( 65 TypeMatcher(type_sp)); 66 } 67 68 /// Deletes all formatters registered with the string `name`, in all 69 /// subcontainers. Delete(ConstString name)70 bool Delete(ConstString name) { 71 bool success = false; 72 for (auto sc : m_subcontainers) 73 success = sc->Delete(name) || success; 74 return success; 75 } 76 77 /// Returns the total count of elements across all subcontainers. GetCount()78 uint32_t GetCount() { 79 uint32_t result = 0; 80 for (auto sc : m_subcontainers) 81 result += sc->GetCount(); 82 return result; 83 } 84 85 /// Returns the formatter at `index`, simulating a flattened view of all 86 /// subcontainers in priority order. GetAtIndex(size_t index)87 MapValueType GetAtIndex(size_t index) { 88 for (auto sc : m_subcontainers) { 89 if (index < sc->GetCount()) 90 return sc->GetAtIndex(index); 91 index -= sc->GetCount(); 92 } 93 return MapValueType(); 94 } 95 96 /// Looks for a matching candidate across all priority tiers, in priority 97 /// order. If a match is found, returns `true` and puts the matching entry in 98 /// `entry`. Get(const FormattersMatchVector & candidates,std::shared_ptr<FormatterImpl> & entry)99 bool Get(const FormattersMatchVector &candidates, 100 std::shared_ptr<FormatterImpl> &entry) { 101 for (auto sc : m_subcontainers) { 102 if (sc->Get(candidates, entry)) 103 return true; 104 } 105 return false; 106 } 107 AnyMatches(const FormattersMatchCandidate & candidate)108 bool AnyMatches(const FormattersMatchCandidate &candidate) { 109 std::shared_ptr<FormatterImpl> entry; 110 for (auto sc : m_subcontainers) { 111 if (sc->Get(FormattersMatchVector{candidate}, entry)) 112 return true; 113 } 114 return false; 115 } 116 117 /// Returns a formatter that is an exact match for `type_specifier_sp`. It 118 /// looks for a formatter with the same matching type that was created from 119 /// the same string. This is useful so we can refer to a formatter using the 120 /// same string used to register it. 121 /// 122 /// For example, `type_specifier_sp` can be something like 123 /// {"std::vector<.*>", eFormatterMatchRegex}, and we'd look for a regex 124 /// matcher with that exact regex string, NOT try to match that string using 125 /// regex. 126 MapValueType GetForTypeNameSpecifier(lldb::TypeNameSpecifierImplSP type_specifier_sp)127 GetForTypeNameSpecifier(lldb::TypeNameSpecifierImplSP type_specifier_sp) { 128 MapValueType retval; 129 if (type_specifier_sp) { 130 m_subcontainers[type_specifier_sp->GetMatchType()]->GetExact( 131 ConstString(type_specifier_sp->GetName()), retval); 132 } 133 return retval; 134 } 135 136 /// Returns the type name specifier at `index`, simulating a flattened view of 137 /// all subcontainers in priority order. GetTypeNameSpecifierAtIndex(size_t index)138 lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex(size_t index) { 139 for (auto sc : m_subcontainers) { 140 if (index < sc->GetCount()) 141 return sc->GetTypeNameSpecifierAtIndex(index); 142 index -= sc->GetCount(); 143 } 144 return lldb::TypeNameSpecifierImplSP(); 145 } 146 147 /// Iterates through tiers in order, running `callback` on each element of 148 /// each tier. ForEach(std::function<bool (const TypeMatcher &,const std::shared_ptr<FormatterImpl> &)> callback)149 void ForEach(std::function<bool(const TypeMatcher &, 150 const std::shared_ptr<FormatterImpl> &)> 151 callback) { 152 for (auto sc : m_subcontainers) { 153 sc->ForEach(callback); 154 } 155 } 156 AutoComplete(CompletionRequest & request)157 void AutoComplete(CompletionRequest &request) { 158 for (auto sc: m_subcontainers) 159 sc->AutoComplete(request); 160 } 161 162 private: 163 std::array<std::shared_ptr<Subcontainer>, lldb::eLastFormatterMatchType + 1> 164 m_subcontainers; 165 }; 166 167 class TypeCategoryImpl { 168 private: 169 typedef TieredFormatterContainer<TypeFormatImpl> FormatContainer; 170 typedef TieredFormatterContainer<TypeSummaryImpl> SummaryContainer; 171 typedef TieredFormatterContainer<TypeFilterImpl> FilterContainer; 172 typedef TieredFormatterContainer<SyntheticChildren> SynthContainer; 173 174 public: 175 typedef uint16_t FormatCategoryItems; 176 static const uint16_t ALL_ITEM_TYPES = UINT16_MAX; 177 178 // TypeFilterImpl inherits from SyntheticChildren, so we can't simply overload 179 // ForEach on the type of the callback because it would result in "call to 180 // member function 'ForEach' is ambiguous" errors. Instead we use this 181 // templated struct to hold the formatter type and the callback. 182 template<typename T> 183 struct ForEachCallback { 184 // Make it constructible from any callable that fits. This allows us to use 185 // lambdas a bit more easily at the call site. For example: 186 // ForEachCallback<TypeFormatImpl> callback = [](...) {...}; ForEachCallbackForEachCallback187 template <typename Callable> ForEachCallback(Callable c) : callback(c) {} 188 std::function<bool(const TypeMatcher &, const std::shared_ptr<T> &)> 189 callback; 190 }; 191 192 TypeCategoryImpl(IFormatChangeListener *clist, ConstString name); 193 ForEach(ForEachCallback<TypeFormatImpl> callback)194 void ForEach(ForEachCallback<TypeFormatImpl> callback) { 195 m_format_cont.ForEach(callback.callback); 196 } 197 ForEach(ForEachCallback<TypeSummaryImpl> callback)198 void ForEach(ForEachCallback<TypeSummaryImpl> callback) { 199 m_summary_cont.ForEach(callback.callback); 200 } 201 ForEach(ForEachCallback<TypeFilterImpl> callback)202 void ForEach(ForEachCallback<TypeFilterImpl> callback) { 203 m_filter_cont.ForEach(callback.callback); 204 } 205 ForEach(ForEachCallback<SyntheticChildren> callback)206 void ForEach(ForEachCallback<SyntheticChildren> callback) { 207 m_synth_cont.ForEach(callback.callback); 208 } 209 210 FormatContainer::MapValueType 211 GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp); 212 213 SummaryContainer::MapValueType 214 GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp); 215 216 FilterContainer::MapValueType 217 GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp); 218 219 SynthContainer::MapValueType 220 GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp); 221 AddTypeFormat(lldb::TypeNameSpecifierImplSP type_sp,lldb::TypeFormatImplSP format_sp)222 void AddTypeFormat(lldb::TypeNameSpecifierImplSP type_sp, 223 lldb::TypeFormatImplSP format_sp) { 224 m_format_cont.Add(type_sp, format_sp); 225 } 226 AddTypeFormat(llvm::StringRef name,lldb::FormatterMatchType match_type,lldb::TypeFormatImplSP format_sp)227 void AddTypeFormat(llvm::StringRef name, lldb::FormatterMatchType match_type, 228 lldb::TypeFormatImplSP format_sp) { 229 AddTypeFormat( 230 std::make_shared<lldb_private::TypeNameSpecifierImpl>(name, match_type), 231 format_sp); 232 } 233 AddTypeSummary(lldb::TypeNameSpecifierImplSP type_sp,lldb::TypeSummaryImplSP summary_sp)234 void AddTypeSummary(lldb::TypeNameSpecifierImplSP type_sp, 235 lldb::TypeSummaryImplSP summary_sp) { 236 m_summary_cont.Add(type_sp, summary_sp); 237 } 238 AddTypeSummary(llvm::StringRef name,lldb::FormatterMatchType match_type,lldb::TypeSummaryImplSP summary_sp)239 void AddTypeSummary(llvm::StringRef name, lldb::FormatterMatchType match_type, 240 lldb::TypeSummaryImplSP summary_sp) { 241 AddTypeSummary( 242 std::make_shared<lldb_private::TypeNameSpecifierImpl>(name, match_type), 243 summary_sp); 244 } 245 AddTypeFilter(lldb::TypeNameSpecifierImplSP type_sp,lldb::TypeFilterImplSP filter_sp)246 void AddTypeFilter(lldb::TypeNameSpecifierImplSP type_sp, 247 lldb::TypeFilterImplSP filter_sp) { 248 m_filter_cont.Add(type_sp, filter_sp); 249 } 250 AddTypeFilter(llvm::StringRef name,lldb::FormatterMatchType match_type,lldb::TypeFilterImplSP filter_sp)251 void AddTypeFilter(llvm::StringRef name, lldb::FormatterMatchType match_type, 252 lldb::TypeFilterImplSP filter_sp) { 253 AddTypeFilter( 254 std::make_shared<lldb_private::TypeNameSpecifierImpl>(name, match_type), 255 filter_sp); 256 } 257 AddTypeSynthetic(lldb::TypeNameSpecifierImplSP type_sp,lldb::SyntheticChildrenSP synth_sp)258 void AddTypeSynthetic(lldb::TypeNameSpecifierImplSP type_sp, 259 lldb::SyntheticChildrenSP synth_sp) { 260 m_synth_cont.Add(type_sp, synth_sp); 261 } 262 AddTypeSynthetic(llvm::StringRef name,lldb::FormatterMatchType match_type,lldb::SyntheticChildrenSP synth_sp)263 void AddTypeSynthetic(llvm::StringRef name, 264 lldb::FormatterMatchType match_type, 265 lldb::SyntheticChildrenSP synth_sp) { 266 AddTypeSynthetic( 267 std::make_shared<lldb_private::TypeNameSpecifierImpl>(name, match_type), 268 synth_sp); 269 } 270 DeleteTypeFormat(lldb::TypeNameSpecifierImplSP type_sp)271 bool DeleteTypeFormat(lldb::TypeNameSpecifierImplSP type_sp) { 272 return m_format_cont.Delete(type_sp); 273 } 274 DeleteTypeSummary(lldb::TypeNameSpecifierImplSP type_sp)275 bool DeleteTypeSummary(lldb::TypeNameSpecifierImplSP type_sp) { 276 return m_summary_cont.Delete(type_sp); 277 } 278 DeleteTypeFilter(lldb::TypeNameSpecifierImplSP type_sp)279 bool DeleteTypeFilter(lldb::TypeNameSpecifierImplSP type_sp) { 280 return m_filter_cont.Delete(type_sp); 281 } 282 DeleteTypeSynthetic(lldb::TypeNameSpecifierImplSP type_sp)283 bool DeleteTypeSynthetic(lldb::TypeNameSpecifierImplSP type_sp) { 284 return m_synth_cont.Delete(type_sp); 285 } 286 GetNumFormats()287 uint32_t GetNumFormats() { return m_format_cont.GetCount(); } 288 GetNumSummaries()289 uint32_t GetNumSummaries() { return m_summary_cont.GetCount(); } 290 GetNumFilters()291 uint32_t GetNumFilters() { return m_filter_cont.GetCount(); } 292 GetNumSynthetics()293 uint32_t GetNumSynthetics() { return m_synth_cont.GetCount(); } 294 295 lldb::TypeNameSpecifierImplSP 296 GetTypeNameSpecifierForFormatAtIndex(size_t index); 297 298 lldb::TypeNameSpecifierImplSP 299 GetTypeNameSpecifierForSummaryAtIndex(size_t index); 300 301 lldb::TypeNameSpecifierImplSP 302 GetTypeNameSpecifierForFilterAtIndex(size_t index); 303 304 lldb::TypeNameSpecifierImplSP 305 GetTypeNameSpecifierForSyntheticAtIndex(size_t index); 306 307 FormatContainer::MapValueType GetFormatAtIndex(size_t index); 308 309 SummaryContainer::MapValueType GetSummaryAtIndex(size_t index); 310 311 FilterContainer::MapValueType GetFilterAtIndex(size_t index); 312 313 SynthContainer::MapValueType GetSyntheticAtIndex(size_t index); 314 IsEnabled()315 bool IsEnabled() const { return m_enabled; } 316 GetEnabledPosition()317 uint32_t GetEnabledPosition() { 318 if (!m_enabled) 319 return UINT32_MAX; 320 else 321 return m_enabled_position; 322 } 323 324 bool Get(lldb::LanguageType lang, const FormattersMatchVector &candidates, 325 lldb::TypeFormatImplSP &entry); 326 327 bool Get(lldb::LanguageType lang, const FormattersMatchVector &candidates, 328 lldb::TypeSummaryImplSP &entry); 329 330 bool Get(lldb::LanguageType lang, const FormattersMatchVector &candidates, 331 lldb::SyntheticChildrenSP &entry); 332 333 void Clear(FormatCategoryItems items = ALL_ITEM_TYPES); 334 335 bool Delete(ConstString name, FormatCategoryItems items = ALL_ITEM_TYPES); 336 337 uint32_t GetCount(FormatCategoryItems items = ALL_ITEM_TYPES); 338 GetName()339 const char *GetName() { return m_name.GetCString(); } 340 341 size_t GetNumLanguages(); 342 343 lldb::LanguageType GetLanguageAtIndex(size_t idx); 344 345 void AddLanguage(lldb::LanguageType lang); 346 347 std::string GetDescription(); 348 349 bool AnyMatches(const FormattersMatchCandidate &candidate_type, 350 FormatCategoryItems items = ALL_ITEM_TYPES, 351 bool only_enabled = true, 352 const char **matching_category = nullptr, 353 FormatCategoryItems *matching_type = nullptr); 354 355 void AutoComplete(CompletionRequest &request, FormatCategoryItems items); 356 357 typedef std::shared_ptr<TypeCategoryImpl> SharedPointer; 358 359 private: 360 FormatContainer m_format_cont; 361 SummaryContainer m_summary_cont; 362 FilterContainer m_filter_cont; 363 SynthContainer m_synth_cont; 364 365 bool m_enabled; 366 367 IFormatChangeListener *m_change_listener; 368 369 std::recursive_mutex m_mutex; 370 371 ConstString m_name; 372 373 std::vector<lldb::LanguageType> m_languages; 374 375 uint32_t m_enabled_position = 0; 376 377 void Enable(bool value, uint32_t position); 378 Disable()379 void Disable() { Enable(false, UINT32_MAX); } 380 381 bool IsApplicable(lldb::LanguageType lang); 382 GetLastEnabledPosition()383 uint32_t GetLastEnabledPosition() { return m_enabled_position; } 384 SetEnabledPosition(uint32_t p)385 void SetEnabledPosition(uint32_t p) { m_enabled_position = p; } 386 387 friend class FormatManager; 388 friend class LanguageCategory; 389 friend class TypeCategoryMap; 390 391 friend class FormattersContainer<TypeFormatImpl>; 392 393 friend class FormattersContainer<TypeSummaryImpl>; 394 395 friend class FormattersContainer<TypeFilterImpl>; 396 397 friend class FormattersContainer<ScriptedSyntheticChildren>; 398 }; 399 400 } // namespace lldb_private 401 402 #endif // LLDB_DATAFORMATTERS_TYPECATEGORY_H 403