mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
43ee626c3c
to reflect the new license. These used slightly different spellings that defeated my regular expressions. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351648
2219 lines
70 KiB
C++
2219 lines
70 KiB
C++
//===- MicrosoftDemangle.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines a demangler for MSVC-style mangled symbols.
|
|
//
|
|
// This file has no dependencies on the rest of LLVM so that it can be
|
|
// easily reused in other programs such as libcxxabi.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Demangle/MicrosoftDemangle.h"
|
|
#include "llvm/Demangle/Demangle.h"
|
|
#include "llvm/Demangle/MicrosoftDemangleNodes.h"
|
|
|
|
#include "llvm/Demangle/DemangleConfig.h"
|
|
#include "llvm/Demangle/StringView.h"
|
|
#include "llvm/Demangle/Utility.h"
|
|
|
|
#include <array>
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
#include <tuple>
|
|
|
|
using namespace llvm;
|
|
using namespace ms_demangle;
|
|
|
|
static bool startsWithDigit(StringView S) {
|
|
return !S.empty() && std::isdigit(S.front());
|
|
}
|
|
|
|
|
|
struct NodeList {
|
|
Node *N = nullptr;
|
|
NodeList *Next = nullptr;
|
|
};
|
|
|
|
static bool isMemberPointer(StringView MangledName, bool &Error) {
|
|
Error = false;
|
|
switch (MangledName.popFront()) {
|
|
case '$':
|
|
// This is probably an rvalue reference (e.g. $$Q), and you cannot have an
|
|
// rvalue reference to a member.
|
|
return false;
|
|
case 'A':
|
|
// 'A' indicates a reference, and you cannot have a reference to a member
|
|
// function or member.
|
|
return false;
|
|
case 'P':
|
|
case 'Q':
|
|
case 'R':
|
|
case 'S':
|
|
// These 4 values indicate some kind of pointer, but we still don't know
|
|
// what.
|
|
break;
|
|
default:
|
|
Error = true;
|
|
return false;
|
|
}
|
|
|
|
// If it starts with a number, then 6 indicates a non-member function
|
|
// pointer, and 8 indicates a member function pointer.
|
|
if (startsWithDigit(MangledName)) {
|
|
assert(MangledName[0] == '6' || MangledName[0] == '8');
|
|
return (MangledName[0] == '8');
|
|
}
|
|
|
|
// Remove ext qualifiers since those can appear on either type and are
|
|
// therefore not indicative.
|
|
MangledName.consumeFront('E'); // 64-bit
|
|
MangledName.consumeFront('I'); // restrict
|
|
MangledName.consumeFront('F'); // unaligned
|
|
|
|
assert(!MangledName.empty());
|
|
|
|
// The next value should be either ABCD (non-member) or QRST (member).
|
|
switch (MangledName.front()) {
|
|
case 'A':
|
|
case 'B':
|
|
case 'C':
|
|
case 'D':
|
|
return false;
|
|
case 'Q':
|
|
case 'R':
|
|
case 'S':
|
|
case 'T':
|
|
return true;
|
|
default:
|
|
Error = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static SpecialIntrinsicKind
|
|
consumeSpecialIntrinsicKind(StringView &MangledName) {
|
|
if (MangledName.consumeFront("?_7"))
|
|
return SpecialIntrinsicKind::Vftable;
|
|
if (MangledName.consumeFront("?_8"))
|
|
return SpecialIntrinsicKind::Vbtable;
|
|
if (MangledName.consumeFront("?_9"))
|
|
return SpecialIntrinsicKind::VcallThunk;
|
|
if (MangledName.consumeFront("?_A"))
|
|
return SpecialIntrinsicKind::Typeof;
|
|
if (MangledName.consumeFront("?_B"))
|
|
return SpecialIntrinsicKind::LocalStaticGuard;
|
|
if (MangledName.consumeFront("?_C"))
|
|
return SpecialIntrinsicKind::StringLiteralSymbol;
|
|
if (MangledName.consumeFront("?_P"))
|
|
return SpecialIntrinsicKind::UdtReturning;
|
|
if (MangledName.consumeFront("?_R0"))
|
|
return SpecialIntrinsicKind::RttiTypeDescriptor;
|
|
if (MangledName.consumeFront("?_R1"))
|
|
return SpecialIntrinsicKind::RttiBaseClassDescriptor;
|
|
if (MangledName.consumeFront("?_R2"))
|
|
return SpecialIntrinsicKind::RttiBaseClassArray;
|
|
if (MangledName.consumeFront("?_R3"))
|
|
return SpecialIntrinsicKind::RttiClassHierarchyDescriptor;
|
|
if (MangledName.consumeFront("?_R4"))
|
|
return SpecialIntrinsicKind::RttiCompleteObjLocator;
|
|
if (MangledName.consumeFront("?_S"))
|
|
return SpecialIntrinsicKind::LocalVftable;
|
|
if (MangledName.consumeFront("?__E"))
|
|
return SpecialIntrinsicKind::DynamicInitializer;
|
|
if (MangledName.consumeFront("?__F"))
|
|
return SpecialIntrinsicKind::DynamicAtexitDestructor;
|
|
if (MangledName.consumeFront("?__J"))
|
|
return SpecialIntrinsicKind::LocalStaticThreadGuard;
|
|
return SpecialIntrinsicKind::None;
|
|
}
|
|
|
|
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;
|
|
|
|
// \?[0-9]\?
|
|
// ?@? is the discriminator 0.
|
|
if (Candidate.size() == 1)
|
|
return Candidate[0] == '@' || (Candidate[0] >= '0' && Candidate[0] <= '9');
|
|
|
|
// If it's not 0-9, then it's an encoded number terminated with an @
|
|
if (Candidate.back() != '@')
|
|
return false;
|
|
Candidate = Candidate.dropBack();
|
|
|
|
// An encoded number starts with B-P and all subsequent digits are in A-P.
|
|
// Note that the reason the first digit cannot be A is two fold. First, it
|
|
// would create an ambiguity with ?A which delimits the beginning of an
|
|
// anonymous namespace. Second, A represents 0, and you don't start a multi
|
|
// digit number with a leading 0. Presumably the anonymous namespace
|
|
// ambiguity is also why single digit encoded numbers use 0-9 rather than A-J.
|
|
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 bool isTagType(StringView S) {
|
|
switch (S.front()) {
|
|
case 'T': // union
|
|
case 'U': // struct
|
|
case 'V': // class
|
|
case 'W': // enum
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool isCustomType(StringView S) { return S[0] == '?'; }
|
|
|
|
static bool isPointerType(StringView S) {
|
|
if (S.startsWith("$$Q")) // foo &&
|
|
return true;
|
|
|
|
switch (S.front()) {
|
|
case 'A': // foo &
|
|
case 'P': // foo *
|
|
case 'Q': // foo *const
|
|
case 'R': // foo *volatile
|
|
case 'S': // foo *const volatile
|
|
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");
|
|
}
|
|
|
|
static FunctionRefQualifier
|
|
demangleFunctionRefQualifier(StringView &MangledName) {
|
|
if (MangledName.consumeFront('G'))
|
|
return FunctionRefQualifier::Reference;
|
|
else if (MangledName.consumeFront('H'))
|
|
return FunctionRefQualifier::RValueReference;
|
|
return FunctionRefQualifier::None;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
StringView Demangler::copyString(StringView Borrowed) {
|
|
char *Stable = Arena.allocUnalignedBuffer(Borrowed.size() + 1);
|
|
std::strcpy(Stable, Borrowed.begin());
|
|
|
|
return {Stable, Borrowed.size()};
|
|
}
|
|
|
|
SpecialTableSymbolNode *
|
|
Demangler::demangleSpecialTableSymbolNode(StringView &MangledName,
|
|
SpecialIntrinsicKind K) {
|
|
NamedIdentifierNode *NI = Arena.alloc<NamedIdentifierNode>();
|
|
switch (K) {
|
|
case SpecialIntrinsicKind::Vftable:
|
|
NI->Name = "`vftable'";
|
|
break;
|
|
case SpecialIntrinsicKind::Vbtable:
|
|
NI->Name = "`vbtable'";
|
|
break;
|
|
case SpecialIntrinsicKind::LocalVftable:
|
|
NI->Name = "`local vftable'";
|
|
break;
|
|
case SpecialIntrinsicKind::RttiCompleteObjLocator:
|
|
NI->Name = "`RTTI Complete Object Locator'";
|
|
break;
|
|
default:
|
|
DEMANGLE_UNREACHABLE;
|
|
}
|
|
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI);
|
|
SpecialTableSymbolNode *STSN = Arena.alloc<SpecialTableSymbolNode>();
|
|
STSN->Name = QN;
|
|
bool IsMember = false;
|
|
char Front = MangledName.popFront();
|
|
if (Front != '6' && Front != '7') {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
std::tie(STSN->Quals, IsMember) = demangleQualifiers(MangledName);
|
|
if (!MangledName.consumeFront('@'))
|
|
STSN->TargetName = demangleFullyQualifiedTypeName(MangledName);
|
|
return STSN;
|
|
}
|
|
|
|
LocalStaticGuardVariableNode *
|
|
Demangler::demangleLocalStaticGuard(StringView &MangledName) {
|
|
LocalStaticGuardIdentifierNode *LSGI =
|
|
Arena.alloc<LocalStaticGuardIdentifierNode>();
|
|
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, LSGI);
|
|
LocalStaticGuardVariableNode *LSGVN =
|
|
Arena.alloc<LocalStaticGuardVariableNode>();
|
|
LSGVN->Name = QN;
|
|
|
|
if (MangledName.consumeFront("4IA"))
|
|
LSGVN->IsVisible = false;
|
|
else if (MangledName.consumeFront("5"))
|
|
LSGVN->IsVisible = true;
|
|
else {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
if (!MangledName.empty())
|
|
LSGI->ScopeIndex = demangleUnsigned(MangledName);
|
|
return LSGVN;
|
|
}
|
|
|
|
static NamedIdentifierNode *synthesizeNamedIdentifier(ArenaAllocator &Arena,
|
|
StringView Name) {
|
|
NamedIdentifierNode *Id = Arena.alloc<NamedIdentifierNode>();
|
|
Id->Name = Name;
|
|
return Id;
|
|
}
|
|
|
|
static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena,
|
|
IdentifierNode *Identifier) {
|
|
QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>();
|
|
QN->Components = Arena.alloc<NodeArrayNode>();
|
|
QN->Components->Count = 1;
|
|
QN->Components->Nodes = Arena.allocArray<Node *>(1);
|
|
QN->Components->Nodes[0] = Identifier;
|
|
return QN;
|
|
}
|
|
|
|
static QualifiedNameNode *synthesizeQualifiedName(ArenaAllocator &Arena,
|
|
StringView Name) {
|
|
NamedIdentifierNode *Id = synthesizeNamedIdentifier(Arena, Name);
|
|
return synthesizeQualifiedName(Arena, Id);
|
|
}
|
|
|
|
static VariableSymbolNode *synthesizeVariable(ArenaAllocator &Arena,
|
|
TypeNode *Type,
|
|
StringView VariableName) {
|
|
VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
|
|
VSN->Type = Type;
|
|
VSN->Name = synthesizeQualifiedName(Arena, VariableName);
|
|
return VSN;
|
|
}
|
|
|
|
VariableSymbolNode *Demangler::demangleUntypedVariable(
|
|
ArenaAllocator &Arena, StringView &MangledName, StringView VariableName) {
|
|
NamedIdentifierNode *NI = synthesizeNamedIdentifier(Arena, VariableName);
|
|
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, NI);
|
|
VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
|
|
VSN->Name = QN;
|
|
if (MangledName.consumeFront("8"))
|
|
return VSN;
|
|
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
VariableSymbolNode *
|
|
Demangler::demangleRttiBaseClassDescriptorNode(ArenaAllocator &Arena,
|
|
StringView &MangledName) {
|
|
RttiBaseClassDescriptorNode *RBCDN =
|
|
Arena.alloc<RttiBaseClassDescriptorNode>();
|
|
RBCDN->NVOffset = demangleUnsigned(MangledName);
|
|
RBCDN->VBPtrOffset = demangleSigned(MangledName);
|
|
RBCDN->VBTableOffset = demangleUnsigned(MangledName);
|
|
RBCDN->Flags = demangleUnsigned(MangledName);
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
|
|
VSN->Name = demangleNameScopeChain(MangledName, RBCDN);
|
|
MangledName.consumeFront('8');
|
|
return VSN;
|
|
}
|
|
|
|
FunctionSymbolNode *Demangler::demangleInitFiniStub(StringView &MangledName,
|
|
bool IsDestructor) {
|
|
DynamicStructorIdentifierNode *DSIN =
|
|
Arena.alloc<DynamicStructorIdentifierNode>();
|
|
DSIN->IsDestructor = IsDestructor;
|
|
|
|
bool IsKnownStaticDataMember = false;
|
|
if (MangledName.consumeFront('?'))
|
|
IsKnownStaticDataMember = true;
|
|
|
|
QualifiedNameNode *QN = demangleFullyQualifiedSymbolName(MangledName);
|
|
|
|
SymbolNode *Symbol = demangleEncodedSymbol(MangledName, QN);
|
|
FunctionSymbolNode *FSN = nullptr;
|
|
Symbol->Name = QN;
|
|
|
|
if (Symbol->kind() == NodeKind::VariableSymbol) {
|
|
DSIN->Variable = static_cast<VariableSymbolNode *>(Symbol);
|
|
|
|
// Older versions of clang mangled this type of symbol incorrectly. They
|
|
// would omit the leading ? and they would only emit a single @ at the end.
|
|
// The correct mangling is a leading ? and 2 trailing @ signs. Handle
|
|
// both cases.
|
|
int AtCount = IsKnownStaticDataMember ? 2 : 1;
|
|
for (int I = 0; I < AtCount; ++I) {
|
|
if (MangledName.consumeFront('@'))
|
|
continue;
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
FSN = demangleFunctionEncoding(MangledName);
|
|
FSN->Name = synthesizeQualifiedName(Arena, DSIN);
|
|
} else {
|
|
if (IsKnownStaticDataMember) {
|
|
// This was supposed to be a static data member, but we got a function.
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
FSN = static_cast<FunctionSymbolNode *>(Symbol);
|
|
DSIN->Name = Symbol->Name;
|
|
FSN->Name = synthesizeQualifiedName(Arena, DSIN);
|
|
}
|
|
|
|
return FSN;
|
|
}
|
|
|
|
SymbolNode *Demangler::demangleSpecialIntrinsic(StringView &MangledName) {
|
|
SpecialIntrinsicKind SIK = consumeSpecialIntrinsicKind(MangledName);
|
|
if (SIK == SpecialIntrinsicKind::None)
|
|
return nullptr;
|
|
|
|
switch (SIK) {
|
|
case SpecialIntrinsicKind::StringLiteralSymbol:
|
|
return demangleStringLiteral(MangledName);
|
|
case SpecialIntrinsicKind::Vftable:
|
|
case SpecialIntrinsicKind::Vbtable:
|
|
case SpecialIntrinsicKind::LocalVftable:
|
|
case SpecialIntrinsicKind::RttiCompleteObjLocator:
|
|
return demangleSpecialTableSymbolNode(MangledName, SIK);
|
|
case SpecialIntrinsicKind::VcallThunk:
|
|
return demangleVcallThunkNode(MangledName);
|
|
case SpecialIntrinsicKind::LocalStaticGuard:
|
|
return demangleLocalStaticGuard(MangledName);
|
|
case SpecialIntrinsicKind::RttiTypeDescriptor: {
|
|
TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result);
|
|
if (Error)
|
|
break;
|
|
if (!MangledName.consumeFront("@8"))
|
|
break;
|
|
if (!MangledName.empty())
|
|
break;
|
|
return synthesizeVariable(Arena, T, "`RTTI Type Descriptor'");
|
|
}
|
|
case SpecialIntrinsicKind::RttiBaseClassArray:
|
|
return demangleUntypedVariable(Arena, MangledName,
|
|
"`RTTI Base Class Array'");
|
|
case SpecialIntrinsicKind::RttiClassHierarchyDescriptor:
|
|
return demangleUntypedVariable(Arena, MangledName,
|
|
"`RTTI Class Hierarchy Descriptor'");
|
|
case SpecialIntrinsicKind::RttiBaseClassDescriptor:
|
|
return demangleRttiBaseClassDescriptorNode(Arena, MangledName);
|
|
case SpecialIntrinsicKind::DynamicInitializer:
|
|
return demangleInitFiniStub(MangledName, false);
|
|
case SpecialIntrinsicKind::DynamicAtexitDestructor:
|
|
return demangleInitFiniStub(MangledName, true);
|
|
default:
|
|
break;
|
|
}
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
IdentifierNode *
|
|
Demangler::demangleFunctionIdentifierCode(StringView &MangledName) {
|
|
assert(MangledName.startsWith('?'));
|
|
MangledName = MangledName.dropFront();
|
|
|
|
if (MangledName.consumeFront("__"))
|
|
return demangleFunctionIdentifierCode(
|
|
MangledName, FunctionIdentifierCodeGroup::DoubleUnder);
|
|
else if (MangledName.consumeFront("_"))
|
|
return demangleFunctionIdentifierCode(MangledName,
|
|
FunctionIdentifierCodeGroup::Under);
|
|
return demangleFunctionIdentifierCode(MangledName,
|
|
FunctionIdentifierCodeGroup::Basic);
|
|
}
|
|
|
|
StructorIdentifierNode *
|
|
Demangler::demangleStructorIdentifier(StringView &MangledName,
|
|
bool IsDestructor) {
|
|
StructorIdentifierNode *N = Arena.alloc<StructorIdentifierNode>();
|
|
N->IsDestructor = IsDestructor;
|
|
return N;
|
|
}
|
|
|
|
ConversionOperatorIdentifierNode *
|
|
Demangler::demangleConversionOperatorIdentifier(StringView &MangledName) {
|
|
ConversionOperatorIdentifierNode *N =
|
|
Arena.alloc<ConversionOperatorIdentifierNode>();
|
|
return N;
|
|
}
|
|
|
|
LiteralOperatorIdentifierNode *
|
|
Demangler::demangleLiteralOperatorIdentifier(StringView &MangledName) {
|
|
LiteralOperatorIdentifierNode *N =
|
|
Arena.alloc<LiteralOperatorIdentifierNode>();
|
|
N->Name = demangleSimpleString(MangledName, false);
|
|
return N;
|
|
}
|
|
|
|
static IntrinsicFunctionKind
|
|
translateIntrinsicFunctionCode(char CH, FunctionIdentifierCodeGroup Group) {
|
|
// Not all ? identifiers are intrinsics *functions*. This function only maps
|
|
// operator codes for the special functions, all others are handled elsewhere,
|
|
// hence the IFK::None entries in the table.
|
|
using IFK = IntrinsicFunctionKind;
|
|
static IFK Basic[36] = {
|
|
IFK::None, // ?0 # Foo::Foo()
|
|
IFK::None, // ?1 # Foo::~Foo()
|
|
IFK::New, // ?2 # operator new
|
|
IFK::Delete, // ?3 # operator delete
|
|
IFK::Assign, // ?4 # operator=
|
|
IFK::RightShift, // ?5 # operator>>
|
|
IFK::LeftShift, // ?6 # operator<<
|
|
IFK::LogicalNot, // ?7 # operator!
|
|
IFK::Equals, // ?8 # operator==
|
|
IFK::NotEquals, // ?9 # operator!=
|
|
IFK::ArraySubscript, // ?A # operator[]
|
|
IFK::None, // ?B # Foo::operator <type>()
|
|
IFK::Pointer, // ?C # operator->
|
|
IFK::Dereference, // ?D # operator*
|
|
IFK::Increment, // ?E # operator++
|
|
IFK::Decrement, // ?F # operator--
|
|
IFK::Minus, // ?G # operator-
|
|
IFK::Plus, // ?H # operator+
|
|
IFK::BitwiseAnd, // ?I # operator&
|
|
IFK::MemberPointer, // ?J # operator->*
|
|
IFK::Divide, // ?K # operator/
|
|
IFK::Modulus, // ?L # operator%
|
|
IFK::LessThan, // ?M operator<
|
|
IFK::LessThanEqual, // ?N operator<=
|
|
IFK::GreaterThan, // ?O operator>
|
|
IFK::GreaterThanEqual, // ?P operator>=
|
|
IFK::Comma, // ?Q operator,
|
|
IFK::Parens, // ?R operator()
|
|
IFK::BitwiseNot, // ?S operator~
|
|
IFK::BitwiseXor, // ?T operator^
|
|
IFK::BitwiseOr, // ?U operator|
|
|
IFK::LogicalAnd, // ?V operator&&
|
|
IFK::LogicalOr, // ?W operator||
|
|
IFK::TimesEqual, // ?X operator*=
|
|
IFK::PlusEqual, // ?Y operator+=
|
|
IFK::MinusEqual, // ?Z operator-=
|
|
};
|
|
static IFK Under[36] = {
|
|
IFK::DivEqual, // ?_0 operator/=
|
|
IFK::ModEqual, // ?_1 operator%=
|
|
IFK::RshEqual, // ?_2 operator>>=
|
|
IFK::LshEqual, // ?_3 operator<<=
|
|
IFK::BitwiseAndEqual, // ?_4 operator&=
|
|
IFK::BitwiseOrEqual, // ?_5 operator|=
|
|
IFK::BitwiseXorEqual, // ?_6 operator^=
|
|
IFK::None, // ?_7 # vftable
|
|
IFK::None, // ?_8 # vbtable
|
|
IFK::None, // ?_9 # vcall
|
|
IFK::None, // ?_A # typeof
|
|
IFK::None, // ?_B # local static guard
|
|
IFK::None, // ?_C # string literal
|
|
IFK::VbaseDtor, // ?_D # vbase destructor
|
|
IFK::VecDelDtor, // ?_E # vector deleting destructor
|
|
IFK::DefaultCtorClosure, // ?_F # default constructor closure
|
|
IFK::ScalarDelDtor, // ?_G # scalar deleting destructor
|
|
IFK::VecCtorIter, // ?_H # vector constructor iterator
|
|
IFK::VecDtorIter, // ?_I # vector destructor iterator
|
|
IFK::VecVbaseCtorIter, // ?_J # vector vbase constructor iterator
|
|
IFK::VdispMap, // ?_K # virtual displacement map
|
|
IFK::EHVecCtorIter, // ?_L # eh vector constructor iterator
|
|
IFK::EHVecDtorIter, // ?_M # eh vector destructor iterator
|
|
IFK::EHVecVbaseCtorIter, // ?_N # eh vector vbase constructor iterator
|
|
IFK::CopyCtorClosure, // ?_O # copy constructor closure
|
|
IFK::None, // ?_P<name> # udt returning <name>
|
|
IFK::None, // ?_Q # <unknown>
|
|
IFK::None, // ?_R0 - ?_R4 # RTTI Codes
|
|
IFK::None, // ?_S # local vftable
|
|
IFK::LocalVftableCtorClosure, // ?_T # local vftable constructor closure
|
|
IFK::ArrayNew, // ?_U operator new[]
|
|
IFK::ArrayDelete, // ?_V operator delete[]
|
|
IFK::None, // ?_W <unused>
|
|
IFK::None, // ?_X <unused>
|
|
IFK::None, // ?_Y <unused>
|
|
IFK::None, // ?_Z <unused>
|
|
};
|
|
static IFK DoubleUnder[36] = {
|
|
IFK::None, // ?__0 <unused>
|
|
IFK::None, // ?__1 <unused>
|
|
IFK::None, // ?__2 <unused>
|
|
IFK::None, // ?__3 <unused>
|
|
IFK::None, // ?__4 <unused>
|
|
IFK::None, // ?__5 <unused>
|
|
IFK::None, // ?__6 <unused>
|
|
IFK::None, // ?__7 <unused>
|
|
IFK::None, // ?__8 <unused>
|
|
IFK::None, // ?__9 <unused>
|
|
IFK::ManVectorCtorIter, // ?__A managed vector ctor iterator
|
|
IFK::ManVectorDtorIter, // ?__B managed vector dtor iterator
|
|
IFK::EHVectorCopyCtorIter, // ?__C EH vector copy ctor iterator
|
|
IFK::EHVectorVbaseCopyCtorIter, // ?__D EH vector vbase copy ctor iter
|
|
IFK::None, // ?__E dynamic initializer for `T'
|
|
IFK::None, // ?__F dynamic atexit destructor for `T'
|
|
IFK::VectorCopyCtorIter, // ?__G vector copy constructor iter
|
|
IFK::VectorVbaseCopyCtorIter, // ?__H vector vbase copy ctor iter
|
|
IFK::ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy ctor
|
|
// iter
|
|
IFK::None, // ?__J local static thread guard
|
|
IFK::None, // ?__K operator ""_name
|
|
IFK::CoAwait, // ?__L co_await
|
|
IFK::None, // ?__M <unused>
|
|
IFK::None, // ?__N <unused>
|
|
IFK::None, // ?__O <unused>
|
|
IFK::None, // ?__P <unused>
|
|
IFK::None, // ?__Q <unused>
|
|
IFK::None, // ?__R <unused>
|
|
IFK::None, // ?__S <unused>
|
|
IFK::None, // ?__T <unused>
|
|
IFK::None, // ?__U <unused>
|
|
IFK::None, // ?__V <unused>
|
|
IFK::None, // ?__W <unused>
|
|
IFK::None, // ?__X <unused>
|
|
IFK::None, // ?__Y <unused>
|
|
IFK::None, // ?__Z <unused>
|
|
};
|
|
|
|
int Index = (CH >= '0' && CH <= '9') ? (CH - '0') : (CH - 'A' + 10);
|
|
switch (Group) {
|
|
case FunctionIdentifierCodeGroup::Basic:
|
|
return Basic[Index];
|
|
case FunctionIdentifierCodeGroup::Under:
|
|
return Under[Index];
|
|
case FunctionIdentifierCodeGroup::DoubleUnder:
|
|
return DoubleUnder[Index];
|
|
}
|
|
DEMANGLE_UNREACHABLE;
|
|
}
|
|
|
|
IdentifierNode *
|
|
Demangler::demangleFunctionIdentifierCode(StringView &MangledName,
|
|
FunctionIdentifierCodeGroup Group) {
|
|
switch (Group) {
|
|
case FunctionIdentifierCodeGroup::Basic:
|
|
switch (char CH = MangledName.popFront()) {
|
|
case '0':
|
|
case '1':
|
|
return demangleStructorIdentifier(MangledName, CH == '1');
|
|
case 'B':
|
|
return demangleConversionOperatorIdentifier(MangledName);
|
|
default:
|
|
return Arena.alloc<IntrinsicFunctionIdentifierNode>(
|
|
translateIntrinsicFunctionCode(CH, Group));
|
|
}
|
|
break;
|
|
case FunctionIdentifierCodeGroup::Under:
|
|
return Arena.alloc<IntrinsicFunctionIdentifierNode>(
|
|
translateIntrinsicFunctionCode(MangledName.popFront(), Group));
|
|
case FunctionIdentifierCodeGroup::DoubleUnder:
|
|
switch (char CH = MangledName.popFront()) {
|
|
case 'K':
|
|
return demangleLiteralOperatorIdentifier(MangledName);
|
|
default:
|
|
return Arena.alloc<IntrinsicFunctionIdentifierNode>(
|
|
translateIntrinsicFunctionCode(CH, Group));
|
|
}
|
|
}
|
|
// No Mangling Yet: Spaceship, // operator<=>
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
SymbolNode *Demangler::demangleEncodedSymbol(StringView &MangledName,
|
|
QualifiedNameNode *Name) {
|
|
// Read a variable.
|
|
switch (MangledName.front()) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4': {
|
|
StorageClass SC = demangleVariableStorageClass(MangledName);
|
|
return demangleVariableEncoding(MangledName, SC);
|
|
}
|
|
case '8':
|
|
return nullptr;
|
|
}
|
|
FunctionSymbolNode *FSN = demangleFunctionEncoding(MangledName);
|
|
|
|
IdentifierNode *UQN = Name->getUnqualifiedIdentifier();
|
|
if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) {
|
|
ConversionOperatorIdentifierNode *COIN =
|
|
static_cast<ConversionOperatorIdentifierNode *>(UQN);
|
|
COIN->TargetType = FSN->Signature->ReturnType;
|
|
}
|
|
return FSN;
|
|
}
|
|
|
|
// Parser entry point.
|
|
SymbolNode *Demangler::parse(StringView &MangledName) {
|
|
// We can't demangle MD5 names, just output them as-is.
|
|
// Also, MSVC-style mangled symbols must start with '?'.
|
|
if (MangledName.startsWith("??@")) {
|
|
// This is an MD5 mangled name. We can't demangle it, just return the
|
|
// mangled name.
|
|
SymbolNode *S = Arena.alloc<SymbolNode>(NodeKind::Md5Symbol);
|
|
S->Name = synthesizeQualifiedName(Arena, MangledName);
|
|
return S;
|
|
}
|
|
|
|
if (!MangledName.startsWith('?')) {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
MangledName.consumeFront('?');
|
|
|
|
// ?$ is a template instantiation, but all other names that start with ? are
|
|
// operators / special names.
|
|
if (SymbolNode *SI = demangleSpecialIntrinsic(MangledName))
|
|
return SI;
|
|
|
|
// What follows is a main symbol name. This may include namespaces or class
|
|
// back references.
|
|
QualifiedNameNode *QN = demangleFullyQualifiedSymbolName(MangledName);
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
SymbolNode *Symbol = demangleEncodedSymbol(MangledName, QN);
|
|
if (Symbol) {
|
|
Symbol->Name = QN;
|
|
}
|
|
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
return Symbol;
|
|
}
|
|
|
|
TagTypeNode *Demangler::parseTagUniqueName(StringView &MangledName) {
|
|
if (!MangledName.consumeFront(".?A"))
|
|
return nullptr;
|
|
MangledName.consumeFront(".?A");
|
|
if (MangledName.empty())
|
|
return nullptr;
|
|
|
|
return demangleClassType(MangledName);
|
|
}
|
|
|
|
// <type-encoding> ::= <storage-class> <variable-type>
|
|
// <storage-class> ::= 0 # private static member
|
|
// ::= 1 # protected static member
|
|
// ::= 2 # public static member
|
|
// ::= 3 # global
|
|
// ::= 4 # static local
|
|
|
|
VariableSymbolNode *Demangler::demangleVariableEncoding(StringView &MangledName,
|
|
StorageClass SC) {
|
|
VariableSymbolNode *VSN = Arena.alloc<VariableSymbolNode>();
|
|
|
|
VSN->Type = demangleType(MangledName, QualifierMangleMode::Drop);
|
|
VSN->SC = SC;
|
|
|
|
// <variable-type> ::= <type> <cvr-qualifiers>
|
|
// ::= <type> <pointee-cvr-qualifiers> # pointers, references
|
|
switch (VSN->Type->kind()) {
|
|
case NodeKind::PointerType: {
|
|
PointerTypeNode *PTN = static_cast<PointerTypeNode *>(VSN->Type);
|
|
|
|
Qualifiers ExtraChildQuals = Q_None;
|
|
PTN->Quals = Qualifiers(VSN->Type->Quals |
|
|
demanglePointerExtQualifiers(MangledName));
|
|
|
|
bool IsMember = false;
|
|
std::tie(ExtraChildQuals, IsMember) = demangleQualifiers(MangledName);
|
|
|
|
if (PTN->ClassParent) {
|
|
QualifiedNameNode *BackRefName =
|
|
demangleFullyQualifiedTypeName(MangledName);
|
|
(void)BackRefName;
|
|
}
|
|
PTN->Pointee->Quals = Qualifiers(PTN->Pointee->Quals | ExtraChildQuals);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
VSN->Type->Quals = demangleQualifiers(MangledName).first;
|
|
break;
|
|
}
|
|
|
|
return VSN;
|
|
}
|
|
|
|
// Sometimes numbers are encoded in mangled symbols. For example,
|
|
// "int (*x)[20]" is a valid C type (x is a pointer to an array of
|
|
// length 20), so we need some way to embed numbers as part of symbols.
|
|
// This function parses it.
|
|
//
|
|
// <number> ::= [?] <non-negative integer>
|
|
//
|
|
// <non-negative integer> ::= <decimal digit> # when 1 <= Number <= 10
|
|
// ::= <hex digit>+ @ # when Numbrer == 0 or >= 10
|
|
//
|
|
// <hex-digit> ::= [A-P] # A = 0, B = 1, ...
|
|
std::pair<uint64_t, bool> Demangler::demangleNumber(StringView &MangledName) {
|
|
bool IsNegative = MangledName.consumeFront('?');
|
|
|
|
if (startsWithDigit(MangledName)) {
|
|
uint64_t Ret = MangledName[0] - '0' + 1;
|
|
MangledName = MangledName.dropFront(1);
|
|
return {Ret, IsNegative};
|
|
}
|
|
|
|
uint64_t Ret = 0;
|
|
for (size_t i = 0; i < MangledName.size(); ++i) {
|
|
char C = MangledName[i];
|
|
if (C == '@') {
|
|
MangledName = MangledName.dropFront(i + 1);
|
|
return {Ret, IsNegative};
|
|
}
|
|
if ('A' <= C && C <= 'P') {
|
|
Ret = (Ret << 4) + (C - 'A');
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
Error = true;
|
|
return {0ULL, false};
|
|
}
|
|
|
|
uint64_t Demangler::demangleUnsigned(StringView &MangledName) {
|
|
bool IsNegative = false;
|
|
uint64_t Number = 0;
|
|
std::tie(Number, IsNegative) = demangleNumber(MangledName);
|
|
if (IsNegative)
|
|
Error = true;
|
|
return Number;
|
|
}
|
|
|
|
int64_t Demangler::demangleSigned(StringView &MangledName) {
|
|
bool IsNegative = false;
|
|
uint64_t Number = 0;
|
|
std::tie(Number, IsNegative) = demangleNumber(MangledName);
|
|
if (Number > INT64_MAX)
|
|
Error = true;
|
|
int64_t I = static_cast<int64_t>(Number);
|
|
return IsNegative ? -I : I;
|
|
}
|
|
|
|
// First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9.
|
|
// Memorize it.
|
|
void Demangler::memorizeString(StringView S) {
|
|
if (Backrefs.NamesCount >= BackrefContext::Max)
|
|
return;
|
|
for (size_t i = 0; i < Backrefs.NamesCount; ++i)
|
|
if (S == Backrefs.Names[i]->Name)
|
|
return;
|
|
NamedIdentifierNode *N = Arena.alloc<NamedIdentifierNode>();
|
|
N->Name = S;
|
|
Backrefs.Names[Backrefs.NamesCount++] = N;
|
|
}
|
|
|
|
NamedIdentifierNode *Demangler::demangleBackRefName(StringView &MangledName) {
|
|
assert(startsWithDigit(MangledName));
|
|
|
|
size_t I = MangledName[0] - '0';
|
|
if (I >= Backrefs.NamesCount) {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
MangledName = MangledName.dropFront();
|
|
return Backrefs.Names[I];
|
|
}
|
|
|
|
void Demangler::memorizeIdentifier(IdentifierNode *Identifier) {
|
|
// Render this class template name into a string buffer so that we can
|
|
// memorize it for the purpose of back-referencing.
|
|
OutputStream OS;
|
|
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
|
|
// FIXME: Propagate out-of-memory as an error?
|
|
std::terminate();
|
|
Identifier->output(OS, OF_Default);
|
|
OS << '\0';
|
|
char *Name = OS.getBuffer();
|
|
|
|
StringView Owned = copyString(Name);
|
|
memorizeString(Owned);
|
|
std::free(Name);
|
|
}
|
|
|
|
IdentifierNode *
|
|
Demangler::demangleTemplateInstantiationName(StringView &MangledName,
|
|
NameBackrefBehavior NBB) {
|
|
assert(MangledName.startsWith("?$"));
|
|
MangledName.consumeFront("?$");
|
|
|
|
BackrefContext OuterContext;
|
|
std::swap(OuterContext, Backrefs);
|
|
|
|
IdentifierNode *Identifier =
|
|
demangleUnqualifiedSymbolName(MangledName, NBB_Simple);
|
|
if (!Error)
|
|
Identifier->TemplateParams = demangleTemplateParameterList(MangledName);
|
|
|
|
std::swap(OuterContext, Backrefs);
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
if (NBB & NBB_Template)
|
|
memorizeIdentifier(Identifier);
|
|
|
|
return Identifier;
|
|
}
|
|
|
|
NamedIdentifierNode *Demangler::demangleSimpleName(StringView &MangledName,
|
|
bool Memorize) {
|
|
StringView S = demangleSimpleString(MangledName, Memorize);
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
NamedIdentifierNode *Name = Arena.alloc<NamedIdentifierNode>();
|
|
Name->Name = S;
|
|
return Name;
|
|
}
|
|
|
|
static bool isRebasedHexDigit(char C) { return (C >= 'A' && C <= 'P'); }
|
|
|
|
static uint8_t rebasedHexDigitToNumber(char C) {
|
|
assert(isRebasedHexDigit(C));
|
|
return (C <= 'J') ? (C - 'A') : (10 + C - 'K');
|
|
}
|
|
|
|
uint8_t Demangler::demangleCharLiteral(StringView &MangledName) {
|
|
if (!MangledName.startsWith('?'))
|
|
return MangledName.popFront();
|
|
|
|
MangledName = MangledName.dropFront();
|
|
if (MangledName.empty())
|
|
goto CharLiteralError;
|
|
|
|
if (MangledName.consumeFront('$')) {
|
|
// Two hex digits
|
|
if (MangledName.size() < 2)
|
|
goto CharLiteralError;
|
|
StringView Nibbles = MangledName.substr(0, 2);
|
|
if (!isRebasedHexDigit(Nibbles[0]) || !isRebasedHexDigit(Nibbles[1]))
|
|
goto CharLiteralError;
|
|
// Don't append the null terminator.
|
|
uint8_t C1 = rebasedHexDigitToNumber(Nibbles[0]);
|
|
uint8_t C2 = rebasedHexDigitToNumber(Nibbles[1]);
|
|
MangledName = MangledName.dropFront(2);
|
|
return (C1 << 4) | C2;
|
|
}
|
|
|
|
if (startsWithDigit(MangledName)) {
|
|
const char *Lookup = ",/\\:. \n\t'-";
|
|
char C = Lookup[MangledName[0] - '0'];
|
|
MangledName = MangledName.dropFront();
|
|
return C;
|
|
}
|
|
|
|
if (MangledName[0] >= 'a' && MangledName[0] <= 'z') {
|
|
char Lookup[26] = {'\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7',
|
|
'\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE',
|
|
'\xEF', '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5',
|
|
'\xF6', '\xF7', '\xF8', '\xF9', '\xFA'};
|
|
char C = Lookup[MangledName[0] - 'a'];
|
|
MangledName = MangledName.dropFront();
|
|
return C;
|
|
}
|
|
|
|
if (MangledName[0] >= 'A' && MangledName[0] <= 'Z') {
|
|
char Lookup[26] = {'\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7',
|
|
'\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE',
|
|
'\xCF', '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5',
|
|
'\xD6', '\xD7', '\xD8', '\xD9', '\xDA'};
|
|
char C = Lookup[MangledName[0] - 'A'];
|
|
MangledName = MangledName.dropFront();
|
|
return C;
|
|
}
|
|
|
|
CharLiteralError:
|
|
Error = true;
|
|
return '\0';
|
|
}
|
|
|
|
wchar_t Demangler::demangleWcharLiteral(StringView &MangledName) {
|
|
uint8_t C1, C2;
|
|
|
|
C1 = demangleCharLiteral(MangledName);
|
|
if (Error)
|
|
goto WCharLiteralError;
|
|
C2 = demangleCharLiteral(MangledName);
|
|
if (Error)
|
|
goto WCharLiteralError;
|
|
|
|
return ((wchar_t)C1 << 8) | (wchar_t)C2;
|
|
|
|
WCharLiteralError:
|
|
Error = true;
|
|
return L'\0';
|
|
}
|
|
|
|
static void writeHexDigit(char *Buffer, uint8_t Digit) {
|
|
assert(Digit <= 15);
|
|
*Buffer = (Digit < 10) ? ('0' + Digit) : ('A' + Digit - 10);
|
|
}
|
|
|
|
static void outputHex(OutputStream &OS, unsigned C) {
|
|
if (C == 0) {
|
|
OS << "\\x00";
|
|
return;
|
|
}
|
|
// It's easier to do the math if we can work from right to left, but we need
|
|
// to print the numbers from left to right. So render this into a temporary
|
|
// buffer first, then output the temporary buffer. Each byte is of the form
|
|
// \xAB, which means that each byte needs 4 characters. Since there are at
|
|
// most 4 bytes, we need a 4*4+1 = 17 character temporary buffer.
|
|
char TempBuffer[17];
|
|
|
|
::memset(TempBuffer, 0, sizeof(TempBuffer));
|
|
constexpr int MaxPos = 15;
|
|
|
|
int Pos = MaxPos - 1;
|
|
while (C != 0) {
|
|
for (int I = 0; I < 2; ++I) {
|
|
writeHexDigit(&TempBuffer[Pos--], C % 16);
|
|
C /= 16;
|
|
}
|
|
TempBuffer[Pos--] = 'x';
|
|
TempBuffer[Pos--] = '\\';
|
|
assert(Pos >= 0);
|
|
}
|
|
OS << StringView(&TempBuffer[Pos + 1]);
|
|
}
|
|
|
|
static void outputEscapedChar(OutputStream &OS, unsigned C) {
|
|
switch (C) {
|
|
case '\'': // single quote
|
|
OS << "\\\'";
|
|
return;
|
|
case '\"': // double quote
|
|
OS << "\\\"";
|
|
return;
|
|
case '\\': // backslash
|
|
OS << "\\\\";
|
|
return;
|
|
case '\a': // bell
|
|
OS << "\\a";
|
|
return;
|
|
case '\b': // backspace
|
|
OS << "\\b";
|
|
return;
|
|
case '\f': // form feed
|
|
OS << "\\f";
|
|
return;
|
|
case '\n': // new line
|
|
OS << "\\n";
|
|
return;
|
|
case '\r': // carriage return
|
|
OS << "\\r";
|
|
return;
|
|
case '\t': // tab
|
|
OS << "\\t";
|
|
return;
|
|
case '\v': // vertical tab
|
|
OS << "\\v";
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (C > 0x1F && C < 0x7F) {
|
|
// Standard ascii char.
|
|
OS << (char)C;
|
|
return;
|
|
}
|
|
|
|
outputHex(OS, C);
|
|
}
|
|
|
|
static unsigned countTrailingNullBytes(const uint8_t *StringBytes, int Length) {
|
|
const uint8_t *End = StringBytes + Length - 1;
|
|
unsigned Count = 0;
|
|
while (Length > 0 && *End == 0) {
|
|
--Length;
|
|
--End;
|
|
++Count;
|
|
}
|
|
return Count;
|
|
}
|
|
|
|
static unsigned countEmbeddedNulls(const uint8_t *StringBytes,
|
|
unsigned Length) {
|
|
unsigned Result = 0;
|
|
for (unsigned I = 0; I < Length; ++I) {
|
|
if (*StringBytes++ == 0)
|
|
++Result;
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
static unsigned guessCharByteSize(const uint8_t *StringBytes, unsigned NumChars,
|
|
unsigned NumBytes) {
|
|
assert(NumBytes > 0);
|
|
|
|
// If the number of bytes is odd, this is guaranteed to be a char string.
|
|
if (NumBytes % 2 == 1)
|
|
return 1;
|
|
|
|
// All strings can encode at most 32 bytes of data. If it's less than that,
|
|
// then we encoded the entire string. In this case we check for a 1-byte,
|
|
// 2-byte, or 4-byte null terminator.
|
|
if (NumBytes < 32) {
|
|
unsigned TrailingNulls = countTrailingNullBytes(StringBytes, NumChars);
|
|
if (TrailingNulls >= 4)
|
|
return 4;
|
|
if (TrailingNulls >= 2)
|
|
return 2;
|
|
return 1;
|
|
}
|
|
|
|
// The whole string was not able to be encoded. Try to look at embedded null
|
|
// terminators to guess. The heuristic is that we count all embedded null
|
|
// terminators. If more than 2/3 are null, it's a char32. If more than 1/3
|
|
// are null, it's a char16. Otherwise it's a char8. This obviously isn't
|
|
// perfect and is biased towards languages that have ascii alphabets, but this
|
|
// was always going to be best effort since the encoding is lossy.
|
|
unsigned Nulls = countEmbeddedNulls(StringBytes, NumChars);
|
|
if (Nulls >= 2 * NumChars / 3)
|
|
return 4;
|
|
if (Nulls >= NumChars / 3)
|
|
return 2;
|
|
return 1;
|
|
}
|
|
|
|
static unsigned decodeMultiByteChar(const uint8_t *StringBytes,
|
|
unsigned CharIndex, unsigned CharBytes) {
|
|
assert(CharBytes == 1 || CharBytes == 2 || CharBytes == 4);
|
|
unsigned Offset = CharIndex * CharBytes;
|
|
unsigned Result = 0;
|
|
StringBytes = StringBytes + Offset;
|
|
for (unsigned I = 0; I < CharBytes; ++I) {
|
|
unsigned C = static_cast<unsigned>(StringBytes[I]);
|
|
Result |= C << (8 * I);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
FunctionSymbolNode *Demangler::demangleVcallThunkNode(StringView &MangledName) {
|
|
FunctionSymbolNode *FSN = Arena.alloc<FunctionSymbolNode>();
|
|
VcallThunkIdentifierNode *VTIN = Arena.alloc<VcallThunkIdentifierNode>();
|
|
FSN->Signature = Arena.alloc<ThunkSignatureNode>();
|
|
FSN->Signature->FunctionClass = FC_NoParameterList;
|
|
|
|
FSN->Name = demangleNameScopeChain(MangledName, VTIN);
|
|
if (!Error)
|
|
Error = !MangledName.consumeFront("$B");
|
|
if (!Error)
|
|
VTIN->OffsetInVTable = demangleUnsigned(MangledName);
|
|
if (!Error)
|
|
Error = !MangledName.consumeFront('A');
|
|
if (!Error)
|
|
FSN->Signature->CallConvention = demangleCallingConvention(MangledName);
|
|
return (Error) ? nullptr : FSN;
|
|
}
|
|
|
|
EncodedStringLiteralNode *
|
|
Demangler::demangleStringLiteral(StringView &MangledName) {
|
|
// This function uses goto, so declare all variables up front.
|
|
OutputStream OS;
|
|
StringView CRC;
|
|
uint64_t StringByteSize;
|
|
bool IsWcharT = false;
|
|
bool IsNegative = false;
|
|
size_t CrcEndPos = 0;
|
|
char *ResultBuffer = nullptr;
|
|
|
|
EncodedStringLiteralNode *Result = Arena.alloc<EncodedStringLiteralNode>();
|
|
|
|
// Prefix indicating the beginning of a string literal
|
|
if (!MangledName.consumeFront("@_"))
|
|
goto StringLiteralError;
|
|
if (MangledName.empty())
|
|
goto StringLiteralError;
|
|
|
|
// Char Type (regular or wchar_t)
|
|
switch (MangledName.popFront()) {
|
|
case '1':
|
|
IsWcharT = true;
|
|
DEMANGLE_FALLTHROUGH;
|
|
case '0':
|
|
break;
|
|
default:
|
|
goto StringLiteralError;
|
|
}
|
|
|
|
// Encoded Length
|
|
std::tie(StringByteSize, IsNegative) = demangleNumber(MangledName);
|
|
if (Error || IsNegative)
|
|
goto StringLiteralError;
|
|
|
|
// CRC 32 (always 8 characters plus a terminator)
|
|
CrcEndPos = MangledName.find('@');
|
|
if (CrcEndPos == StringView::npos)
|
|
goto StringLiteralError;
|
|
CRC = MangledName.substr(0, CrcEndPos);
|
|
MangledName = MangledName.dropFront(CrcEndPos + 1);
|
|
if (MangledName.empty())
|
|
goto StringLiteralError;
|
|
|
|
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
|
|
// FIXME: Propagate out-of-memory as an error?
|
|
std::terminate();
|
|
if (IsWcharT) {
|
|
Result->Char = CharKind::Wchar;
|
|
if (StringByteSize > 64)
|
|
Result->IsTruncated = true;
|
|
|
|
while (!MangledName.consumeFront('@')) {
|
|
assert(StringByteSize >= 2);
|
|
wchar_t W = demangleWcharLiteral(MangledName);
|
|
if (StringByteSize != 2 || Result->IsTruncated)
|
|
outputEscapedChar(OS, W);
|
|
StringByteSize -= 2;
|
|
if (Error)
|
|
goto StringLiteralError;
|
|
}
|
|
} else {
|
|
// The max byte length is actually 32, but some compilers mangled strings
|
|
// incorrectly, so we have to assume it can go higher.
|
|
constexpr unsigned MaxStringByteLength = 32 * 4;
|
|
uint8_t StringBytes[MaxStringByteLength];
|
|
|
|
unsigned BytesDecoded = 0;
|
|
while (!MangledName.consumeFront('@')) {
|
|
assert(StringByteSize >= 1);
|
|
StringBytes[BytesDecoded++] = demangleCharLiteral(MangledName);
|
|
}
|
|
|
|
if (StringByteSize > BytesDecoded)
|
|
Result->IsTruncated = true;
|
|
|
|
unsigned CharBytes =
|
|
guessCharByteSize(StringBytes, BytesDecoded, StringByteSize);
|
|
assert(StringByteSize % CharBytes == 0);
|
|
switch (CharBytes) {
|
|
case 1:
|
|
Result->Char = CharKind::Char;
|
|
break;
|
|
case 2:
|
|
Result->Char = CharKind::Char16;
|
|
break;
|
|
case 4:
|
|
Result->Char = CharKind::Char32;
|
|
break;
|
|
default:
|
|
DEMANGLE_UNREACHABLE;
|
|
}
|
|
const unsigned NumChars = BytesDecoded / CharBytes;
|
|
for (unsigned CharIndex = 0; CharIndex < NumChars; ++CharIndex) {
|
|
unsigned NextChar =
|
|
decodeMultiByteChar(StringBytes, CharIndex, CharBytes);
|
|
if (CharIndex + 1 < NumChars || Result->IsTruncated)
|
|
outputEscapedChar(OS, NextChar);
|
|
}
|
|
}
|
|
|
|
OS << '\0';
|
|
ResultBuffer = OS.getBuffer();
|
|
Result->DecodedString = copyString(ResultBuffer);
|
|
std::free(ResultBuffer);
|
|
return Result;
|
|
|
|
StringLiteralError:
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
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 {};
|
|
}
|
|
|
|
NamedIdentifierNode *
|
|
Demangler::demangleAnonymousNamespaceName(StringView &MangledName) {
|
|
assert(MangledName.startsWith("?A"));
|
|
MangledName.consumeFront("?A");
|
|
|
|
NamedIdentifierNode *Node = Arena.alloc<NamedIdentifierNode>();
|
|
Node->Name = "`anonymous namespace'";
|
|
size_t EndPos = MangledName.find('@');
|
|
if (EndPos == StringView::npos) {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
StringView NamespaceKey = MangledName.substr(0, EndPos);
|
|
memorizeString(NamespaceKey);
|
|
MangledName = MangledName.substr(EndPos + 1);
|
|
return Node;
|
|
}
|
|
|
|
NamedIdentifierNode *
|
|
Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) {
|
|
assert(startsWithLocalScopePattern(MangledName));
|
|
|
|
NamedIdentifierNode *Identifier = Arena.alloc<NamedIdentifierNode>();
|
|
MangledName.consumeFront('?');
|
|
auto Number = demangleNumber(MangledName);
|
|
assert(!Number.second);
|
|
|
|
// One ? to terminate the number
|
|
MangledName.consumeFront('?');
|
|
|
|
assert(!Error);
|
|
Node *Scope = parse(MangledName);
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
// Render the parent symbol's name into a buffer.
|
|
OutputStream OS;
|
|
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
|
|
// FIXME: Propagate out-of-memory as an error?
|
|
std::terminate();
|
|
OS << '`';
|
|
Scope->output(OS, OF_Default);
|
|
OS << '\'';
|
|
OS << "::`" << Number.first << "'";
|
|
OS << '\0';
|
|
char *Result = OS.getBuffer();
|
|
Identifier->Name = copyString(Result);
|
|
std::free(Result);
|
|
return Identifier;
|
|
}
|
|
|
|
// Parses a type name in the form of A@B@C@@ which represents C::B::A.
|
|
QualifiedNameNode *
|
|
Demangler::demangleFullyQualifiedTypeName(StringView &MangledName) {
|
|
IdentifierNode *Identifier = demangleUnqualifiedTypeName(MangledName, true);
|
|
if (Error)
|
|
return nullptr;
|
|
assert(Identifier);
|
|
|
|
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier);
|
|
if (Error)
|
|
return nullptr;
|
|
assert(QN);
|
|
return QN;
|
|
}
|
|
|
|
// Parses a symbol name in the form of A@B@C@@ which represents C::B::A.
|
|
// Symbol names have slightly different rules regarding what can appear
|
|
// so we separate out the implementations for flexibility.
|
|
QualifiedNameNode *
|
|
Demangler::demangleFullyQualifiedSymbolName(StringView &MangledName) {
|
|
// This is the final component of a symbol name (i.e. the leftmost component
|
|
// of a mangled name. Since the only possible template instantiation that
|
|
// can appear in this context is a function template, and since those are
|
|
// not saved for the purposes of name backreferences, only backref simple
|
|
// names.
|
|
IdentifierNode *Identifier =
|
|
demangleUnqualifiedSymbolName(MangledName, NBB_Simple);
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
QualifiedNameNode *QN = demangleNameScopeChain(MangledName, Identifier);
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
if (Identifier->kind() == NodeKind::StructorIdentifier) {
|
|
StructorIdentifierNode *SIN =
|
|
static_cast<StructorIdentifierNode *>(Identifier);
|
|
assert(QN->Components->Count >= 2);
|
|
Node *ClassNode = QN->Components->Nodes[QN->Components->Count - 2];
|
|
SIN->Class = static_cast<IdentifierNode *>(ClassNode);
|
|
}
|
|
assert(QN);
|
|
return QN;
|
|
}
|
|
|
|
IdentifierNode *Demangler::demangleUnqualifiedTypeName(StringView &MangledName,
|
|
bool Memorize) {
|
|
// An inner-most name can be a back-reference, because a fully-qualified name
|
|
// (e.g. Scope + Inner) can contain other fully qualified names inside of
|
|
// them (for example template parameters), and these nested parameters can
|
|
// refer to previously mangled types.
|
|
if (startsWithDigit(MangledName))
|
|
return demangleBackRefName(MangledName);
|
|
|
|
if (MangledName.startsWith("?$"))
|
|
return demangleTemplateInstantiationName(MangledName, NBB_Template);
|
|
|
|
return demangleSimpleName(MangledName, Memorize);
|
|
}
|
|
|
|
IdentifierNode *
|
|
Demangler::demangleUnqualifiedSymbolName(StringView &MangledName,
|
|
NameBackrefBehavior NBB) {
|
|
if (startsWithDigit(MangledName))
|
|
return demangleBackRefName(MangledName);
|
|
if (MangledName.startsWith("?$"))
|
|
return demangleTemplateInstantiationName(MangledName, NBB);
|
|
if (MangledName.startsWith('?'))
|
|
return demangleFunctionIdentifierCode(MangledName);
|
|
return demangleSimpleName(MangledName, (NBB & NBB_Simple) != 0);
|
|
}
|
|
|
|
IdentifierNode *Demangler::demangleNameScopePiece(StringView &MangledName) {
|
|
if (startsWithDigit(MangledName))
|
|
return demangleBackRefName(MangledName);
|
|
|
|
if (MangledName.startsWith("?$"))
|
|
return demangleTemplateInstantiationName(MangledName, NBB_Template);
|
|
|
|
if (MangledName.startsWith("?A"))
|
|
return demangleAnonymousNamespaceName(MangledName);
|
|
|
|
if (startsWithLocalScopePattern(MangledName))
|
|
return demangleLocallyScopedNamePiece(MangledName);
|
|
|
|
return demangleSimpleName(MangledName, true);
|
|
}
|
|
|
|
static NodeArrayNode *nodeListToNodeArray(ArenaAllocator &Arena, NodeList *Head,
|
|
size_t Count) {
|
|
NodeArrayNode *N = Arena.alloc<NodeArrayNode>();
|
|
N->Count = Count;
|
|
N->Nodes = Arena.allocArray<Node *>(Count);
|
|
for (size_t I = 0; I < Count; ++I) {
|
|
N->Nodes[I] = Head->N;
|
|
Head = Head->Next;
|
|
}
|
|
return N;
|
|
}
|
|
|
|
QualifiedNameNode *
|
|
Demangler::demangleNameScopeChain(StringView &MangledName,
|
|
IdentifierNode *UnqualifiedName) {
|
|
NodeList *Head = Arena.alloc<NodeList>();
|
|
|
|
Head->N = UnqualifiedName;
|
|
|
|
size_t Count = 1;
|
|
while (!MangledName.consumeFront("@")) {
|
|
++Count;
|
|
NodeList *NewHead = Arena.alloc<NodeList>();
|
|
NewHead->Next = Head;
|
|
Head = NewHead;
|
|
|
|
if (MangledName.empty()) {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
assert(!Error);
|
|
IdentifierNode *Elem = demangleNameScopePiece(MangledName);
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
Head->N = Elem;
|
|
}
|
|
|
|
QualifiedNameNode *QN = Arena.alloc<QualifiedNameNode>();
|
|
QN->Components = nodeListToNodeArray(Arena, Head, Count);
|
|
return QN;
|
|
}
|
|
|
|
FuncClass Demangler::demangleFunctionClass(StringView &MangledName) {
|
|
switch (MangledName.popFront()) {
|
|
case '9':
|
|
return FuncClass(FC_ExternC | FC_NoParameterList);
|
|
case 'A':
|
|
return FC_Private;
|
|
case 'B':
|
|
return FuncClass(FC_Private | FC_Far);
|
|
case 'C':
|
|
return FuncClass(FC_Private | FC_Static);
|
|
case 'D':
|
|
return FuncClass(FC_Private | FC_Static);
|
|
case 'E':
|
|
return FuncClass(FC_Private | FC_Virtual);
|
|
case 'F':
|
|
return FuncClass(FC_Private | FC_Virtual);
|
|
case 'G':
|
|
return FuncClass(FC_Private | FC_StaticThisAdjust);
|
|
case 'H':
|
|
return FuncClass(FC_Private | FC_StaticThisAdjust | FC_Far);
|
|
case 'I':
|
|
return FuncClass(FC_Protected);
|
|
case 'J':
|
|
return FuncClass(FC_Protected | FC_Far);
|
|
case 'K':
|
|
return FuncClass(FC_Protected | FC_Static);
|
|
case 'L':
|
|
return FuncClass(FC_Protected | FC_Static | FC_Far);
|
|
case 'M':
|
|
return FuncClass(FC_Protected | FC_Virtual);
|
|
case 'N':
|
|
return FuncClass(FC_Protected | FC_Virtual | FC_Far);
|
|
case 'O':
|
|
return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust);
|
|
case 'P':
|
|
return FuncClass(FC_Protected | FC_Virtual | FC_StaticThisAdjust | FC_Far);
|
|
case 'Q':
|
|
return FuncClass(FC_Public);
|
|
case 'R':
|
|
return FuncClass(FC_Public | FC_Far);
|
|
case 'S':
|
|
return FuncClass(FC_Public | FC_Static);
|
|
case 'T':
|
|
return FuncClass(FC_Public | FC_Static | FC_Far);
|
|
case 'U':
|
|
return FuncClass(FC_Public | FC_Virtual);
|
|
case 'V':
|
|
return FuncClass(FC_Public | FC_Virtual | FC_Far);
|
|
case 'W':
|
|
return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust);
|
|
case 'X':
|
|
return FuncClass(FC_Public | FC_Virtual | FC_StaticThisAdjust | FC_Far);
|
|
case 'Y':
|
|
return FuncClass(FC_Global);
|
|
case 'Z':
|
|
return FuncClass(FC_Global | FC_Far);
|
|
case '$': {
|
|
FuncClass VFlag = FC_VirtualThisAdjust;
|
|
if (MangledName.consumeFront('R'))
|
|
VFlag = FuncClass(VFlag | FC_VirtualThisAdjustEx);
|
|
|
|
switch (MangledName.popFront()) {
|
|
case '0':
|
|
return FuncClass(FC_Private | FC_Virtual | VFlag);
|
|
case '1':
|
|
return FuncClass(FC_Private | FC_Virtual | VFlag | FC_Far);
|
|
case '2':
|
|
return FuncClass(FC_Protected | FC_Virtual | VFlag);
|
|
case '3':
|
|
return FuncClass(FC_Protected | FC_Virtual | VFlag | FC_Far);
|
|
case '4':
|
|
return FuncClass(FC_Public | FC_Virtual | VFlag);
|
|
case '5':
|
|
return FuncClass(FC_Public | FC_Virtual | VFlag | FC_Far);
|
|
}
|
|
}
|
|
}
|
|
|
|
Error = true;
|
|
return FC_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()) {
|
|
// Member qualifiers
|
|
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);
|
|
// Non-Member qualifiers
|
|
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);
|
|
}
|
|
|
|
// <variable-type> ::= <type> <cvr-qualifiers>
|
|
// ::= <type> <pointee-cvr-qualifiers> # pointers, references
|
|
TypeNode *Demangler::demangleType(StringView &MangledName,
|
|
QualifierMangleMode QMM) {
|
|
Qualifiers Quals = Q_None;
|
|
bool IsMember = false;
|
|
if (QMM == QualifierMangleMode::Mangle) {
|
|
std::tie(Quals, IsMember) = demangleQualifiers(MangledName);
|
|
} else if (QMM == QualifierMangleMode::Result) {
|
|
if (MangledName.consumeFront('?'))
|
|
std::tie(Quals, IsMember) = demangleQualifiers(MangledName);
|
|
}
|
|
|
|
TypeNode *Ty = nullptr;
|
|
if (isTagType(MangledName))
|
|
Ty = demangleClassType(MangledName);
|
|
else if (isPointerType(MangledName)) {
|
|
if (isMemberPointer(MangledName, Error))
|
|
Ty = demangleMemberPointerType(MangledName);
|
|
else if (!Error)
|
|
Ty = demanglePointerType(MangledName);
|
|
else
|
|
return nullptr;
|
|
} else if (isArrayType(MangledName))
|
|
Ty = demangleArrayType(MangledName);
|
|
else if (isFunctionType(MangledName)) {
|
|
if (MangledName.consumeFront("$$A8@@"))
|
|
Ty = demangleFunctionType(MangledName, true);
|
|
else {
|
|
assert(MangledName.startsWith("$$A6"));
|
|
MangledName.consumeFront("$$A6");
|
|
Ty = demangleFunctionType(MangledName, false);
|
|
}
|
|
} else if (isCustomType(MangledName)) {
|
|
Ty = demangleCustomType(MangledName);
|
|
} else {
|
|
Ty = demanglePrimitiveType(MangledName);
|
|
}
|
|
|
|
if (!Ty || Error)
|
|
return Ty;
|
|
Ty->Quals = Qualifiers(Ty->Quals | Quals);
|
|
return Ty;
|
|
}
|
|
|
|
bool Demangler::demangleThrowSpecification(StringView &MangledName) {
|
|
if (MangledName.consumeFront("_E"))
|
|
return true;
|
|
if (MangledName.consumeFront('Z'))
|
|
return false;
|
|
|
|
Error = true;
|
|
return false;
|
|
}
|
|
|
|
FunctionSignatureNode *Demangler::demangleFunctionType(StringView &MangledName,
|
|
bool HasThisQuals) {
|
|
FunctionSignatureNode *FTy = Arena.alloc<FunctionSignatureNode>();
|
|
|
|
if (HasThisQuals) {
|
|
FTy->Quals = demanglePointerExtQualifiers(MangledName);
|
|
FTy->RefQualifier = demangleFunctionRefQualifier(MangledName);
|
|
FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers(MangledName).first);
|
|
}
|
|
|
|
// Fields that appear on both member and non-member functions.
|
|
FTy->CallConvention = demangleCallingConvention(MangledName);
|
|
|
|
// <return-type> ::= <type>
|
|
// ::= @ # structors (they have no declared return type)
|
|
bool IsStructor = MangledName.consumeFront('@');
|
|
if (!IsStructor)
|
|
FTy->ReturnType = demangleType(MangledName, QualifierMangleMode::Result);
|
|
|
|
FTy->Params = demangleFunctionParameterList(MangledName);
|
|
|
|
FTy->IsNoexcept = demangleThrowSpecification(MangledName);
|
|
|
|
return FTy;
|
|
}
|
|
|
|
FunctionSymbolNode *
|
|
Demangler::demangleFunctionEncoding(StringView &MangledName) {
|
|
FuncClass ExtraFlags = FC_None;
|
|
if (MangledName.consumeFront("$$J0"))
|
|
ExtraFlags = FC_ExternC;
|
|
|
|
FuncClass FC = demangleFunctionClass(MangledName);
|
|
FC = FuncClass(ExtraFlags | FC);
|
|
|
|
FunctionSignatureNode *FSN = nullptr;
|
|
ThunkSignatureNode *TTN = nullptr;
|
|
if (FC & FC_StaticThisAdjust) {
|
|
TTN = Arena.alloc<ThunkSignatureNode>();
|
|
TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName);
|
|
} else if (FC & FC_VirtualThisAdjust) {
|
|
TTN = Arena.alloc<ThunkSignatureNode>();
|
|
if (FC & FC_VirtualThisAdjustEx) {
|
|
TTN->ThisAdjust.VBPtrOffset = demangleSigned(MangledName);
|
|
TTN->ThisAdjust.VBOffsetOffset = demangleSigned(MangledName);
|
|
}
|
|
TTN->ThisAdjust.VtordispOffset = demangleSigned(MangledName);
|
|
TTN->ThisAdjust.StaticOffset = demangleSigned(MangledName);
|
|
}
|
|
|
|
if (FC & FC_NoParameterList) {
|
|
// This is an extern "C" function whose full signature hasn't been mangled.
|
|
// This happens when we need to mangle a local symbol inside of an extern
|
|
// "C" function.
|
|
FSN = Arena.alloc<FunctionSignatureNode>();
|
|
} else {
|
|
bool HasThisQuals = !(FC & (FC_Global | FC_Static));
|
|
FSN = demangleFunctionType(MangledName, HasThisQuals);
|
|
}
|
|
if (TTN) {
|
|
*static_cast<FunctionSignatureNode *>(TTN) = *FSN;
|
|
FSN = TTN;
|
|
}
|
|
FSN->FunctionClass = FC;
|
|
|
|
FunctionSymbolNode *Symbol = Arena.alloc<FunctionSymbolNode>();
|
|
Symbol->Signature = FSN;
|
|
return Symbol;
|
|
}
|
|
|
|
CustomTypeNode *Demangler::demangleCustomType(StringView &MangledName) {
|
|
assert(MangledName.startsWith('?'));
|
|
MangledName.popFront();
|
|
|
|
CustomTypeNode *CTN = Arena.alloc<CustomTypeNode>();
|
|
CTN->Identifier = demangleUnqualifiedTypeName(MangledName, true);
|
|
if (!MangledName.consumeFront('@'))
|
|
Error = true;
|
|
if (Error)
|
|
return nullptr;
|
|
return CTN;
|
|
}
|
|
|
|
// Reads a primitive type.
|
|
PrimitiveTypeNode *Demangler::demanglePrimitiveType(StringView &MangledName) {
|
|
if (MangledName.consumeFront("$$T"))
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Nullptr);
|
|
|
|
switch (MangledName.popFront()) {
|
|
case 'X':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Void);
|
|
case 'D':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char);
|
|
case 'C':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Schar);
|
|
case 'E':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uchar);
|
|
case 'F':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Short);
|
|
case 'G':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ushort);
|
|
case 'H':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int);
|
|
case 'I':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint);
|
|
case 'J':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Long);
|
|
case 'K':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ulong);
|
|
case 'M':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Float);
|
|
case 'N':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Double);
|
|
case 'O':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Ldouble);
|
|
case '_': {
|
|
if (MangledName.empty()) {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
switch (MangledName.popFront()) {
|
|
case 'N':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Bool);
|
|
case 'J':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Int64);
|
|
case 'K':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Uint64);
|
|
case 'W':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Wchar);
|
|
case 'S':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char16);
|
|
case 'U':
|
|
return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char32);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
TagTypeNode *Demangler::demangleClassType(StringView &MangledName) {
|
|
TagTypeNode *TT = nullptr;
|
|
|
|
switch (MangledName.popFront()) {
|
|
case 'T':
|
|
TT = Arena.alloc<TagTypeNode>(TagKind::Union);
|
|
break;
|
|
case 'U':
|
|
TT = Arena.alloc<TagTypeNode>(TagKind::Struct);
|
|
break;
|
|
case 'V':
|
|
TT = Arena.alloc<TagTypeNode>(TagKind::Class);
|
|
break;
|
|
case 'W':
|
|
if (MangledName.popFront() != '4') {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
TT = Arena.alloc<TagTypeNode>(TagKind::Enum);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
TT->QualifiedName = demangleFullyQualifiedTypeName(MangledName);
|
|
return TT;
|
|
}
|
|
|
|
// <pointer-type> ::= E? <pointer-cvr-qualifiers> <ext-qualifiers> <type>
|
|
// # the E is required for 64-bit non-static pointers
|
|
PointerTypeNode *Demangler::demanglePointerType(StringView &MangledName) {
|
|
PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>();
|
|
|
|
std::tie(Pointer->Quals, Pointer->Affinity) =
|
|
demanglePointerCVQualifiers(MangledName);
|
|
|
|
if (MangledName.consumeFront("6")) {
|
|
Pointer->Pointee = demangleFunctionType(MangledName, false);
|
|
return Pointer;
|
|
}
|
|
|
|
Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);
|
|
Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
|
|
|
|
Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Mangle);
|
|
return Pointer;
|
|
}
|
|
|
|
PointerTypeNode *Demangler::demangleMemberPointerType(StringView &MangledName) {
|
|
PointerTypeNode *Pointer = Arena.alloc<PointerTypeNode>();
|
|
|
|
std::tie(Pointer->Quals, Pointer->Affinity) =
|
|
demanglePointerCVQualifiers(MangledName);
|
|
assert(Pointer->Affinity == PointerAffinity::Pointer);
|
|
|
|
Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName);
|
|
Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
|
|
|
|
if (MangledName.consumeFront("8")) {
|
|
Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName);
|
|
Pointer->Pointee = demangleFunctionType(MangledName, true);
|
|
} else {
|
|
Qualifiers PointeeQuals = Q_None;
|
|
bool IsMember = false;
|
|
std::tie(PointeeQuals, IsMember) = demangleQualifiers(MangledName);
|
|
assert(IsMember);
|
|
Pointer->ClassParent = demangleFullyQualifiedTypeName(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;
|
|
}
|
|
|
|
ArrayTypeNode *Demangler::demangleArrayType(StringView &MangledName) {
|
|
assert(MangledName.front() == 'Y');
|
|
MangledName.popFront();
|
|
|
|
uint64_t Rank = 0;
|
|
bool IsNegative = false;
|
|
std::tie(Rank, IsNegative) = demangleNumber(MangledName);
|
|
if (IsNegative || Rank == 0) {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
ArrayTypeNode *ATy = Arena.alloc<ArrayTypeNode>();
|
|
NodeList *Head = Arena.alloc<NodeList>();
|
|
NodeList *Tail = Head;
|
|
|
|
for (uint64_t I = 0; I < Rank; ++I) {
|
|
uint64_t D = 0;
|
|
std::tie(D, IsNegative) = demangleNumber(MangledName);
|
|
if (IsNegative) {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
Tail->N = Arena.alloc<IntegerLiteralNode>(D, IsNegative);
|
|
if (I + 1 < Rank) {
|
|
Tail->Next = Arena.alloc<NodeList>();
|
|
Tail = Tail->Next;
|
|
}
|
|
}
|
|
ATy->Dimensions = nodeListToNodeArray(Arena, Head, Rank);
|
|
|
|
if (MangledName.consumeFront("$$C")) {
|
|
bool IsMember = false;
|
|
std::tie(ATy->Quals, IsMember) = demangleQualifiers(MangledName);
|
|
if (IsMember) {
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
ATy->ElementType = demangleType(MangledName, QualifierMangleMode::Drop);
|
|
return ATy;
|
|
}
|
|
|
|
// Reads a function or a template parameters.
|
|
NodeArrayNode *
|
|
Demangler::demangleFunctionParameterList(StringView &MangledName) {
|
|
// Empty parameter list.
|
|
if (MangledName.consumeFront('X'))
|
|
return {};
|
|
|
|
NodeList *Head = Arena.alloc<NodeList>();
|
|
NodeList **Current = &Head;
|
|
size_t Count = 0;
|
|
while (!Error && !MangledName.startsWith('@') &&
|
|
!MangledName.startsWith('Z')) {
|
|
++Count;
|
|
|
|
if (startsWithDigit(MangledName)) {
|
|
size_t N = MangledName[0] - '0';
|
|
if (N >= Backrefs.FunctionParamCount) {
|
|
Error = true;
|
|
return {};
|
|
}
|
|
MangledName = MangledName.dropFront();
|
|
|
|
*Current = Arena.alloc<NodeList>();
|
|
(*Current)->N = Backrefs.FunctionParams[N];
|
|
Current = &(*Current)->Next;
|
|
continue;
|
|
}
|
|
|
|
size_t OldSize = MangledName.size();
|
|
|
|
*Current = Arena.alloc<NodeList>();
|
|
TypeNode *TN = demangleType(MangledName, QualifierMangleMode::Drop);
|
|
if (!TN || Error)
|
|
return nullptr;
|
|
|
|
(*Current)->N = TN;
|
|
|
|
size_t CharsConsumed = OldSize - MangledName.size();
|
|
assert(CharsConsumed != 0);
|
|
|
|
// Single-letter types are ignored for backreferences because memorizing
|
|
// them doesn't save anything.
|
|
if (Backrefs.FunctionParamCount <= 9 && CharsConsumed > 1)
|
|
Backrefs.FunctionParams[Backrefs.FunctionParamCount++] = TN;
|
|
|
|
Current = &(*Current)->Next;
|
|
}
|
|
|
|
if (Error)
|
|
return {};
|
|
|
|
NodeArrayNode *NA = nodeListToNodeArray(Arena, Head, Count);
|
|
// A non-empty parameter list is terminated by either 'Z' (variadic) parameter
|
|
// list or '@' (non variadic). Careful not to consume "@Z", as in that case
|
|
// the following Z could be a throw specifier.
|
|
if (MangledName.consumeFront('@'))
|
|
return NA;
|
|
|
|
if (MangledName.consumeFront('Z')) {
|
|
// This is a variadic parameter list. We probably need a variadic node to
|
|
// append to the end.
|
|
return NA;
|
|
}
|
|
|
|
Error = true;
|
|
return {};
|
|
}
|
|
|
|
NodeArrayNode *
|
|
Demangler::demangleTemplateParameterList(StringView &MangledName) {
|
|
NodeList *Head;
|
|
NodeList **Current = &Head;
|
|
size_t Count = 0;
|
|
|
|
while (!Error && !MangledName.startsWith('@')) {
|
|
if (MangledName.consumeFront("$S") || MangledName.consumeFront("$$V") ||
|
|
MangledName.consumeFront("$$$V") || MangledName.consumeFront("$$Z")) {
|
|
// parameter pack separator
|
|
continue;
|
|
}
|
|
|
|
++Count;
|
|
|
|
// Template parameter lists don't participate in back-referencing.
|
|
*Current = Arena.alloc<NodeList>();
|
|
|
|
NodeList &TP = **Current;
|
|
|
|
TemplateParameterReferenceNode *TPRN = nullptr;
|
|
if (MangledName.consumeFront("$$Y")) {
|
|
// Template alias
|
|
TP.N = demangleFullyQualifiedTypeName(MangledName);
|
|
} else if (MangledName.consumeFront("$$B")) {
|
|
// Array
|
|
TP.N = demangleType(MangledName, QualifierMangleMode::Drop);
|
|
} else if (MangledName.consumeFront("$$C")) {
|
|
// Type has qualifiers.
|
|
TP.N = demangleType(MangledName, QualifierMangleMode::Mangle);
|
|
} else if (MangledName.startsWith("$1") || MangledName.startsWith("$H") ||
|
|
MangledName.startsWith("$I") || MangledName.startsWith("$J")) {
|
|
// Pointer to member
|
|
TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
|
|
TPRN->IsMemberPointer = true;
|
|
|
|
MangledName = MangledName.dropFront();
|
|
// 1 - single inheritance <name>
|
|
// H - multiple inheritance <name> <number>
|
|
// I - virtual inheritance <name> <number> <number> <number>
|
|
// J - unspecified inheritance <name> <number> <number> <number>
|
|
char InheritanceSpecifier = MangledName.popFront();
|
|
SymbolNode *S = nullptr;
|
|
if (MangledName.startsWith('?')) {
|
|
S = parse(MangledName);
|
|
memorizeIdentifier(S->Name->getUnqualifiedIdentifier());
|
|
}
|
|
|
|
switch (InheritanceSpecifier) {
|
|
case 'J':
|
|
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
|
|
demangleSigned(MangledName);
|
|
DEMANGLE_FALLTHROUGH;
|
|
case 'I':
|
|
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
|
|
demangleSigned(MangledName);
|
|
DEMANGLE_FALLTHROUGH;
|
|
case 'H':
|
|
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
|
|
demangleSigned(MangledName);
|
|
DEMANGLE_FALLTHROUGH;
|
|
case '1':
|
|
break;
|
|
default:
|
|
Error = true;
|
|
break;
|
|
}
|
|
TPRN->Affinity = PointerAffinity::Pointer;
|
|
TPRN->Symbol = S;
|
|
} else if (MangledName.startsWith("$E?")) {
|
|
MangledName.consumeFront("$E");
|
|
// Reference to symbol
|
|
TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
|
|
TPRN->Symbol = parse(MangledName);
|
|
TPRN->Affinity = PointerAffinity::Reference;
|
|
} else if (MangledName.startsWith("$F") || MangledName.startsWith("$G")) {
|
|
TP.N = TPRN = Arena.alloc<TemplateParameterReferenceNode>();
|
|
|
|
// Data member pointer.
|
|
MangledName = MangledName.dropFront();
|
|
char InheritanceSpecifier = MangledName.popFront();
|
|
|
|
switch (InheritanceSpecifier) {
|
|
case 'G':
|
|
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
|
|
demangleSigned(MangledName);
|
|
DEMANGLE_FALLTHROUGH;
|
|
case 'F':
|
|
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
|
|
demangleSigned(MangledName);
|
|
TPRN->ThunkOffsets[TPRN->ThunkOffsetCount++] =
|
|
demangleSigned(MangledName);
|
|
DEMANGLE_FALLTHROUGH;
|
|
case '0':
|
|
break;
|
|
default:
|
|
Error = true;
|
|
break;
|
|
}
|
|
TPRN->IsMemberPointer = true;
|
|
|
|
} else if (MangledName.consumeFront("$0")) {
|
|
// Integral non-type template parameter
|
|
bool IsNegative = false;
|
|
uint64_t Value = 0;
|
|
std::tie(Value, IsNegative) = demangleNumber(MangledName);
|
|
|
|
TP.N = Arena.alloc<IntegerLiteralNode>(Value, IsNegative);
|
|
} else {
|
|
TP.N = demangleType(MangledName, QualifierMangleMode::Drop);
|
|
}
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
Current = &TP.Next;
|
|
}
|
|
|
|
if (Error)
|
|
return nullptr;
|
|
|
|
// Template parameter lists cannot be variadic, so it can only be terminated
|
|
// by @.
|
|
if (MangledName.consumeFront('@'))
|
|
return nodeListToNodeArray(Arena, Head, Count);
|
|
Error = true;
|
|
return nullptr;
|
|
}
|
|
|
|
void Demangler::dumpBackReferences() {
|
|
std::printf("%d function parameter backreferences\n",
|
|
(int)Backrefs.FunctionParamCount);
|
|
|
|
// Create an output stream so we can render each type.
|
|
OutputStream OS;
|
|
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
|
|
std::terminate();
|
|
for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) {
|
|
OS.setCurrentPosition(0);
|
|
|
|
TypeNode *T = Backrefs.FunctionParams[I];
|
|
T->output(OS, OF_Default);
|
|
|
|
std::printf(" [%d] - %.*s\n", (int)I, (int)OS.getCurrentPosition(),
|
|
OS.getBuffer());
|
|
}
|
|
std::free(OS.getBuffer());
|
|
|
|
if (Backrefs.FunctionParamCount > 0)
|
|
std::printf("\n");
|
|
std::printf("%d name backreferences\n", (int)Backrefs.NamesCount);
|
|
for (size_t I = 0; I < Backrefs.NamesCount; ++I) {
|
|
std::printf(" [%d] - %.*s\n", (int)I, (int)Backrefs.Names[I]->Name.size(),
|
|
Backrefs.Names[I]->Name.begin());
|
|
}
|
|
if (Backrefs.NamesCount > 0)
|
|
std::printf("\n");
|
|
}
|
|
|
|
char *llvm::microsoftDemangle(const char *MangledName, char *Buf, size_t *N,
|
|
int *Status, MSDemangleFlags Flags) {
|
|
int InternalStatus = demangle_success;
|
|
Demangler D;
|
|
OutputStream S;
|
|
|
|
StringView Name{MangledName};
|
|
SymbolNode *AST = D.parse(Name);
|
|
|
|
if (Flags & MSDF_DumpBackrefs)
|
|
D.dumpBackReferences();
|
|
|
|
if (D.Error)
|
|
InternalStatus = demangle_invalid_mangled_name;
|
|
else if (!initializeOutputStream(Buf, N, S, 1024))
|
|
InternalStatus = demangle_memory_alloc_failure;
|
|
else {
|
|
AST->output(S, OF_Default);
|
|
S += '\0';
|
|
if (N != nullptr)
|
|
*N = S.getCurrentPosition();
|
|
Buf = S.getBuffer();
|
|
}
|
|
|
|
if (Status)
|
|
*Status = InternalStatus;
|
|
return InternalStatus == demangle_success ? Buf : nullptr;
|
|
}
|