| //===-- DemangledNameInfo.cpp ---------------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Core/DemangledNameInfo.h" |
| #include "lldb/Core/Mangled.h" |
| |
| using namespace llvm::itanium_demangle; |
| |
| namespace lldb_private { |
| |
| std::optional<DemangledNameInfo> |
| DemangledNameInfoCache::Get(const Mangled &mangled) { |
| ConstString mangled_name = mangled.GetMangledName(); |
| if (!mangled_name) |
| return std::nullopt; |
| |
| bool cache_enabled = false; |
| { |
| auto state = m_state.LockShared(); |
| cache_enabled = state->max_entries != 0; |
| auto it = state->infos.find(mangled_name); |
| if (it != state->infos.end()) |
| return it->second; |
| } |
| |
| // Demangle outside of the lock. Two threads asking for the same name at the |
| // same time just compute the same info twice, which is cheaper than making |
| // every other thread wait for one demangle to finish. |
| std::optional<DemangledNameInfo> info = mangled.ComputeDemangledInfo(); |
| |
| // Caching is disabled. |
| if (!cache_enabled) |
| return info; |
| |
| auto state = m_state.Lock(); |
| // Another thread might have cached this name while we were demangling. |
| if (state->infos.contains(mangled_name)) |
| return info; |
| |
| // Make room for the new entry by dropping a random one. |
| if (state->infos.size() >= state->max_entries) |
| state->EvictRandomEntry(); |
| |
| state->infos.insert({mangled_name, info}); |
| state->mangled_names.push_back(mangled_name); |
| return info; |
| } |
| |
| void DemangledNameInfoCache::State::EvictRandomEntry() { |
| assert(!mangled_names.empty() && "evicting from an empty cache"); |
| std::uniform_int_distribution<size_t> dist(0, mangled_names.size() - 1); |
| size_t index = dist(rng); |
| infos.erase(mangled_names[index]); |
| mangled_names[index] = mangled_names.back(); |
| mangled_names.pop_back(); |
| } |
| |
| void DemangledNameInfoCache::Clear() { |
| auto state = m_state.Lock(); |
| state->infos.clear(); |
| state->mangled_names.clear(); |
| } |
| |
| size_t DemangledNameInfoCache::GetMaxEntries() const { |
| return m_state.LockShared()->max_entries; |
| } |
| |
| size_t DemangledNameInfoCache::GetNumEntries() const { |
| return m_state.LockShared()->infos.size(); |
| } |
| |
| void DemangledNameInfoCache::SetMaxEntries(size_t max_entries) { |
| // This is called before every lookup, so avoid taking the exclusive lock |
| // when the limit didn't change. |
| if (GetMaxEntries() == max_entries) |
| return; |
| |
| auto state = m_state.Lock(); |
| state->max_entries = max_entries; |
| if (state->infos.size() > state->max_entries) { |
| state->infos.clear(); |
| state->mangled_names.clear(); |
| } |
| } |
| |
| bool TrackingOutputBuffer::shouldTrack() const { |
| if (!isPrintingTopLevelFunctionType()) |
| return false; |
| |
| if (isInsideTemplateArgs()) |
| return false; |
| |
| if (NameInfo.ArgumentsRange.first > 0) |
| return false; |
| |
| return true; |
| } |
| |
| bool TrackingOutputBuffer::canFinalize() const { |
| if (!isPrintingTopLevelFunctionType()) |
| return false; |
| |
| if (isInsideTemplateArgs()) |
| return false; |
| |
| if (NameInfo.ArgumentsRange.first == 0) |
| return false; |
| |
| return true; |
| } |
| |
| void TrackingOutputBuffer::updateBasenameEnd() { |
| if (!shouldTrack()) |
| return; |
| |
| NameInfo.BasenameRange.second = getCurrentPosition(); |
| } |
| |
| void TrackingOutputBuffer::updateScopeStart() { |
| if (!shouldTrack()) |
| return; |
| |
| NameInfo.ScopeRange.first = getCurrentPosition(); |
| } |
| |
| void TrackingOutputBuffer::updateScopeEnd() { |
| if (!shouldTrack()) |
| return; |
| |
| NameInfo.ScopeRange.second = getCurrentPosition(); |
| } |
| |
| void TrackingOutputBuffer::finalizeArgumentEnd() { |
| if (!canFinalize()) |
| return; |
| |
| NameInfo.ArgumentsRange.second = getCurrentPosition(); |
| } |
| |
| void TrackingOutputBuffer::finalizeQualifiersStart() { |
| if (!canFinalize()) |
| return; |
| |
| NameInfo.QualifiersRange.first = getCurrentPosition(); |
| } |
| |
| void TrackingOutputBuffer::finalizeQualifiersEnd() { |
| if (!canFinalize()) |
| return; |
| |
| NameInfo.QualifiersRange.second = getCurrentPosition(); |
| } |
| |
| void TrackingOutputBuffer::finalizeStart() { |
| if (!shouldTrack()) |
| return; |
| |
| NameInfo.ArgumentsRange.first = getCurrentPosition(); |
| |
| // If nothing has set the end of the basename yet (for example when |
| // printing templates), then the beginning of the arguments is the end of |
| // the basename. |
| if (NameInfo.BasenameRange.second == 0) |
| NameInfo.BasenameRange.second = getCurrentPosition(); |
| |
| // There is something between the basename and the start of the function |
| // arguments. Assume those are template arguments (which *should* be true for |
| // C++ demangled names, but this assumption may change in the future, in |
| // which case this needs to be adjusted). |
| if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first) |
| NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second, |
| NameInfo.ArgumentsRange.first}; |
| |
| assert(!shouldTrack()); |
| assert(canFinalize()); |
| } |
| |
| void TrackingOutputBuffer::finalizeEnd() { |
| if (!canFinalize()) |
| return; |
| |
| if (NameInfo.ScopeRange.first > NameInfo.ScopeRange.second) |
| NameInfo.ScopeRange.second = NameInfo.ScopeRange.first; |
| NameInfo.BasenameRange.first = NameInfo.ScopeRange.second; |
| |
| // We call anything past the FunctionEncoding the "suffix". |
| // In practice this would be nodes like `DotSuffix` that wrap |
| // a FunctionEncoding. |
| NameInfo.SuffixRange.first = getCurrentPosition(); |
| } |
| |
| ScopedOverride<unsigned> TrackingOutputBuffer::enterFunctionTypePrinting() { |
| return {FunctionPrintingDepth, FunctionPrintingDepth + 1}; |
| } |
| |
| bool TrackingOutputBuffer::isPrintingTopLevelFunctionType() const { |
| return FunctionPrintingDepth == 1; |
| } |
| |
| void TrackingOutputBuffer::printLeft(const Node &N) { |
| switch (N.getKind()) { |
| case Node::KFunctionType: |
| printLeftImpl(static_cast<const FunctionType &>(N)); |
| break; |
| case Node::KFunctionEncoding: |
| printLeftImpl(static_cast<const FunctionEncoding &>(N)); |
| break; |
| case Node::KNestedName: |
| printLeftImpl(static_cast<const NestedName &>(N)); |
| break; |
| case Node::KNameWithTemplateArgs: |
| printLeftImpl(static_cast<const NameWithTemplateArgs &>(N)); |
| break; |
| default: |
| OutputBuffer::printLeft(N); |
| } |
| |
| // Keep updating suffix until we reach the end. |
| NameInfo.SuffixRange.second = getCurrentPosition(); |
| } |
| |
| void TrackingOutputBuffer::printRight(const Node &N) { |
| switch (N.getKind()) { |
| case Node::KFunctionType: |
| printRightImpl(static_cast<const FunctionType &>(N)); |
| break; |
| case Node::KFunctionEncoding: |
| printRightImpl(static_cast<const FunctionEncoding &>(N)); |
| break; |
| default: |
| OutputBuffer::printRight(N); |
| } |
| |
| // Keep updating suffix until we reach the end. |
| NameInfo.SuffixRange.second = getCurrentPosition(); |
| } |
| |
| void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) { |
| auto Scoped = enterFunctionTypePrinting(); |
| OutputBuffer::printLeft(N); |
| } |
| |
| void TrackingOutputBuffer::printRightImpl(const FunctionType &N) { |
| auto Scoped = enterFunctionTypePrinting(); |
| OutputBuffer::printRight(N); |
| } |
| |
| void TrackingOutputBuffer::printLeftImpl(const FunctionEncoding &N) { |
| auto Scoped = enterFunctionTypePrinting(); |
| |
| const Node *Ret = N.getReturnType(); |
| if (Ret) { |
| printLeft(*Ret); |
| if (!Ret->hasRHSComponent(*this)) |
| *this += " "; |
| } |
| |
| updateScopeStart(); |
| |
| N.getName()->print(*this); |
| } |
| |
| void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) { |
| auto Scoped = enterFunctionTypePrinting(); |
| finalizeStart(); |
| |
| printOpen(); |
| N.getParams().printWithComma(*this); |
| printClose(); |
| |
| finalizeArgumentEnd(); |
| |
| const Node *Ret = N.getReturnType(); |
| |
| if (Ret) |
| printRight(*Ret); |
| |
| finalizeQualifiersStart(); |
| |
| auto CVQuals = N.getCVQuals(); |
| auto RefQual = N.getRefQual(); |
| auto *Attrs = N.getAttrs(); |
| auto *Requires = N.getRequires(); |
| |
| if (CVQuals & QualConst) |
| *this += " const"; |
| if (CVQuals & QualVolatile) |
| *this += " volatile"; |
| if (CVQuals & QualRestrict) |
| *this += " restrict"; |
| if (RefQual == FrefQualLValue) |
| *this += " &"; |
| else if (RefQual == FrefQualRValue) |
| *this += " &&"; |
| if (Attrs != nullptr) |
| Attrs->print(*this); |
| if (Requires != nullptr) { |
| *this += " requires "; |
| Requires->print(*this); |
| } |
| |
| finalizeQualifiersEnd(); |
| finalizeEnd(); |
| } |
| |
| void TrackingOutputBuffer::printLeftImpl(const NestedName &N) { |
| N.Qual->print(*this); |
| *this += "::"; |
| updateScopeEnd(); |
| N.Name->print(*this); |
| updateBasenameEnd(); |
| } |
| |
| void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) { |
| N.Name->print(*this); |
| updateBasenameEnd(); |
| N.TemplateArgs->print(*this); |
| } |
| |
| } // namespace lldb_private |