#include "lldb/Core/Mangled.h"
#include "lldb/Core/DataFileCache.h"
#include "lldb/Core/RichManglingContext.h"
#include "lldb/Target/Language.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/DataEncoder.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegularExpression.h"
#include "lldb/Utility/Stream.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/VariableList.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/Compiler.h"
#include "CangjieDemangle.h"
#include <mutex>
#include <string>
#include <utility>
#include <cstdlib>
#include <cstring>
using namespace lldb_private;
static inline bool cstring_is_mangled(llvm::StringRef s) {
return Mangled::GetManglingScheme(s) != Mangled::eManglingSchemeNone;
}
#pragma mark Mangled
Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef const name) {
if (name.empty())
return Mangled::eManglingSchemeNone;
if (name.startswith("_C"))
return Mangled::eManglingSchemeCangjie;
if (name.startswith("?"))
return Mangled::eManglingSchemeMSVC;
if (name.startswith("_R"))
return Mangled::eManglingSchemeRustV0;
if (name.startswith("_D"))
return Mangled::eManglingSchemeD;
if (name.startswith("_Z"))
return Mangled::eManglingSchemeItanium;
if (name.startswith("___Z"))
return Mangled::eManglingSchemeItanium;
return Mangled::eManglingSchemeNone;
}
Mangled::Mangled(ConstString s) : m_mangled(), m_demangled() {
if (s)
SetValue(s, true);
}
Mangled::Mangled(llvm::StringRef name) {
if (!name.empty())
SetValue(ConstString(name));
}
Mangled::operator bool() const { return m_mangled || m_demangled; }
void Mangled::Clear() {
m_mangled.Clear();
m_demangled.Clear();
}
int Mangled::Compare(const Mangled &a, const Mangled &b) {
return ConstString::Compare(a.GetName(ePreferMangled),
b.GetName(ePreferMangled));
}
void Mangled::SetValue(ConstString s, bool mangled) {
if (s) {
if (mangled) {
m_demangled.Clear();
m_mangled = s;
} else {
m_demangled = s;
m_mangled.Clear();
}
} else {
m_demangled.Clear();
m_mangled.Clear();
}
}
void Mangled::SetValue(ConstString name) {
if (name) {
if (cstring_is_mangled(name.GetStringRef())) {
m_demangled.Clear();
m_mangled = name;
} else {
m_demangled = name;
m_mangled.Clear();
}
} else {
m_demangled.Clear();
m_mangled.Clear();
}
}
static char *GetMSVCDemangledStr(const char *M) {
char *demangled_cstr = llvm::microsoftDemangle(
M, nullptr, nullptr, nullptr, nullptr,
llvm::MSDemangleFlags(
llvm::MSDF_NoAccessSpecifier | llvm::MSDF_NoCallingConvention |
llvm::MSDF_NoMemberType | llvm::MSDF_NoVariableType));
if (Log *log = GetLog(LLDBLog::Demangle)) {
if (demangled_cstr && demangled_cstr[0])
LLDB_LOGF(log, "demangled msvc: %s -> \"%s\"", M, demangled_cstr);
else
LLDB_LOGF(log, "demangled msvc: %s -> error", M);
}
return demangled_cstr;
}
static char *GetItaniumDemangledStr(const char *M) {
char *demangled_cstr = nullptr;
llvm::ItaniumPartialDemangler ipd;
bool err = ipd.partialDemangle(M);
if (!err) {
size_t demangled_size = 80;
demangled_cstr = static_cast<char *>(std::malloc(demangled_size));
demangled_cstr = ipd.finishDemangle(demangled_cstr, &demangled_size);
assert(demangled_cstr &&
"finishDemangle must always succeed if partialDemangle did");
assert(demangled_cstr[demangled_size - 1] == '\0' &&
"Expected demangled_size to return length including trailing null");
}
if (Log *log = GetLog(LLDBLog::Demangle)) {
if (demangled_cstr)
LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr);
else
LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M);
}
return demangled_cstr;
}
static char *GetRustV0DemangledStr(const char *M) {
char *demangled_cstr = llvm::rustDemangle(M);
if (Log *log = GetLog(LLDBLog::Demangle)) {
if (demangled_cstr && demangled_cstr[0])
LLDB_LOG(log, "demangled rustv0: {0} -> \"{1}\"", M, demangled_cstr);
else
LLDB_LOG(log, "demangled rustv0: {0} -> error: failed to demangle", M);
}
return demangled_cstr;
}
static char *GetDLangDemangledStr(const char *M) {
char *demangled_cstr = llvm::dlangDemangle(M);
if (Log *log = GetLog(LLDBLog::Demangle)) {
if (demangled_cstr && demangled_cstr[0])
LLDB_LOG(log, "demangled dlang: {0} -> \"{1}\"", M, demangled_cstr);
else
LLDB_LOG(log, "demangled dlang: {0} -> error: failed to demangle", M);
}
return demangled_cstr;
}
bool Mangled::GetRichManglingInfo(RichManglingContext &context,
SkipMangledNameFn *skip_mangled_name) {
assert(m_mangled);
ManglingScheme scheme = GetManglingScheme(m_mangled.GetStringRef());
if (skip_mangled_name && skip_mangled_name(m_mangled.GetStringRef(), scheme))
return false;
switch (scheme) {
case eManglingSchemeNone:
return false;
case eManglingSchemeCangjie:
case eManglingSchemeItanium:
return context.FromItaniumName(m_mangled);
case eManglingSchemeMSVC: {
if (!m_demangled && !m_mangled.GetMangledCounterpart(m_demangled)) {
if (char *d = GetMSVCDemangledStr(m_mangled.GetCString())) {
m_demangled.SetStringWithMangledCounterpart(llvm::StringRef(d),
m_mangled);
::free(d);
} else {
m_demangled.SetCString("");
}
}
if (m_demangled.IsEmpty()) {
return false;
} else {
return context.FromCxxMethodName(m_demangled);
}
}
case eManglingSchemeRustV0:
case eManglingSchemeD:
return false;
}
llvm_unreachable("Fully covered switch above!");
}
static void CollectionGenericParamesFromFunction(const SymbolContext &const_sc, std::vector<std::string> &vector) {
SymbolContext &sc = const_cast<SymbolContext &>(const_sc);
if (!sc.function) {
return;
}
auto block = sc.GetFunctionBlock();
if (!block) {
return;
}
auto var_list = block->GetBlockVariableList(true);
if (!var_list) {
return;
}
for (unsigned i = 0; i < var_list->GetSize(); ++i) {
auto var_sp = var_list->GetVariableAtIndex(i);
auto name = var_sp->GetName().GetStringRef();
if (!name.consume_front("$G_")) {
continue;
}
uint64_t index;
if (name.consumeInteger(10, index)) {
continue;
}
Type *type = var_sp->GetType();
if (!type)
continue;
vector.push_back(type->GetName().AsCString());
}
}
static std::string GetDemangleInfo(const char *mangled, const SymbolContext *sc) {
Log *log = GetLog(LLDBLog::Demangle);
std::string name(mangled);
std::string suffix(".objKlass");
auto pos = name.find(suffix);
if (pos != std::string::npos && pos == name.size() - suffix.size()) {
name.erase(pos);
}
std::vector<std::string> parames;
if (sc && sc->function) {
CollectionGenericParamesFromFunction(*sc, parames);
}
auto demangle_info = Cangjie::Demangle(name, parames);
std::string pkgname = demangle_info.GetPkgName();
std::string fullname = demangle_info.GetFullName();
if (!pkgname.empty()) {
fullname = pkgname + std::string("::") + fullname;
}
LLDB_LOG(log, "Cangjie Demangle: %s->%s", mangled, fullname.c_str());
return fullname;
}
std::string Mangled::GetDemangledTypeName(std::string &type_name) {
auto demangle_info = Cangjie::DemangleType(type_name);
std::string fullname = demangle_info.GetFullName();
return fullname;
}
ConstString Mangled::GetDemangledBaseName(const SymbolContext *sc) const {
std::string demangled;
if (m_mangled && m_demangled.IsNull()) {
demangled = GetDemangleInfo(m_mangled.AsCString(), sc);
}
if (!m_demangled.IsNull()) {
demangled = std::string(m_demangled.AsCString());
}
if (demangled.find("(") != std::string::npos) {
demangled = demangled.substr(0, demangled.find("("));
} else {
return ConstString();
}
if (demangled.find_last_of("::") != std::string::npos) {
demangled = demangled.substr(demangled.find_last_of("::") + 1);
}
while (true) {
auto left = demangled.find_first_of("<");
auto right = demangled.find_first_of(">");
if (left != std::string::npos && right != std::string::npos && left < right) {
demangled.erase(left, right - left + 1);
continue;
}
break;
}
if (!demangled.empty()) {
return ConstString(demangled.c_str());
}
return ConstString();
}
ConstString Mangled::GetDemangledName(const SymbolContext *sc) const {
if (m_mangled && (m_demangled.IsNull() || (sc && sc->function))) {
std::string cjdemangled = GetDemangleInfo(m_mangled.AsCString(), sc);
if (!cjdemangled.empty()) {
m_demangled.SetStringWithMangledCounterpart(llvm::StringRef(cjdemangled.c_str()),
m_mangled);
return m_demangled;
}
const char *mangled_name = m_mangled.GetCString();
ManglingScheme mangling_scheme =
GetManglingScheme(m_mangled.GetStringRef());
if (mangling_scheme != eManglingSchemeNone &&
!m_mangled.GetMangledCounterpart(m_demangled)) {
char *demangled_name = nullptr;
switch (mangling_scheme) {
case eManglingSchemeMSVC:
demangled_name = GetMSVCDemangledStr(mangled_name);
break;
case eManglingSchemeCangjie:
case eManglingSchemeItanium: {
demangled_name = GetItaniumDemangledStr(mangled_name);
break;
}
case eManglingSchemeRustV0:
demangled_name = GetRustV0DemangledStr(mangled_name);
break;
case eManglingSchemeD:
demangled_name = GetDLangDemangledStr(mangled_name);
break;
case eManglingSchemeNone:
llvm_unreachable("eManglingSchemeNone was handled already");
}
if (demangled_name) {
m_demangled.SetStringWithMangledCounterpart(
llvm::StringRef(demangled_name), m_mangled);
free(demangled_name);
}
}
if (m_demangled.IsNull()) {
m_demangled.SetCString("");
}
}
return m_demangled;
}
ConstString Mangled::GetDisplayDemangledName(const SymbolContext *sc) const {
return GetDemangledName(sc);
}
bool Mangled::NameMatches(const RegularExpression ®ex) const {
if (m_mangled && regex.Execute(m_mangled.GetStringRef()))
return true;
ConstString demangled = GetDemangledName();
return demangled && regex.Execute(demangled.GetStringRef());
}
ConstString Mangled::GetName(Mangled::NamePreference preference, const SymbolContext *sc) const {
if (preference == ePreferMangled && m_mangled)
return m_mangled;
ConstString demangled = GetDemangledName(sc);
if (preference == ePreferDemangledWithoutArguments && demangled) {
std::string demangled_without_arg(demangled.AsCString());
if (demangled_without_arg.find("(") != std::string::npos) {
demangled_without_arg = demangled_without_arg.substr(0, demangled_without_arg.find("("));
}
return ConstString(demangled_without_arg.c_str());
}
if (preference == ePreferDemangledWithoutArguments) {
if (Language *lang = Language::FindPlugin(GuessLanguage())) {
return lang->GetDemangledFunctionNameWithoutArguments(*this);
}
}
if (preference == ePreferDemangled) {
if (demangled)
return demangled;
return m_mangled;
}
return demangled;
}
void Mangled::Dump(Stream *s) const {
if (m_mangled) {
*s << ", mangled = " << m_mangled;
}
if (m_demangled) {
const char *demangled = m_demangled.AsCString();
s->Printf(", demangled = %s", demangled[0] ? demangled : "<error>");
}
}
void Mangled::DumpDebug(Stream *s) const {
s->Printf("%*p: Mangled mangled = ", static_cast<int>(sizeof(void *) * 2),
static_cast<const void *>(this));
m_mangled.DumpDebug(s);
s->Printf(", demangled = ");
m_demangled.DumpDebug(s);
}
size_t Mangled::MemorySize() const {
return m_mangled.MemorySize() + m_demangled.MemorySize();
}
lldb::LanguageType Mangled::GuessLanguage() const {
lldb::LanguageType result = lldb::eLanguageTypeUnknown;
Language::ForEach([this, &result](Language *l) {
if (l->SymbolNameFitsToLanguage(*this)) {
result = l->GetLanguageType();
return false;
}
return true;
});
return result;
}
Stream &operator<<(Stream &s, const Mangled &obj) {
if (obj.GetMangledName())
s << "mangled = '" << obj.GetMangledName() << "'";
ConstString demangled = obj.GetDemangledName();
if (demangled)
s << ", demangled = '" << demangled << '\'';
else
s << ", demangled = <error>";
return s;
}
enum MangledEncoding {
Empty = 0u,
DemangledOnly = 1u,
MangledOnly = 2u,
MangledAndDemangled = 3u
};
bool Mangled::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,
const StringTableReader &strtab) {
m_mangled.Clear();
m_demangled.Clear();
MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr);
switch (encoding) {
case Empty:
return true;
case DemangledOnly:
m_demangled.SetString(strtab.Get(data.GetU32(offset_ptr)));
return true;
case MangledOnly:
m_mangled.SetString(strtab.Get(data.GetU32(offset_ptr)));
return true;
case MangledAndDemangled:
m_mangled.SetString(strtab.Get(data.GetU32(offset_ptr)));
m_demangled.SetString(strtab.Get(data.GetU32(offset_ptr)));
return true;
}
return false;
}
void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const {
MangledEncoding encoding = Empty;
if (m_mangled) {
encoding = MangledOnly;
if (m_demangled) {
ConstString s;
if (!(m_mangled.GetMangledCounterpart(s) && s == m_demangled))
encoding = MangledAndDemangled;
}
} else if (m_demangled) {
encoding = DemangledOnly;
}
file.AppendU8(encoding);
switch (encoding) {
case Empty:
break;
case DemangledOnly:
file.AppendU32(strtab.Add(m_demangled));
break;
case MangledOnly:
file.AppendU32(strtab.Add(m_mangled));
break;
case MangledAndDemangled:
file.AppendU32(strtab.Add(m_mangled));
file.AppendU32(strtab.Add(m_demangled));
break;
}
}