| //===-- FormattersContainer.h -----------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLDB_DATAFORMATTERS_FORMATTERSCONTAINER_H |
| #define LLDB_DATAFORMATTERS_FORMATTERSCONTAINER_H |
| |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <mutex> |
| #include <string> |
| |
| #include "lldb/lldb-public.h" |
| |
| #include "lldb/Core/ValueObject.h" |
| #include "lldb/DataFormatters/FormatClasses.h" |
| #include "lldb/DataFormatters/TypeFormat.h" |
| #include "lldb/DataFormatters/TypeSummary.h" |
| #include "lldb/DataFormatters/TypeSynthetic.h" |
| #include "lldb/Symbol/CompilerType.h" |
| #include "lldb/Utility/RegularExpression.h" |
| #include "lldb/Utility/StringLexer.h" |
| |
| namespace lldb_private { |
| |
| class IFormatChangeListener { |
| public: |
| virtual ~IFormatChangeListener() = default; |
| |
| virtual void Changed() = 0; |
| |
| virtual uint32_t GetCurrentRevision() = 0; |
| }; |
| |
| /// Class for matching type names. |
| class TypeMatcher { |
| RegularExpression m_type_name_regex; |
| ConstString m_type_name; |
| /// False if m_type_name_regex should be used for matching. False if this is |
| /// just matching by comparing with m_type_name string. |
| bool m_is_regex; |
| |
| // if the user tries to add formatters for, say, "struct Foo" those will not |
| // match any type because of the way we strip qualifiers from typenames this |
| // method looks for the case where the user is adding a |
| // "class","struct","enum" or "union" Foo and strips the unnecessary qualifier |
| static ConstString StripTypeName(ConstString type) { |
| if (type.IsEmpty()) |
| return type; |
| |
| std::string type_cstr(type.AsCString()); |
| StringLexer type_lexer(type_cstr); |
| |
| type_lexer.AdvanceIf("class "); |
| type_lexer.AdvanceIf("enum "); |
| type_lexer.AdvanceIf("struct "); |
| type_lexer.AdvanceIf("union "); |
| |
| while (type_lexer.NextIf({' ', '\t', '\v', '\f'}).first) |
| ; |
| |
| return ConstString(type_lexer.GetUnlexed()); |
| } |
| |
| public: |
| TypeMatcher() = delete; |
| /// Creates a matcher that accepts any type with exactly the given type name. |
| TypeMatcher(ConstString type_name) |
| : m_type_name(type_name), m_is_regex(false) {} |
| /// Creates a matcher that accepts any type matching the given regex. |
| TypeMatcher(RegularExpression regex) |
| : m_type_name_regex(std::move(regex)), m_is_regex(true) {} |
| |
| /// True iff this matches the given type name. |
| bool Matches(ConstString type_name) const { |
| if (m_is_regex) |
| return m_type_name_regex.Execute(type_name.GetStringRef()); |
| return m_type_name == type_name || |
| StripTypeName(m_type_name) == StripTypeName(type_name); |
| } |
| |
| /// Returns the underlying match string for this TypeMatcher. |
| ConstString GetMatchString() const { |
| if (m_is_regex) |
| return ConstString(m_type_name_regex.GetText()); |
| return StripTypeName(m_type_name); |
| } |
| |
| /// Returns true if this TypeMatcher and the given one were most created by |
| /// the same match string. |
| /// The main purpose of this function is to find existing TypeMatcher |
| /// instances by the user input that created them. This is necessary as LLDB |
| /// allows referencing existing TypeMatchers in commands by the user input |
| /// that originally created them: |
| /// (lldb) type summary add --summary-string \"A\" -x TypeName |
| /// (lldb) type summary delete TypeName |
| bool CreatedBySameMatchString(TypeMatcher other) const { |
| return GetMatchString() == other.GetMatchString(); |
| } |
| }; |
| |
| template <typename ValueType> class FormattersContainer { |
| public: |
| typedef typename std::shared_ptr<ValueType> ValueSP; |
| typedef std::vector<std::pair<TypeMatcher, ValueSP>> MapType; |
| typedef std::function<bool(const TypeMatcher &, const ValueSP &)> |
| ForEachCallback; |
| typedef typename std::shared_ptr<FormattersContainer<ValueType>> |
| SharedPointer; |
| |
| friend class TypeCategoryImpl; |
| |
| FormattersContainer(IFormatChangeListener *lst) : listener(lst) {} |
| |
| void Add(TypeMatcher matcher, const ValueSP &entry) { |
| if (listener) |
| entry->GetRevision() = listener->GetCurrentRevision(); |
| else |
| entry->GetRevision() = 0; |
| |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| Delete(matcher); |
| m_map.emplace_back(std::move(matcher), std::move(entry)); |
| if (listener) |
| listener->Changed(); |
| } |
| |
| bool Delete(TypeMatcher matcher) { |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| for (auto iter = m_map.begin(); iter != m_map.end(); ++iter) |
| if (iter->first.CreatedBySameMatchString(matcher)) { |
| m_map.erase(iter); |
| if (listener) |
| listener->Changed(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool Get(ConstString type, ValueSP &entry) { |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| for (auto &formatter : llvm::reverse(m_map)) { |
| if (formatter.first.Matches(type)) { |
| entry = formatter.second; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool GetExact(TypeMatcher matcher, ValueSP &entry) { |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| for (const auto &pos : m_map) |
| if (pos.first.CreatedBySameMatchString(matcher)) { |
| entry = pos.second; |
| return true; |
| } |
| return false; |
| } |
| |
| ValueSP GetAtIndex(size_t index) { |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| if (index >= m_map.size()) |
| return ValueSP(); |
| return m_map[index].second; |
| } |
| |
| lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex(size_t index) { |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| if (index >= m_map.size()) |
| return lldb::TypeNameSpecifierImplSP(); |
| TypeMatcher type_matcher = m_map[index].first; |
| return std::make_shared<TypeNameSpecifierImpl>( |
| type_matcher.GetMatchString().GetStringRef(), true); |
| } |
| |
| void Clear() { |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| m_map.clear(); |
| if (listener) |
| listener->Changed(); |
| } |
| |
| void ForEach(ForEachCallback callback) { |
| if (callback) { |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| for (const auto &pos : m_map) { |
| const TypeMatcher &type = pos.first; |
| if (!callback(type, pos.second)) |
| break; |
| } |
| } |
| } |
| |
| uint32_t GetCount() { |
| std::lock_guard<std::recursive_mutex> guard(m_map_mutex); |
| return m_map.size(); |
| } |
| |
| void AutoComplete(CompletionRequest &request) { |
| ForEach([&request](const TypeMatcher &matcher, const ValueSP &value) { |
| request.TryCompleteCurrentArg(matcher.GetMatchString().GetStringRef()); |
| return true; |
| }); |
| } |
| |
| protected: |
| FormattersContainer(const FormattersContainer &) = delete; |
| const FormattersContainer &operator=(const FormattersContainer &) = delete; |
| |
| bool Get(const FormattersMatchVector &candidates, ValueSP &entry) { |
| for (const FormattersMatchCandidate &candidate : candidates) { |
| if (Get(candidate.GetTypeName(), entry)) { |
| if (candidate.IsMatch(entry) == false) { |
| entry.reset(); |
| continue; |
| } else { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| MapType m_map; |
| std::recursive_mutex m_map_mutex; |
| IFormatChangeListener *listener; |
| }; |
| |
| } // namespace lldb_private |
| |
| #endif // LLDB_DATAFORMATTERS_FORMATTERSCONTAINER_H |