#include "llvm/Demangle/Demangle.h"
#include "Compiler.h"
#include "StringView.h"
#include "Utility.h"
#include <cctype>
#include <tuple>
namespace {
constexpr size_t AllocUnit = 4096;
class ArenaAllocator {
struct AllocatorNode {
uint8_t *Buf = nullptr;
size_t Used = 0;
size_t Capacity = 0;
AllocatorNode *Next = nullptr;
};
void addNode(size_t Capacity) {
AllocatorNode *NewHead = new AllocatorNode;
NewHead->Buf = new uint8_t[Capacity];
NewHead->Next = Head;
NewHead->Capacity = Capacity;
Head = NewHead;
NewHead->Used = 0;
}
public:
ArenaAllocator() { addNode(AllocUnit); }
~ArenaAllocator() {
while (Head) {
assert(Head->Buf);
delete[] Head->Buf;
AllocatorNode *Next = Head->Next;
delete Head;
Head = Next;
}
}
char *allocUnalignedBuffer(size_t Length) {
uint8_t *Buf = Head->Buf + Head->Used;
Head->Used += Length;
if (Head->Used > Head->Capacity) {
addNode(std::max(AllocUnit, Length));
Head->Used = Length;
Buf = Head->Buf;
}
return reinterpret_cast<char *>(Buf);
}
template <typename T, typename... Args> T *alloc(Args &&... ConstructorArgs) {
size_t Size = sizeof(T);
assert(Head && Head->Buf);
size_t P = (size_t)Head->Buf + Head->Used;
uintptr_t AlignedP =
(((size_t)P + alignof(T) - 1) & ~(size_t)(alignof(T) - 1));
uint8_t *PP = (uint8_t *)AlignedP;
size_t Adjustment = AlignedP - P;
Head->Used += Size + Adjustment;
if (Head->Used < Head->Capacity)
return new (PP) T(std::forward<Args>(ConstructorArgs)...);
addNode(AllocUnit);
Head->Used = Size;
return new (Head->Buf) T(std::forward<Args>(ConstructorArgs)...);
}
private:
AllocatorNode *Head = nullptr;
};
}
static bool startsWithDigit(StringView S) {
return !S.empty() && std::isdigit(S.front());
}
static void outputSpaceIfNecessary(OutputStream &OS) {
if (OS.empty())
return;
char C = OS.back();
if (isalnum(C) || C == '>')
OS << " ";
}
enum Qualifiers : uint8_t {
Q_None = 0,
Q_Const = 1 << 0,
Q_Volatile = 1 << 1,
Q_Far = 1 << 2,
Q_Huge = 1 << 3,
Q_Unaligned = 1 << 4,
Q_Restrict = 1 << 5,
Q_Pointer64 = 1 << 6
};
enum class StorageClass : uint8_t {
None,
PrivateStatic,
ProtectedStatic,
PublicStatic,
Global,
FunctionLocalStatic
};
enum class QualifierMangleMode { Drop, Mangle, Result };
enum class PointerAffinity { Pointer, Reference, RValueReference };
enum class CallingConv : uint8_t {
None,
Cdecl,
Pascal,
Thiscall,
Stdcall,
Fastcall,
Clrcall,
Eabi,
Vectorcall,
Regcall,
};
enum class ReferenceKind : uint8_t { None, LValueRef, RValueRef };
enum class PrimTy : uint8_t {
Unknown,
None,
Function,
Ptr,
MemberPtr,
Array,
Struct,
Union,
Class,
Enum,
Void,
Bool,
Char,
Schar,
Uchar,
Char16,
Char32,
Short,
Ushort,
Int,
Uint,
Long,
Ulong,
Int64,
Uint64,
Wchar,
Float,
Double,
Ldouble,
Nullptr
};
enum FuncClass : uint8_t {
Public = 1 << 0,
Protected = 1 << 1,
Private = 1 << 2,
Global = 1 << 3,
Static = 1 << 4,
Virtual = 1 << 5,
Far = 1 << 6,
};
namespace {
struct Type;
struct Name;
struct FunctionParams {
bool IsVariadic = false;
Type *Current = nullptr;
FunctionParams *Next = nullptr;
};
struct TemplateParams {
bool IsTemplateTemplate = false;
bool IsAliasTemplate = false;
Type *ParamType = nullptr;
Name *ParamName = nullptr;
TemplateParams *Next = nullptr;
};
struct Type {
virtual ~Type() {}
virtual Type *clone(ArenaAllocator &Arena) const;
static void outputPre(OutputStream &OS, Type &Ty);
static void outputPost(OutputStream &OS, Type &Ty);
virtual void outputPre(OutputStream &OS);
virtual void outputPost(OutputStream &OS);
PrimTy Prim = PrimTy::Unknown;
Qualifiers Quals = Q_None;
StorageClass Storage = StorageClass::None;
};
struct Name {
StringView Str;
StringView Operator;
TemplateParams *TParams = nullptr;
Name *Next = nullptr;
};
struct PointerType : public Type {
Type *clone(ArenaAllocator &Arena) const override;
void outputPre(OutputStream &OS) override;
void outputPost(OutputStream &OS) override;
PointerAffinity Affinity;
Type *Pointee = nullptr;
};
struct MemberPointerType : public Type {
Type *clone(ArenaAllocator &Arena) const override;
void outputPre(OutputStream &OS) override;
void outputPost(OutputStream &OS) override;
Name *MemberName = nullptr;
Type *Pointee = nullptr;
};
struct FunctionType : public Type {
Type *clone(ArenaAllocator &Arena) const override;
void outputPre(OutputStream &OS) override;
void outputPost(OutputStream &OS) override;
bool IsFunctionPointer = false;
Type *ReturnType = nullptr;
ReferenceKind RefKind;
CallingConv CallConvention;
FuncClass FunctionClass;
FunctionParams Params;
};
struct UdtType : public Type {
Type *clone(ArenaAllocator &Arena) const override;
void outputPre(OutputStream &OS) override;
Name *UdtName = nullptr;
};
struct ArrayType : public Type {
Type *clone(ArenaAllocator &Arena) const override;
void outputPre(OutputStream &OS) override;
void outputPost(OutputStream &OS) override;
ArrayType *NextDimension = nullptr;
uint32_t ArrayDimension = 0;
Type *ElementType = nullptr;
};
}
static bool isMemberPointer(StringView MangledName) {
switch (MangledName.popFront()) {
case '$':
return false;
case 'A':
return false;
case 'P':
case 'Q':
case 'R':
case 'S':
break;
default:
assert(false && "Ty is not a pointer type!");
}
if (startsWithDigit(MangledName)) {
assert(MangledName[0] == '6' || MangledName[0] == '8');
return (MangledName[0] == '8');
}
MangledName.consumeFront('E');
MangledName.consumeFront('I');
MangledName.consumeFront('F');
assert(!MangledName.empty());
switch (MangledName.front()) {
case 'A':
case 'B':
case 'C':
case 'D':
return false;
case 'Q':
case 'R':
case 'S':
case 'T':
return true;
default:
assert(false);
}
return false;
}
static void outputCallingConvention(OutputStream &OS, CallingConv CC) {
outputSpaceIfNecessary(OS);
switch (CC) {
case CallingConv::Cdecl:
OS << "__cdecl";
break;
case CallingConv::Fastcall:
OS << "__fastcall";
break;
case CallingConv::Pascal:
OS << "__pascal";
break;
case CallingConv::Regcall:
OS << "__regcall";
break;
case CallingConv::Stdcall:
OS << "__stdcall";
break;
case CallingConv::Thiscall:
OS << "__thiscall";
break;
case CallingConv::Eabi:
OS << "__eabi";
break;
case CallingConv::Vectorcall:
OS << "__vectorcall";
break;
case CallingConv::Clrcall:
OS << "__clrcall";
break;
default:
break;
}
}
static bool startsWithLocalScopePattern(StringView S) {
if (!S.consumeFront('?'))
return false;
if (S.size() < 2)
return false;
size_t End = S.find('?');
if (End == StringView::npos)
return false;
StringView Candidate = S.substr(0, End);
if (Candidate.empty())
return false;
if (Candidate.size() == 1)
return Candidate[0] == '@' || (Candidate[0] >= '0' && Candidate[0] <= '9');
if (Candidate.back() != '@')
return false;
Candidate = Candidate.dropBack();
if (Candidate[0] < 'B' || Candidate[0] > 'P')
return false;
Candidate = Candidate.dropFront();
while (!Candidate.empty()) {
if (Candidate[0] < 'A' || Candidate[0] > 'P')
return false;
Candidate = Candidate.dropFront();
}
return true;
}
static void outputName(OutputStream &OS, const Name *TheName);
static void outputParameterList(OutputStream &OS,
const FunctionParams &Params) {
if (!Params.Current) {
OS << "void";
return;
}
const FunctionParams *Head = &Params;
while (Head) {
Type::outputPre(OS, *Head->Current);
Type::outputPost(OS, *Head->Current);
Head = Head->Next;
if (Head)
OS << ", ";
}
}
static void outputParameterList(OutputStream &OS,
const TemplateParams &Params) {
if (!Params.ParamType && !Params.ParamName) {
OS << "<>";
return;
}
OS << "<";
const TemplateParams *Head = &Params;
while (Head) {
if (Head->ParamType && Head->ParamName) {
OS << "&";
Type::outputPre(OS, *Head->ParamType);
outputName(OS, Head->ParamName);
Type::outputPost(OS, *Head->ParamType);
} else if (Head->ParamType) {
Type::outputPre(OS, *Head->ParamType);
Type::outputPost(OS, *Head->ParamType);
} else {
outputName(OS, Head->ParamName);
}
Head = Head->Next;
if (Head)
OS << ", ";
}
OS << ">";
}
static void outputName(OutputStream &OS, const Name *TheName) {
if (!TheName)
return;
outputSpaceIfNecessary(OS);
const Name *Previous = nullptr;
for (; TheName->Next; TheName = TheName->Next) {
Previous = TheName;
OS << TheName->Str;
if (TheName->TParams)
outputParameterList(OS, *TheName->TParams);
OS << "::";
}
if (TheName->Operator.empty()) {
OS << TheName->Str;
if (TheName->TParams)
outputParameterList(OS, *TheName->TParams);
return;
}
if (TheName->Operator == "dtor")
OS << "~";
if (TheName->Operator == "ctor" || TheName->Operator == "dtor") {
OS << Previous->Str;
if (Previous->TParams)
outputParameterList(OS, *Previous->TParams);
return;
}
if (!TheName->Str.empty())
OS << TheName->Str << "::";
OS << "operator" << TheName->Operator;
}
namespace {
Type *Type::clone(ArenaAllocator &Arena) const {
return Arena.alloc<Type>(*this);
}
void Type::outputPre(OutputStream &OS, Type &Ty) {
if (Ty.Prim == PrimTy::Function) {
Ty.outputPre(OS);
return;
}
switch (Ty.Storage) {
case StorageClass::PrivateStatic:
case StorageClass::PublicStatic:
case StorageClass::ProtectedStatic:
OS << "static ";
default:
break;
}
Ty.outputPre(OS);
if (Ty.Quals & Q_Const) {
outputSpaceIfNecessary(OS);
OS << "const";
}
if (Ty.Quals & Q_Volatile) {
outputSpaceIfNecessary(OS);
OS << "volatile";
}
if (Ty.Quals & Q_Restrict) {
outputSpaceIfNecessary(OS);
OS << "__restrict";
}
}
void Type::outputPost(OutputStream &OS, Type &Ty) { Ty.outputPost(OS); }
void Type::outputPre(OutputStream &OS) {
switch (Prim) {
case PrimTy::Void:
OS << "void";
break;
case PrimTy::Bool:
OS << "bool";
break;
case PrimTy::Char:
OS << "char";
break;
case PrimTy::Schar:
OS << "signed char";
break;
case PrimTy::Uchar:
OS << "unsigned char";
break;
case PrimTy::Char16:
OS << "char16_t";
break;
case PrimTy::Char32:
OS << "char32_t";
break;
case PrimTy::Short:
OS << "short";
break;
case PrimTy::Ushort:
OS << "unsigned short";
break;
case PrimTy::Int:
OS << "int";
break;
case PrimTy::Uint:
OS << "unsigned int";
break;
case PrimTy::Long:
OS << "long";
break;
case PrimTy::Ulong:
OS << "unsigned long";
break;
case PrimTy::Int64:
OS << "__int64";
break;
case PrimTy::Uint64:
OS << "unsigned __int64";
break;
case PrimTy::Wchar:
OS << "wchar_t";
break;
case PrimTy::Float:
OS << "float";
break;
case PrimTy::Double:
OS << "double";
break;
case PrimTy::Ldouble:
OS << "long double";
break;
case PrimTy::Nullptr:
OS << "std::nullptr_t";
break;
default:
assert(false && "Invalid primitive type!");
}
}
void Type::outputPost(OutputStream &OS) {}
Type *PointerType::clone(ArenaAllocator &Arena) const {
return Arena.alloc<PointerType>(*this);
}
static void outputPointerIndicator(OutputStream &OS, PointerAffinity Affinity,
const Name *MemberName,
const Type *Pointee) {
if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array) {
OS << "(";
if (Pointee->Prim == PrimTy::Function) {
const FunctionType *FTy = static_cast<const FunctionType *>(Pointee);
assert(FTy->IsFunctionPointer);
outputCallingConvention(OS, FTy->CallConvention);
OS << " ";
}
}
if (MemberName) {
outputName(OS, MemberName);
OS << "::";
}
if (Affinity == PointerAffinity::Pointer)
OS << "*";
else if (Affinity == PointerAffinity::Reference)
OS << "&";
else
OS << "&&";
}
void PointerType::outputPre(OutputStream &OS) {
Type::outputPre(OS, *Pointee);
outputSpaceIfNecessary(OS);
if (Quals & Q_Unaligned)
OS << "__unaligned ";
outputPointerIndicator(OS, Affinity, nullptr, Pointee);
}
void PointerType::outputPost(OutputStream &OS) {
if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array)
OS << ")";
Type::outputPost(OS, *Pointee);
}
Type *MemberPointerType::clone(ArenaAllocator &Arena) const {
return Arena.alloc<MemberPointerType>(*this);
}
void MemberPointerType::outputPre(OutputStream &OS) {
Type::outputPre(OS, *Pointee);
outputSpaceIfNecessary(OS);
outputPointerIndicator(OS, PointerAffinity::Pointer, MemberName, Pointee);
if (Quals & Q_Restrict)
OS << " __restrict";
}
void MemberPointerType::outputPost(OutputStream &OS) {
if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array)
OS << ")";
Type::outputPost(OS, *Pointee);
}
Type *FunctionType::clone(ArenaAllocator &Arena) const {
return Arena.alloc<FunctionType>(*this);
}
void FunctionType::outputPre(OutputStream &OS) {
if (!(FunctionClass & Global)) {
if (FunctionClass & Static)
OS << "static ";
}
if (ReturnType) {
Type::outputPre(OS, *ReturnType);
OS << " ";
}
if (!IsFunctionPointer)
outputCallingConvention(OS, CallConvention);
}
void FunctionType::outputPost(OutputStream &OS) {
OS << "(";
outputParameterList(OS, Params);
OS << ")";
if (Quals & Q_Const)
OS << " const";
if (Quals & Q_Volatile)
OS << " volatile";
if (Quals & Q_Restrict)
OS << " __restrict";
if (Quals & Q_Unaligned)
OS << " __unaligned";
if (RefKind == ReferenceKind::LValueRef)
OS << " &";
else if (RefKind == ReferenceKind::RValueRef)
OS << " &&";
if (ReturnType)
Type::outputPost(OS, *ReturnType);
return;
}
Type *UdtType::clone(ArenaAllocator &Arena) const {
return Arena.alloc<UdtType>(*this);
}
void UdtType::outputPre(OutputStream &OS) {
switch (Prim) {
case PrimTy::Class:
OS << "class ";
break;
case PrimTy::Struct:
OS << "struct ";
break;
case PrimTy::Union:
OS << "union ";
break;
case PrimTy::Enum:
OS << "enum ";
break;
default:
assert(false && "Not a udt type!");
}
outputName(OS, UdtName);
}
Type *ArrayType::clone(ArenaAllocator &Arena) const {
return Arena.alloc<ArrayType>(*this);
}
void ArrayType::outputPre(OutputStream &OS) {
Type::outputPre(OS, *ElementType);
}
void ArrayType::outputPost(OutputStream &OS) {
if (ArrayDimension > 0)
OS << "[" << ArrayDimension << "]";
if (NextDimension)
Type::outputPost(OS, *NextDimension);
else if (ElementType)
Type::outputPost(OS, *ElementType);
}
struct Symbol {
Name *SymbolName = nullptr;
Type *SymbolType = nullptr;
};
}
namespace {
class Demangler {
public:
Demangler() = default;
Symbol *parse(StringView &MangledName);
void output(const Symbol *S, OutputStream &OS);
bool Error = false;
private:
Type *demangleVariableEncoding(StringView &MangledName);
Type *demangleFunctionEncoding(StringView &MangledName);
Qualifiers demanglePointerExtQualifiers(StringView &MangledName);
Type *demangleType(StringView &MangledName, QualifierMangleMode QMM);
Type *demangleBasicType(StringView &MangledName);
UdtType *demangleClassType(StringView &MangledName);
PointerType *demanglePointerType(StringView &MangledName);
MemberPointerType *demangleMemberPointerType(StringView &MangledName);
FunctionType *demangleFunctionType(StringView &MangledName, bool HasThisQuals,
bool IsFunctionPointer);
ArrayType *demangleArrayType(StringView &MangledName);
TemplateParams *demangleTemplateParameterList(StringView &MangledName);
FunctionParams demangleFunctionParameterList(StringView &MangledName);
int demangleNumber(StringView &MangledName);
void memorizeString(StringView s);
StringView copyString(StringView Borrowed);
Name *demangleFullyQualifiedTypeName(StringView &MangledName);
Name *demangleFullyQualifiedSymbolName(StringView &MangledName);
Name *demangleUnqualifiedTypeName(StringView &MangledName);
Name *demangleUnqualifiedSymbolName(StringView &MangledName);
Name *demangleNameScopeChain(StringView &MangledName, Name *UnqualifiedName);
Name *demangleNameScopePiece(StringView &MangledName);
Name *demangleBackRefName(StringView &MangledName);
Name *demangleClassTemplateName(StringView &MangledName);
Name *demangleOperatorName(StringView &MangledName);
Name *demangleSimpleName(StringView &MangledName, bool Memorize);
Name *demangleAnonymousNamespaceName(StringView &MangledName);
Name *demangleLocallyScopedNamePiece(StringView &MangledName);
StringView demangleSimpleString(StringView &MangledName, bool Memorize);
FuncClass demangleFunctionClass(StringView &MangledName);
CallingConv demangleCallingConvention(StringView &MangledName);
StorageClass demangleVariableStorageClass(StringView &MangledName);
ReferenceKind demangleReferenceKind(StringView &MangledName);
void demangleThrowSpecification(StringView &MangledName);
std::pair<Qualifiers, bool> demangleQualifiers(StringView &MangledName);
ArenaAllocator Arena;
Type *FunctionParamBackRefs[10];
size_t FunctionParamBackRefCount = 0;
StringView BackReferences[10];
size_t BackRefCount = 0;
};
}
StringView Demangler::copyString(StringView Borrowed) {
char *Stable = Arena.allocUnalignedBuffer(Borrowed.size() + 1);
std::strcpy(Stable, Borrowed.begin());
return {Stable, Borrowed.size()};
}
Symbol *Demangler::parse(StringView &MangledName) {
Symbol *S = Arena.alloc<Symbol>();
if (!MangledName.consumeFront("?")) {
S->SymbolName = Arena.alloc<Name>();
S->SymbolName->Str = MangledName;
S->SymbolType = Arena.alloc<Type>();
S->SymbolType->Prim = PrimTy::Unknown;
return S;
}
S->SymbolName = demangleFullyQualifiedSymbolName(MangledName);
S->SymbolType = startsWithDigit(MangledName)
? demangleVariableEncoding(MangledName)
: demangleFunctionEncoding(MangledName);
return S;
}
Type *Demangler::demangleVariableEncoding(StringView &MangledName) {
StorageClass SC = demangleVariableStorageClass(MangledName);
Type *Ty = demangleType(MangledName, QualifierMangleMode::Drop);
Ty->Storage = SC;
switch (Ty->Prim) {
case PrimTy::Ptr:
case PrimTy::MemberPtr: {
Qualifiers ExtraChildQuals = Q_None;
Ty->Quals =
Qualifiers(Ty->Quals | demanglePointerExtQualifiers(MangledName));
bool IsMember = false;
std::tie(ExtraChildQuals, IsMember) = demangleQualifiers(MangledName);
if (Ty->Prim == PrimTy::MemberPtr) {
assert(IsMember);
Name *BackRefName = demangleFullyQualifiedTypeName(MangledName);
(void)BackRefName;
MemberPointerType *MPTy = static_cast<MemberPointerType *>(Ty);
MPTy->Pointee->Quals = Qualifiers(MPTy->Pointee->Quals | ExtraChildQuals);
} else {
PointerType *PTy = static_cast<PointerType *>(Ty);
PTy->Pointee->Quals = Qualifiers(PTy->Pointee->Quals | ExtraChildQuals);
}
break;
}
default:
Ty->Quals = demangleQualifiers(MangledName).first;
break;
}
return Ty;
}
int Demangler::demangleNumber(StringView &MangledName) {
bool neg = MangledName.consumeFront("?");
if (startsWithDigit(MangledName)) {
int32_t Ret = MangledName[0] - '0' + 1;
MangledName = MangledName.dropFront(1);
return neg ? -Ret : Ret;
}
int Ret = 0;
for (size_t i = 0; i < MangledName.size(); ++i) {
char C = MangledName[i];
if (C == '@') {
MangledName = MangledName.dropFront(i + 1);
return neg ? -Ret : Ret;
}
if ('A' <= C && C <= 'P') {
Ret = (Ret << 4) + (C - 'A');
continue;
}
break;
}
Error = true;
return 0;
}
void Demangler::memorizeString(StringView S) {
if (BackRefCount >= sizeof(BackReferences) / sizeof(*BackReferences))
return;
for (size_t i = 0; i < BackRefCount; ++i)
if (S == BackReferences[i])
return;
BackReferences[BackRefCount++] = S;
}
Name *Demangler::demangleBackRefName(StringView &MangledName) {
assert(startsWithDigit(MangledName));
size_t I = MangledName[0] - '0';
if (I >= BackRefCount) {
Error = true;
return nullptr;
}
MangledName = MangledName.dropFront();
Name *Node = Arena.alloc<Name>();
Node->Str = BackReferences[I];
return Node;
}
Name *Demangler::demangleClassTemplateName(StringView &MangledName) {
assert(MangledName.startsWith("?$"));
MangledName.consumeFront("?$");
Name *Node = demangleSimpleName(MangledName, false);
Node->TParams = demangleTemplateParameterList(MangledName);
OutputStream OS = OutputStream::create(nullptr, nullptr, 1024);
outputName(OS, Node);
OS << '\0';
char *Name = OS.getBuffer();
StringView Owned = copyString(Name);
memorizeString(Owned);
std::free(Name);
return Node;
}
Name *Demangler::demangleOperatorName(StringView &MangledName) {
assert(MangledName.startsWith('?'));
MangledName.consumeFront('?');
auto NameString = [this, &MangledName]() -> StringView {
switch (MangledName.popFront()) {
case '0':
return "ctor";
case '1':
return "dtor";
case '2':
return " new";
case '3':
return " delete";
case '4':
return "=";
case '5':
return ">>";
case '6':
return "<<";
case '7':
return "!";
case '8':
return "==";
case '9':
return "!=";
case 'A':
return "[]";
case 'C':
return "->";
case 'D':
return "*";
case 'E':
return "++";
case 'F':
return "--";
case 'G':
return "-";
case 'H':
return "+";
case 'I':
return "&";
case 'J':
return "->*";
case 'K':
return "/";
case 'L':
return "%";
case 'M':
return "<";
case 'N':
return "<=";
case 'O':
return ">";
case 'P':
return ">=";
case 'Q':
return ",";
case 'R':
return "()";
case 'S':
return "~";
case 'T':
return "^";
case 'U':
return "|";
case 'V':
return "&&";
case 'W':
return "||";
case 'X':
return "*=";
case 'Y':
return "+=";
case 'Z':
return "-=";
case '_': {
if (MangledName.empty())
break;
switch (MangledName.popFront()) {
case '0':
return "/=";
case '1':
return "%=";
case '2':
return ">>=";
case '3':
return "<<=";
case '4':
return "&=";
case '5':
return "|=";
case '6':
return "^=";
case 'U':
return " new[]";
case 'V':
return " delete[]";
case '_':
if (MangledName.consumeFront("L"))
return " co_await";
if (MangledName.consumeFront("K")) {
size_t EndPos = MangledName.find('@');
if (EndPos == StringView::npos)
break;
StringView OpName = demangleSimpleString(MangledName, false);
size_t FullSize = OpName.size() + 3;
char *Buffer = Arena.allocUnalignedBuffer(FullSize);
Buffer[0] = ' ';
Buffer[1] = '"';
Buffer[2] = '"';
std::memcpy(Buffer + 3, OpName.begin(), OpName.size());
return {Buffer, FullSize};
}
}
}
}
Error = true;
return "";
};
Name *Node = Arena.alloc<Name>();
Node->Operator = NameString();
return Node;
}
Name *Demangler::demangleSimpleName(StringView &MangledName, bool Memorize) {
StringView S = demangleSimpleString(MangledName, Memorize);
if (Error)
return nullptr;
Name *Node = Arena.alloc<Name>();
Node->Str = S;
return Node;
}
StringView Demangler::demangleSimpleString(StringView &MangledName,
bool Memorize) {
StringView S;
for (size_t i = 0; i < MangledName.size(); ++i) {
if (MangledName[i] != '@')
continue;
S = MangledName.substr(0, i);
MangledName = MangledName.dropFront(i + 1);
if (Memorize)
memorizeString(S);
return S;
}
Error = true;
return {};
}
Name *Demangler::demangleAnonymousNamespaceName(StringView &MangledName) {
assert(MangledName.startsWith("?A"));
MangledName.consumeFront("?A");
Name *Node = Arena.alloc<Name>();
Node->Str = "`anonymous namespace'";
if (MangledName.consumeFront('@'))
return Node;
Error = true;
return nullptr;
}
Name *Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) {
assert(startsWithLocalScopePattern(MangledName));
Name *Node = Arena.alloc<Name>();
MangledName.consumeFront('?');
int ScopeIdentifier = demangleNumber(MangledName);
MangledName.consumeFront('?');
assert(!Error);
Symbol *Scope = parse(MangledName);
if (Error)
return nullptr;
OutputStream OS = OutputStream::create(nullptr, nullptr, 1024);
OS << '`';
output(Scope, OS);
OS << '\'';
OS << "::`" << ScopeIdentifier << "'";
OS << '\0';
char *Result = OS.getBuffer();
Node->Str = copyString(Result);
std::free(Result);
return Node;
}
Name *Demangler::demangleFullyQualifiedTypeName(StringView &MangledName) {
Name *TypeName = demangleUnqualifiedTypeName(MangledName);
assert(TypeName);
Name *QualName = demangleNameScopeChain(MangledName, TypeName);
assert(QualName);
return QualName;
}
Name *Demangler::demangleFullyQualifiedSymbolName(StringView &MangledName) {
Name *SymbolName = demangleUnqualifiedSymbolName(MangledName);
assert(SymbolName);
Name *QualName = demangleNameScopeChain(MangledName, SymbolName);
assert(QualName);
return QualName;
}
Name *Demangler::demangleUnqualifiedTypeName(StringView &MangledName) {
if (startsWithDigit(MangledName))
return demangleBackRefName(MangledName);
if (MangledName.startsWith("?$"))
return demangleClassTemplateName(MangledName);
return demangleSimpleName(MangledName, true);
}
Name *Demangler::demangleUnqualifiedSymbolName(StringView &MangledName) {
if (startsWithDigit(MangledName))
return demangleBackRefName(MangledName);
if (MangledName.startsWith("?$"))
return demangleClassTemplateName(MangledName);
if (MangledName.startsWith('?'))
return demangleOperatorName(MangledName);
return demangleSimpleName(MangledName, true);
}
Name *Demangler::demangleNameScopePiece(StringView &MangledName) {
if (startsWithDigit(MangledName))
return demangleBackRefName(MangledName);
if (MangledName.startsWith("?$"))
return demangleClassTemplateName(MangledName);
if (MangledName.startsWith("?A"))
return demangleAnonymousNamespaceName(MangledName);
if (startsWithLocalScopePattern(MangledName))
return demangleLocallyScopedNamePiece(MangledName);
return demangleSimpleName(MangledName, true);
}
Name *Demangler::demangleNameScopeChain(StringView &MangledName,
Name *UnqualifiedName) {
Name *Head = UnqualifiedName;
while (!MangledName.consumeFront("@")) {
if (MangledName.empty()) {
Error = true;
return nullptr;
}
assert(!Error);
Name *Elem = demangleNameScopePiece(MangledName);
if (Error)
return nullptr;
Elem->Next = Head;
Head = Elem;
}
return Head;
}
FuncClass Demangler::demangleFunctionClass(StringView &MangledName) {
SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName);
RestoreOnError.shouldRestore(false);
switch (MangledName.popFront()) {
case 'A':
return Private;
case 'B':
return FuncClass(Private | Far);
case 'C':
return FuncClass(Private | Static);
case 'D':
return FuncClass(Private | Static);
case 'E':
return FuncClass(Private | Virtual);
case 'F':
return FuncClass(Private | Virtual);
case 'I':
return Protected;
case 'J':
return FuncClass(Protected | Far);
case 'K':
return FuncClass(Protected | Static);
case 'L':
return FuncClass(Protected | Static | Far);
case 'M':
return FuncClass(Protected | Virtual);
case 'N':
return FuncClass(Protected | Virtual | Far);
case 'Q':
return Public;
case 'R':
return FuncClass(Public | Far);
case 'S':
return FuncClass(Public | Static);
case 'T':
return FuncClass(Public | Static | Far);
case 'U':
return FuncClass(Public | Virtual);
case 'V':
return FuncClass(Public | Virtual | Far);
case 'Y':
return Global;
case 'Z':
return FuncClass(Global | Far);
}
Error = true;
RestoreOnError.shouldRestore(true);
return Public;
}
CallingConv Demangler::demangleCallingConvention(StringView &MangledName) {
switch (MangledName.popFront()) {
case 'A':
case 'B':
return CallingConv::Cdecl;
case 'C':
case 'D':
return CallingConv::Pascal;
case 'E':
case 'F':
return CallingConv::Thiscall;
case 'G':
case 'H':
return CallingConv::Stdcall;
case 'I':
case 'J':
return CallingConv::Fastcall;
case 'M':
case 'N':
return CallingConv::Clrcall;
case 'O':
case 'P':
return CallingConv::Eabi;
case 'Q':
return CallingConv::Vectorcall;
}
return CallingConv::None;
}
StorageClass Demangler::demangleVariableStorageClass(StringView &MangledName) {
assert(std::isdigit(MangledName.front()));
switch (MangledName.popFront()) {
case '0':
return StorageClass::PrivateStatic;
case '1':
return StorageClass::ProtectedStatic;
case '2':
return StorageClass::PublicStatic;
case '3':
return StorageClass::Global;
case '4':
return StorageClass::FunctionLocalStatic;
}
Error = true;
return StorageClass::None;
}
std::pair<Qualifiers, bool>
Demangler::demangleQualifiers(StringView &MangledName) {
switch (MangledName.popFront()) {
case 'Q':
return std::make_pair(Q_None, true);
case 'R':
return std::make_pair(Q_Const, true);
case 'S':
return std::make_pair(Q_Volatile, true);
case 'T':
return std::make_pair(Qualifiers(Q_Const | Q_Volatile), true);
case 'A':
return std::make_pair(Q_None, false);
case 'B':
return std::make_pair(Q_Const, false);
case 'C':
return std::make_pair(Q_Volatile, false);
case 'D':
return std::make_pair(Qualifiers(Q_Const | Q_Volatile), false);
}
Error = true;
return std::make_pair(Q_None, false);
}
static bool isTagType(StringView S) {
switch (S.front()) {
case 'T':
case 'U':
case 'V':
case 'W':
return true;
}
return false;
}
static bool isPointerType(StringView S) {
if (S.startsWith("$$Q"))
return true;
switch (S.front()) {
case 'A':
case 'P':
case 'Q':
case 'R':
case 'S':
return true;
}
return false;
}
static bool isArrayType(StringView S) { return S[0] == 'Y'; }
static bool isFunctionType(StringView S) {
return S.startsWith("$$A8@@") || S.startsWith("$$A6");
}
Type *Demangler::demangleType(StringView &MangledName,
QualifierMangleMode QMM) {
Qualifiers Quals = Q_None;
bool IsMember = false;
bool IsMemberKnown = false;
if (QMM == QualifierMangleMode::Mangle) {
std::tie(Quals, IsMember) = demangleQualifiers(MangledName);
IsMemberKnown = true;
} else if (QMM == QualifierMangleMode::Result) {
if (MangledName.consumeFront('?')) {
std::tie(Quals, IsMember) = demangleQualifiers(MangledName);
IsMemberKnown = true;
}
}
Type *Ty = nullptr;
if (isTagType(MangledName))
Ty = demangleClassType(MangledName);
else if (isPointerType(MangledName)) {
if (!IsMemberKnown)
IsMember = isMemberPointer(MangledName);
if (IsMember)
Ty = demangleMemberPointerType(MangledName);
else
Ty = demanglePointerType(MangledName);
} else if (isArrayType(MangledName))
Ty = demangleArrayType(MangledName);
else if (isFunctionType(MangledName)) {
if (MangledName.consumeFront("$$A8@@"))
Ty = demangleFunctionType(MangledName, true, false);
else {
assert(MangledName.startsWith("$$A6"));
MangledName.consumeFront("$$A6");
Ty = demangleFunctionType(MangledName, false, false);
}
} else {
Ty = demangleBasicType(MangledName);
assert(Ty && !Error);
if (!Ty || Error)
return Ty;
}
Ty->Quals = Qualifiers(Ty->Quals | Quals);
return Ty;
}
ReferenceKind Demangler::demangleReferenceKind(StringView &MangledName) {
if (MangledName.consumeFront('G'))
return ReferenceKind::LValueRef;
else if (MangledName.consumeFront('H'))
return ReferenceKind::RValueRef;
return ReferenceKind::None;
}
void Demangler::demangleThrowSpecification(StringView &MangledName) {
if (MangledName.consumeFront('Z'))
return;
Error = true;
}
FunctionType *Demangler::demangleFunctionType(StringView &MangledName,
bool HasThisQuals,
bool IsFunctionPointer) {
FunctionType *FTy = Arena.alloc<FunctionType>();
FTy->Prim = PrimTy::Function;
FTy->IsFunctionPointer = IsFunctionPointer;
if (HasThisQuals) {
FTy->Quals = demanglePointerExtQualifiers(MangledName);
FTy->RefKind = demangleReferenceKind(MangledName);
FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers(MangledName).first);
}
FTy->CallConvention = demangleCallingConvention(MangledName);
bool IsStructor = MangledName.consumeFront('@');
if (!IsStructor)
FTy->ReturnType = demangleType(MangledName, QualifierMangleMode::Result);
FTy->Params = demangleFunctionParameterList(MangledName);
demangleThrowSpecification(MangledName);
return FTy;
}
Type *Demangler::demangleFunctionEncoding(StringView &MangledName) {
FuncClass FC = demangleFunctionClass(MangledName);
bool HasThisQuals = !(FC & (Global | Static));
FunctionType *FTy = demangleFunctionType(MangledName, HasThisQuals, false);
FTy->FunctionClass = FC;
return FTy;
}
Type *Demangler::demangleBasicType(StringView &MangledName) {
Type *Ty = Arena.alloc<Type>();
if (MangledName.consumeFront("$$T")) {
Ty->Prim = PrimTy::Nullptr;
return Ty;
}
switch (MangledName.popFront()) {
case 'X':
Ty->Prim = PrimTy::Void;
break;
case 'D':
Ty->Prim = PrimTy::Char;
break;
case 'C':
Ty->Prim = PrimTy::Schar;
break;
case 'E':
Ty->Prim = PrimTy::Uchar;
break;
case 'F':
Ty->Prim = PrimTy::Short;
break;
case 'G':
Ty->Prim = PrimTy::Ushort;
break;
case 'H':
Ty->Prim = PrimTy::Int;
break;
case 'I':
Ty->Prim = PrimTy::Uint;
break;
case 'J':
Ty->Prim = PrimTy::Long;
break;
case 'K':
Ty->Prim = PrimTy::Ulong;
break;
case 'M':
Ty->Prim = PrimTy::Float;
break;
case 'N':
Ty->Prim = PrimTy::Double;
break;
case 'O':
Ty->Prim = PrimTy::Ldouble;
break;
case '_': {
if (MangledName.empty()) {
Error = true;
return nullptr;
}
switch (MangledName.popFront()) {
case 'N':
Ty->Prim = PrimTy::Bool;
break;
case 'J':
Ty->Prim = PrimTy::Int64;
break;
case 'K':
Ty->Prim = PrimTy::Uint64;
break;
case 'W':
Ty->Prim = PrimTy::Wchar;
break;
case 'S':
Ty->Prim = PrimTy::Char16;
break;
case 'U':
Ty->Prim = PrimTy::Char32;
break;
default:
Error = true;
return nullptr;
}
break;
}
default:
Error = true;
return nullptr;
}
return Ty;
}
UdtType *Demangler::demangleClassType(StringView &MangledName) {
UdtType *UTy = Arena.alloc<UdtType>();
switch (MangledName.popFront()) {
case 'T':
UTy->Prim = PrimTy::Union;
break;
case 'U':
UTy->Prim = PrimTy::Struct;
break;
case 'V':
UTy->Prim = PrimTy::Class;
break;
case 'W':
if (MangledName.popFront() != '4') {
Error = true;
return nullptr;
}
UTy->Prim = PrimTy::Enum;
break;
default:
assert(false);
}
UTy->UdtName = demangleFullyQualifiedTypeName(MangledName);
return UTy;
}
static std::pair<Qualifiers, PointerAffinity>
demanglePointerCVQualifiers(StringView &MangledName) {
if (MangledName.consumeFront("$$Q"))
return std::make_pair(Q_None, PointerAffinity::RValueReference);
switch (MangledName.popFront()) {
case 'A':
return std::make_pair(Q_None, PointerAffinity::Reference);
case 'P':
return std::make_pair(Q_None, PointerAffinity::Pointer);
case 'Q':
return std::make_pair(Q_Const, PointerAffinity::Pointer);
case 'R':
return std::make_pair(Q_Volatile, PointerAffinity::Pointer);
case 'S':
return std::make_pair(Qualifiers(Q_Const | Q_Volatile),
PointerAffinity::Pointer);
default:
assert(false && "Ty is not a pointer type!");
}
return std::make_pair(Q_None, PointerAffinity::Pointer);
}
PointerType *Demangler::demanglePointerType(StringView &MangledName) {
PointerType *Pointer = Arena.alloc<PointerType>();
std::tie(Pointer->Quals, Pointer->Affinity) =
demanglePointerCVQualifiers(MangledName);
Pointer->Prim = PrimTy::Ptr;
if (MangledName.consumeFront("6")) {
Pointer->Pointee = demangleFunctionType(MangledName, false, true);
return Pointer;
}
Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);
Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Mangle);
return Pointer;
}
MemberPointerType *
Demangler::demangleMemberPointerType(StringView &MangledName) {
MemberPointerType *Pointer = Arena.alloc<MemberPointerType>();
Pointer->Prim = PrimTy::MemberPtr;
PointerAffinity Affinity;
std::tie(Pointer->Quals, Affinity) = demanglePointerCVQualifiers(MangledName);
assert(Affinity == PointerAffinity::Pointer);
Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);
Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
if (MangledName.consumeFront("8")) {
Pointer->MemberName = demangleFullyQualifiedSymbolName(MangledName);
Pointer->Pointee = demangleFunctionType(MangledName, true, true);
} else {
Qualifiers PointeeQuals = Q_None;
bool IsMember = false;
std::tie(PointeeQuals, IsMember) = demangleQualifiers(MangledName);
assert(IsMember);
Pointer->MemberName = demangleFullyQualifiedSymbolName(MangledName);
Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Drop);
Pointer->Pointee->Quals = PointeeQuals;
}
return Pointer;
}
Qualifiers Demangler::demanglePointerExtQualifiers(StringView &MangledName) {
Qualifiers Quals = Q_None;
if (MangledName.consumeFront('E'))
Quals = Qualifiers(Quals | Q_Pointer64);
if (MangledName.consumeFront('I'))
Quals = Qualifiers(Quals | Q_Restrict);
if (MangledName.consumeFront('F'))
Quals = Qualifiers(Quals | Q_Unaligned);
return Quals;
}
ArrayType *Demangler::demangleArrayType(StringView &MangledName) {
assert(MangledName.front() == 'Y');
MangledName.popFront();
int Dimension = demangleNumber(MangledName);
if (Dimension <= 0) {
Error = true;
return nullptr;
}
ArrayType *ATy = Arena.alloc<ArrayType>();
ArrayType *Dim = ATy;
for (int I = 0; I < Dimension; ++I) {
Dim->Prim = PrimTy::Array;
Dim->ArrayDimension = demangleNumber(MangledName);
Dim->NextDimension = Arena.alloc<ArrayType>();
Dim = Dim->NextDimension;
}
if (MangledName.consumeFront("$$C")) {
if (MangledName.consumeFront("B"))
ATy->Quals = Q_Const;
else if (MangledName.consumeFront("C") || MangledName.consumeFront("D"))
ATy->Quals = Qualifiers(Q_Const | Q_Volatile);
else if (!MangledName.consumeFront("A"))
Error = true;
}
ATy->ElementType = demangleType(MangledName, QualifierMangleMode::Drop);
Dim->ElementType = ATy->ElementType;
return ATy;
}
FunctionParams
Demangler::demangleFunctionParameterList(StringView &MangledName) {
if (MangledName.consumeFront('X'))
return {};
FunctionParams *Head;
FunctionParams **Current = &Head;
while (!Error && !MangledName.startsWith('@') &&
!MangledName.startsWith('Z')) {
if (startsWithDigit(MangledName)) {
size_t N = MangledName[0] - '0';
if (N >= FunctionParamBackRefCount) {
Error = true;
return {};
}
MangledName = MangledName.dropFront();
*Current = Arena.alloc<FunctionParams>();
(*Current)->Current = FunctionParamBackRefs[N]->clone(Arena);
Current = &(*Current)->Next;
continue;
}
size_t OldSize = MangledName.size();
*Current = Arena.alloc<FunctionParams>();
(*Current)->Current = demangleType(MangledName, QualifierMangleMode::Drop);
size_t CharsConsumed = OldSize - MangledName.size();
assert(CharsConsumed != 0);
if (FunctionParamBackRefCount <= 9 && CharsConsumed > 1)
FunctionParamBackRefs[FunctionParamBackRefCount++] = (*Current)->Current;
Current = &(*Current)->Next;
}
if (Error)
return {};
if (MangledName.consumeFront('@'))
return *Head;
if (MangledName.consumeFront('Z')) {
Head->IsVariadic = true;
return *Head;
}
Error = true;
return {};
}
TemplateParams *
Demangler::demangleTemplateParameterList(StringView &MangledName) {
TemplateParams *Head;
TemplateParams **Current = &Head;
while (!Error && !MangledName.startsWith('@')) {
*Current = Arena.alloc<TemplateParams>();
if (MangledName.consumeFront("$S") || MangledName.consumeFront("$$V") ||
MangledName.consumeFront("$$$V")) {
if (!MangledName.startsWith('@'))
Error = true;
continue;
}
if (MangledName.consumeFront("$$Y")) {
(*Current)->IsTemplateTemplate = true;
(*Current)->IsAliasTemplate = true;
(*Current)->ParamName = demangleFullyQualifiedTypeName(MangledName);
} else if (MangledName.consumeFront("$1?")) {
(*Current)->ParamName = demangleFullyQualifiedSymbolName(MangledName);
(*Current)->ParamType = demangleFunctionEncoding(MangledName);
} else {
(*Current)->ParamType =
demangleType(MangledName, QualifierMangleMode::Drop);
}
Current = &(*Current)->Next;
}
if (Error)
return {};
if (MangledName.consumeFront('@'))
return Head;
Error = true;
return {};
}
void Demangler::output(const Symbol *S, OutputStream &OS) {
Type::outputPre(OS, *S->SymbolType);
outputName(OS, S->SymbolName);
Type::outputPost(OS, *S->SymbolType);
}
char *llvm::microsoftDemangle(const char *MangledName, char *Buf, size_t *N,
int *Status) {
Demangler D;
StringView Name{MangledName};
Symbol *S = D.parse(Name);
if (D.Error)
*Status = llvm::demangle_invalid_mangled_name;
else
*Status = llvm::demangle_success;
OutputStream OS = OutputStream::create(Buf, N, 1024);
D.output(S, OS);
OS << '\0';
return OS.getBuffer();
}