//===-- 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 #include #include #include #include #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; }; // 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 inline ConstString GetValidTypeName_Impl(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()); } template class FormattersContainer; template class FormatMap { public: typedef typename ValueType::SharedPointer ValueSP; typedef std::vector> MapType; typedef typename MapType::iterator MapIterator; typedef std::function ForEachCallback; FormatMap(IFormatChangeListener *lst) : m_map(), m_map_mutex(), listener(lst) {} void Add(KeyType name, const ValueSP &entry) { if (listener) entry->GetRevision() = listener->GetCurrentRevision(); else entry->GetRevision() = 0; std::lock_guard guard(m_map_mutex); Delete(name); m_map.emplace_back(std::move(name), std::move(entry)); if (listener) listener->Changed(); } bool Delete(const KeyType &name) { std::lock_guard guard(m_map_mutex); for (MapIterator iter = m_map.begin(); iter != m_map.end(); ++iter) if (iter->first == name) { m_map.erase(iter); if (listener) listener->Changed(); return true; } return false; } void Clear() { std::lock_guard guard(m_map_mutex); m_map.clear(); if (listener) listener->Changed(); } bool Get(const KeyType &name, ValueSP &entry) { std::lock_guard guard(m_map_mutex); for (const auto &pos : m_map) if (pos.first == name) { entry = pos.second; return true; } return false; } void ForEach(ForEachCallback callback) { if (callback) { std::lock_guard guard(m_map_mutex); for (const auto &pos : m_map) { const KeyType &type = pos.first; if (!callback(type, pos.second)) break; } } } uint32_t GetCount() { return m_map.size(); } ValueSP GetValueAtIndex(size_t index) { std::lock_guard guard(m_map_mutex); if (index >= m_map.size()) return ValueSP(); return m_map[index].second; } // If caller holds the mutex we could return a reference without copy ctor. KeyType GetKeyAtIndex(size_t index) { std::lock_guard guard(m_map_mutex); if (index >= m_map.size()) return {}; return m_map[index].first; } protected: MapType m_map; std::recursive_mutex m_map_mutex; IFormatChangeListener *listener; MapType &map() { return m_map; } std::recursive_mutex &mutex() { return m_map_mutex; } friend class FormattersContainer; friend class FormatManager; }; template class FormattersContainer { protected: typedef FormatMap BackEndType; public: typedef typename BackEndType::MapType MapType; typedef typename MapType::iterator MapIterator; typedef KeyType MapKeyType; typedef std::shared_ptr MapValueType; typedef typename BackEndType::ForEachCallback ForEachCallback; typedef typename std::shared_ptr> SharedPointer; friend class TypeCategoryImpl; FormattersContainer(std::string name, IFormatChangeListener *lst) : m_format_map(lst), m_name(name) {} void Add(MapKeyType type, const MapValueType &entry) { Add_Impl(std::move(type), entry, static_cast(nullptr)); } bool Delete(ConstString type) { return Delete_Impl(type, static_cast(nullptr)); } bool Get(ValueObject &valobj, MapValueType &entry, lldb::DynamicValueType use_dynamic) { CompilerType ast_type(valobj.GetCompilerType()); bool ret = Get(valobj, ast_type, entry, use_dynamic); if (ret) entry = MapValueType(entry); else entry = MapValueType(); return ret; } bool Get(ConstString type, MapValueType &entry) { return Get_Impl(type, entry, static_cast(nullptr)); } bool GetExact(ConstString type, MapValueType &entry) { return GetExact_Impl(type, entry, static_cast(nullptr)); } MapValueType GetAtIndex(size_t index) { return m_format_map.GetValueAtIndex(index); } lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex(size_t index) { return GetTypeNameSpecifierAtIndex_Impl(index, static_cast(nullptr)); } void Clear() { m_format_map.Clear(); } void ForEach(ForEachCallback callback) { m_format_map.ForEach(callback); } uint32_t GetCount() { return m_format_map.GetCount(); } protected: BackEndType m_format_map; std::string m_name; FormattersContainer(const FormattersContainer &) = delete; const FormattersContainer &operator=(const FormattersContainer &) = delete; void Add_Impl(MapKeyType type, const MapValueType &entry, RegularExpression *dummy) { m_format_map.Add(std::move(type), entry); } void Add_Impl(ConstString type, const MapValueType &entry, ConstString *dummy) { m_format_map.Add(GetValidTypeName_Impl(type), entry); } bool Delete_Impl(ConstString type, ConstString *dummy) { return m_format_map.Delete(type); } bool Delete_Impl(ConstString type, RegularExpression *dummy) { std::lock_guard guard(m_format_map.mutex()); MapIterator pos, end = m_format_map.map().end(); for (pos = m_format_map.map().begin(); pos != end; pos++) { const RegularExpression ®ex = pos->first; if (type.GetStringRef() == regex.GetText()) { m_format_map.map().erase(pos); if (m_format_map.listener) m_format_map.listener->Changed(); return true; } } return false; } bool Get_Impl(ConstString type, MapValueType &entry, ConstString *dummy) { return m_format_map.Get(type, entry); } bool GetExact_Impl(ConstString type, MapValueType &entry, ConstString *dummy) { return Get_Impl(type, entry, static_cast(nullptr)); } lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex_Impl(size_t index, ConstString *dummy) { ConstString key = m_format_map.GetKeyAtIndex(index); if (key) return lldb::TypeNameSpecifierImplSP( new TypeNameSpecifierImpl(key.GetStringRef(), false)); else return lldb::TypeNameSpecifierImplSP(); } lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierAtIndex_Impl(size_t index, RegularExpression *dummy) { RegularExpression regex = m_format_map.GetKeyAtIndex(index); if (regex == RegularExpression()) return lldb::TypeNameSpecifierImplSP(); return lldb::TypeNameSpecifierImplSP( new TypeNameSpecifierImpl(regex.GetText().str().c_str(), true)); } bool Get_Impl(ConstString key, MapValueType &value, RegularExpression *dummy) { llvm::StringRef key_str = key.GetStringRef(); std::lock_guard guard(m_format_map.mutex()); // Patterns are matched in reverse-chronological order. for (const auto &pos : llvm::reverse(m_format_map.map())) { const RegularExpression ®ex = pos.first; if (regex.Execute(key_str)) { value = pos.second; return true; } } return false; } bool GetExact_Impl(ConstString key, MapValueType &value, RegularExpression *dummy) { std::lock_guard guard(m_format_map.mutex()); for (const auto &pos : m_format_map.map()) { const RegularExpression ®ex = pos.first; if (regex.GetText() == key.GetStringRef()) { value = pos.second; return true; } } return false; } bool Get(const FormattersMatchVector &candidates, MapValueType &entry) { for (const FormattersMatchCandidate &candidate : candidates) { if (Get(candidate.GetTypeName(), entry)) { if (candidate.IsMatch(entry) == false) { entry.reset(); continue; } else { return true; } } } return false; } }; } // namespace lldb_private #endif // LLDB_DATAFORMATTERS_FORMATTERSCONTAINER_H