blob: 0f69947548d528b9a57bf6738df9ccf1216b5ec5 [file]
//===-- 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