diff options
author | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/llvm16/lib/Demangle/MicrosoftDemangle.cpp | |
parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
download | ydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz |
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/llvm16/lib/Demangle/MicrosoftDemangle.cpp')
-rw-r--r-- | contrib/libs/llvm16/lib/Demangle/MicrosoftDemangle.cpp | 2368 |
1 files changed, 2368 insertions, 0 deletions
diff --git a/contrib/libs/llvm16/lib/Demangle/MicrosoftDemangle.cpp b/contrib/libs/llvm16/lib/Demangle/MicrosoftDemangle.cpp new file mode 100644 index 0000000000..c21b0a3010 --- /dev/null +++ b/contrib/libs/llvm16/lib/Demangle/MicrosoftDemangle.cpp @@ -0,0 +1,2368 @@ +//===- 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: + // isMemberPointer() is called only if isPointerType() returns true, + // and it rejects other prefixes. + DEMANGLE_UNREACHABLE; + } + + // If it starts with a number, then 6 indicates a non-member function + // pointer, and 8 indicates a member function pointer. + if (startsWithDigit(MangledName)) { + if (MangledName[0] != '6' && MangledName[0] != '8') { + Error = true; + return false; + } + 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 + + if (MangledName.empty()) { + Error = true; + return false; + } + + // 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; + + 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); + } + // This function is only called if isPointerType() returns true, + // and it only returns true for the six cases listed above. + DEMANGLE_UNREACHABLE; +} + +StringView Demangler::copyString(StringView Borrowed) { + char *Stable = Arena.allocUnalignedBuffer(Borrowed.size()); + // This is not a micro-optimization, it avoids UB, should Borrowed be an null + // buffer. + if (Borrowed.size()) + std::memcpy(Stable, Borrowed.begin(), Borrowed.size()); + + 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; + if (MangledName.empty()) { + Error = true; + return nullptr; + } + 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, bool IsThread) { + LocalStaticGuardIdentifierNode *LSGI = + Arena.alloc<LocalStaticGuardIdentifierNode>(); + LSGI->IsThread = IsThread; + 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; + + SymbolNode *Symbol = demangleDeclarator(MangledName); + if (Error) + return nullptr; + + FunctionSymbolNode *FSN = nullptr; + + 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); + if (FSN) + 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); + + switch (SIK) { + case SpecialIntrinsicKind::None: + return nullptr; + 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, /*IsThread=*/false); + case SpecialIntrinsicKind::LocalStaticThreadGuard: + return demangleLocalStaticGuard(MangledName, /*IsThread=*/true); + 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, /*IsDestructor=*/false); + case SpecialIntrinsicKind::DynamicAtexitDestructor: + return demangleInitFiniStub(MangledName, /*IsDestructor=*/true); + case SpecialIntrinsicKind::Typeof: + case SpecialIntrinsicKind::UdtReturning: + // It's unclear which tools produces these manglings, so demangling + // support is not (yet?) implemented. + break; + case SpecialIntrinsicKind::Unknown: + DEMANGLE_UNREACHABLE; // Never returned by consumeSpecialIntrinsicKind. + } + Error = true; + return nullptr; +} + +IdentifierNode * +Demangler::demangleFunctionIdentifierCode(StringView &MangledName) { + assert(MangledName.startsWith('?')); + MangledName = MangledName.dropFront(); + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + if (MangledName.consumeFront("__")) + return demangleFunctionIdentifierCode( + MangledName, FunctionIdentifierCodeGroup::DoubleUnder); + 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, /*Memorize=*/false); + return N; +} + +IntrinsicFunctionKind +Demangler::translateIntrinsicFunctionCode(char CH, + FunctionIdentifierCodeGroup Group) { + using IFK = IntrinsicFunctionKind; + if (!(CH >= '0' && CH <= '9') && !(CH >= 'A' && CH <= 'Z')) { + Error = true; + return IFK::None; + } + + // 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. + 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 operator co_await + IFK::Spaceship, // ?__M operator<=> + 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) { + if (MangledName.empty()) { + Error = true; + return nullptr; + } + 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)); + } + 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)); + } + } + + DEMANGLE_UNREACHABLE; +} + +SymbolNode *Demangler::demangleEncodedSymbol(StringView &MangledName, + QualifiedNameNode *Name) { + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + // Read a variable. + switch (MangledName.front()) { + case '0': + case '1': + case '2': + case '3': + case '4': { + StorageClass SC = demangleVariableStorageClass(MangledName); + return demangleVariableEncoding(MangledName, SC); + } + } + FunctionSymbolNode *FSN = demangleFunctionEncoding(MangledName); + + IdentifierNode *UQN = Name->getUnqualifiedIdentifier(); + if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) { + ConversionOperatorIdentifierNode *COIN = + static_cast<ConversionOperatorIdentifierNode *>(UQN); + if (FSN) + COIN->TargetType = FSN->Signature->ReturnType; + } + return FSN; +} + +SymbolNode *Demangler::demangleDeclarator(StringView &MangledName) { + // 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 (Error) + return nullptr; + Symbol->Name = QN; + + IdentifierNode *UQN = QN->getUnqualifiedIdentifier(); + if (UQN->kind() == NodeKind::ConversionOperatorIdentifier) { + ConversionOperatorIdentifierNode *COIN = + static_cast<ConversionOperatorIdentifierNode *>(UQN); + if (!COIN->TargetType) { + Error = true; + return nullptr; + } + } + return Symbol; +} + +SymbolNode *Demangler::demangleMD5Name(StringView &MangledName) { + assert(MangledName.startsWith("??@")); + // This is an MD5 mangled name. We can't demangle it, just return the + // mangled name. + // An MD5 mangled name is ??@ followed by 32 characters and a terminating @. + size_t MD5Last = MangledName.find('@', strlen("??@")); + if (MD5Last == StringView::npos) { + Error = true; + return nullptr; + } + const char *Start = MangledName.begin(); + MangledName = MangledName.dropFront(MD5Last + 1); + + // There are two additional special cases for MD5 names: + // 1. For complete object locators where the object name is long enough + // for the object to have an MD5 name, the complete object locator is + // called ??@...@??_R4@ (with a trailing "??_R4@" instead of the usual + // leading "??_R4". This is handled here. + // 2. For catchable types, in versions of MSVC before 2015 (<1900) or after + // 2017.2 (>= 1914), the catchable type mangling is _CT??@...@??@...@8 + // instead of_CT??@...@8 with just one MD5 name. Since we don't yet + // demangle catchable types anywhere, this isn't handled for MD5 names + // either. + MangledName.consumeFront("??_R4@"); + + StringView MD5(Start, MangledName.begin()); + SymbolNode *S = Arena.alloc<SymbolNode>(NodeKind::Md5Symbol); + S->Name = synthesizeQualifiedName(Arena, MD5); + + return S; +} + +SymbolNode *Demangler::demangleTypeinfoName(StringView &MangledName) { + assert(MangledName.startsWith('.')); + MangledName.consumeFront('.'); + + TypeNode *T = demangleType(MangledName, QualifierMangleMode::Result); + if (Error || !MangledName.empty()) { + Error = true; + return nullptr; + } + return synthesizeVariable(Arena, T, "`RTTI Type Descriptor Name'"); +} + +// Parser entry point. +SymbolNode *Demangler::parse(StringView &MangledName) { + // Typeinfo names are strings stored in RTTI data. They're not symbol names. + // It's still useful to demangle them. They're the only demangled entity + // that doesn't start with a "?" but a ".". + if (MangledName.startsWith('.')) + return demangleTypeinfoName(MangledName); + + if (MangledName.startsWith("??@")) + return demangleMD5Name(MangledName); + + // MSVC-style mangled symbols must start with '?'. + 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; + + return demangleDeclarator(MangledName); +} + +TagTypeNode *Demangler::parseTagUniqueName(StringView &MangledName) { + if (!MangledName.consumeFront(".?A")) { + Error = true; + return nullptr; + } + MangledName.consumeFront(".?A"); + if (MangledName.empty()) { + Error = true; + 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; + + if (Error) + return nullptr; + + // <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 Number == 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. + OutputBuffer OB; + Identifier->output(OB, OF_Default); + StringView Owned = copyString(OB); + memorizeString(Owned); + std::free(OB.getBuffer()); +} + +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) { + // NBB_Template is only set for types and non-leaf names ("a::" in "a::b"). + // Structors and conversion operators only makes sense in a leaf name, so + // reject them in NBB_Template contexts. + if (Identifier->kind() == NodeKind::ConversionOperatorIdentifier || + Identifier->kind() == NodeKind::StructorIdentifier) { + Error = true; + return nullptr; + } + + 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) { + assert(!MangledName.empty()); + 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 || MangledName.empty()) + 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(OutputBuffer &OB, unsigned C) { + assert (C != 0); + + // 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 = sizeof(TempBuffer) - 1; + + int Pos = MaxPos - 1; // TempBuffer[MaxPos] is the terminating \0. + while (C != 0) { + for (int I = 0; I < 2; ++I) { + writeHexDigit(&TempBuffer[Pos--], C % 16); + C /= 16; + } + } + TempBuffer[Pos--] = 'x'; + assert(Pos >= 0); + TempBuffer[Pos--] = '\\'; + OB << StringView(&TempBuffer[Pos + 1]); +} + +static void outputEscapedChar(OutputBuffer &OB, unsigned C) { + switch (C) { + case '\0': // nul + OB << "\\0"; + return; + case '\'': // single quote + OB << "\\\'"; + return; + case '\"': // double quote + OB << "\\\""; + return; + case '\\': // backslash + OB << "\\\\"; + return; + case '\a': // bell + OB << "\\a"; + return; + case '\b': // backspace + OB << "\\b"; + return; + case '\f': // form feed + OB << "\\f"; + return; + case '\n': // new line + OB << "\\n"; + return; + case '\r': // carriage return + OB << "\\r"; + return; + case '\t': // tab + OB << "\\t"; + return; + case '\v': // vertical tab + OB << "\\v"; + return; + default: + break; + } + + if (C > 0x1F && C < 0x7F) { + // Standard ascii char. + OB << (char)C; + return; + } + + outputHex(OB, 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; +} + +// A mangled (non-wide) string literal stores the total length of the string it +// refers to (passed in NumBytes), and it contains up to 32 bytes of actual text +// (passed in StringBytes, NumChars). +static unsigned guessCharByteSize(const uint8_t *StringBytes, unsigned NumChars, + uint64_t 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 && NumBytes % 4 == 0) + 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 && NumBytes % 4 == 0) + 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. + OutputBuffer OB; + StringView CRC; + uint64_t StringByteSize; + bool IsWcharT = false; + bool IsNegative = false; + size_t CrcEndPos = 0; + + 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 || StringByteSize < (IsWcharT ? 2 : 1)) + 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 (IsWcharT) { + Result->Char = CharKind::Wchar; + if (StringByteSize > 64) + Result->IsTruncated = true; + + while (!MangledName.consumeFront('@')) { + if (MangledName.size() < 2) + goto StringLiteralError; + wchar_t W = demangleWcharLiteral(MangledName); + if (StringByteSize != 2 || Result->IsTruncated) + outputEscapedChar(OB, 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('@')) { + if (MangledName.size() < 1 || BytesDecoded >= MaxStringByteLength) + goto StringLiteralError; + 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(OB, NextChar); + } + } + + Result->DecodedString = copyString(OB); + std::free(OB.getBuffer()); + return Result; + +StringLiteralError: + Error = true; + std::free(OB.getBuffer()); + return nullptr; +} + +// Returns MangledName's prefix before the first '@', or an error if +// MangledName contains no '@' or the prefix has length 0. +StringView Demangler::demangleSimpleString(StringView &MangledName, + bool Memorize) { + StringView S; + for (size_t i = 0; i < MangledName.size(); ++i) { + if (MangledName[i] != '@') + continue; + if (i == 0) + break; + 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('?'); + uint64_t Number = 0; + bool IsNegative = false; + std::tie(Number, IsNegative) = demangleNumber(MangledName); + assert(!IsNegative); + + // 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. + OutputBuffer OB; + OB << '`'; + Scope->output(OB, OF_Default); + OB << '\''; + OB << "::`" << Number << "'"; + + Identifier->Name = copyString(OB); + std::free(OB.getBuffer()); + 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, /*Memorize=*/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) { + if (QN->Components->Count < 2) { + Error = true; + return nullptr; + } + StructorIdentifierNode *SIN = + static_cast<StructorIdentifierNode *>(Identifier); + 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, /*Memorize=*/(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, /*Memorize=*/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 | FC_Far); + case 'E': + return FuncClass(FC_Private | FC_Virtual); + case 'F': + return FuncClass(FC_Private | FC_Virtual | FC_Far); + 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); + if (MangledName.empty()) + break; + 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) { + if (MangledName.empty()) { + Error = true; + return CallingConv::None; + } + + 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; + case 'S': + return CallingConv::Swift; + case 'W': + return CallingConv::SwiftAsync; + } + + return CallingConv::None; +} + +StorageClass Demangler::demangleVariableStorageClass(StringView &MangledName) { + assert(MangledName.front() >= '0' && MangledName.front() <= '4'); + + 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; + } + DEMANGLE_UNREACHABLE; +} + +std::pair<Qualifiers, bool> +Demangler::demangleQualifiers(StringView &MangledName) { + if (MangledName.empty()) { + Error = true; + return std::make_pair(Q_None, false); + } + + 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); + } + + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + 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->IsVariadic); + + FTy->IsNoexcept = demangleThrowSpecification(MangledName); + + return FTy; +} + +FunctionSymbolNode * +Demangler::demangleFunctionEncoding(StringView &MangledName) { + FuncClass ExtraFlags = FC_None; + if (MangledName.consumeFront("$$J0")) + ExtraFlags = FC_ExternC; + + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + 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 (Error) + return nullptr; + + 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, /*Memorize=*/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 'Q': + return Arena.alloc<PrimitiveTypeNode>(PrimitiveKind::Char8); + 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.consumeFront('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); + + // isMemberPointer() only returns true if there is at least one character + // after the qualifiers. + 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 || Error); + Pointer->ClassParent = demangleFullyQualifiedTypeName(MangledName); + + Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Drop); + if (Pointer->Pointee) + 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 (Error || 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's parameters. +NodeArrayNode *Demangler::demangleFunctionParameterList(StringView &MangledName, + bool &IsVariadic) { + // Empty parameter list. + if (MangledName.consumeFront('X')) + return nullptr; + + 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 nullptr; + } + 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 nullptr; + + 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')) { + IsVariadic = true; + return NA; + } + + DEMANGLE_UNREACHABLE; +} + +NodeArrayNode * +Demangler::demangleTemplateParameterList(StringView &MangledName) { + NodeList *Head = nullptr; + NodeList **Current = &Head; + size_t Count = 0; + + while (!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> + // J - unspecified inheritance <name> <number> <number> <number> + char InheritanceSpecifier = MangledName.popFront(); + SymbolNode *S = nullptr; + if (MangledName.startsWith('?')) { + S = parse(MangledName); + if (Error || !S->Name) { + Error = true; + return nullptr; + } + 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: + DEMANGLE_UNREACHABLE; + } + 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); + break; + default: + DEMANGLE_UNREACHABLE; + } + 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; + } + + // The loop above returns nullptr on Error. + assert(!Error); + + // Template parameter lists cannot be variadic, so it can only be terminated + // by @ (as opposed to 'Z' in the function parameter case). + assert(MangledName.startsWith('@')); // The above loop exits only on '@'. + MangledName.consumeFront('@'); + return nodeListToNodeArray(Arena, Head, Count); +} + +void Demangler::dumpBackReferences() { + std::printf("%d function parameter backreferences\n", + (int)Backrefs.FunctionParamCount); + + // Create an output stream so we can render each type. + OutputBuffer OB; + for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) { + OB.setCurrentPosition(0); + + TypeNode *T = Backrefs.FunctionParams[I]; + T->output(OB, OF_Default); + + StringView B = OB; + std::printf(" [%d] - %.*s\n", (int)I, (int)B.size(), B.begin()); + } + std::free(OB.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, size_t *NMangled, + char *Buf, size_t *N, + int *Status, MSDemangleFlags Flags) { + Demangler D; + + StringView Name{MangledName}; + SymbolNode *AST = D.parse(Name); + if (!D.Error && NMangled) + *NMangled = Name.begin() - MangledName; + + if (Flags & MSDF_DumpBackrefs) + D.dumpBackReferences(); + + OutputFlags OF = OF_Default; + if (Flags & MSDF_NoCallingConvention) + OF = OutputFlags(OF | OF_NoCallingConvention); + if (Flags & MSDF_NoAccessSpecifier) + OF = OutputFlags(OF | OF_NoAccessSpecifier); + if (Flags & MSDF_NoReturnType) + OF = OutputFlags(OF | OF_NoReturnType); + if (Flags & MSDF_NoMemberType) + OF = OutputFlags(OF | OF_NoMemberType); + if (Flags & MSDF_NoVariableType) + OF = OutputFlags(OF | OF_NoVariableType); + + int InternalStatus = demangle_success; + if (D.Error) + InternalStatus = demangle_invalid_mangled_name; + else { + OutputBuffer OB(Buf, N); + AST->output(OB, OF); + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + Buf = OB.getBuffer(); + } + + if (Status) + *Status = InternalStatus; + return InternalStatus == demangle_success ? Buf : nullptr; +} |