diff options
Diffstat (limited to 'contrib/llvm/lib/Demangle/MicrosoftDemangle.cpp')
| -rw-r--r-- | contrib/llvm/lib/Demangle/MicrosoftDemangle.cpp | 2036 |
1 files changed, 2036 insertions, 0 deletions
diff --git a/contrib/llvm/lib/Demangle/MicrosoftDemangle.cpp b/contrib/llvm/lib/Demangle/MicrosoftDemangle.cpp new file mode 100644 index 000000000000..3eac87d61011 --- /dev/null +++ b/contrib/llvm/lib/Demangle/MicrosoftDemangle.cpp @@ -0,0 +1,2036 @@ +//===- MicrosoftDemangle.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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/Demangle.h" + +#include "Compiler.h" +#include "StringView.h" +#include "Utility.h" + +#include <cctype> +#include <tuple> + +// This memory allocator is extremely fast, but it doesn't call dtors +// for allocated objects. That means you can't use STL containers +// (such as std::vector) with this allocator. But it pays off -- +// the demangler is 3x faster with this allocator compared to one with +// STL containers. +namespace { + constexpr size_t AllocUnit = 4096; + +class ArenaAllocator { + struct AllocatorNode { + uint8_t *Buf = nullptr; + size_t Used = 0; + size_t Capacity = 0; + AllocatorNode *Next = nullptr; + }; + + void addNode(size_t Capacity) { + AllocatorNode *NewHead = new AllocatorNode; + NewHead->Buf = new uint8_t[Capacity]; + NewHead->Next = Head; + NewHead->Capacity = Capacity; + Head = NewHead; + NewHead->Used = 0; + } + +public: + ArenaAllocator() { addNode(AllocUnit); } + + ~ArenaAllocator() { + while (Head) { + assert(Head->Buf); + delete[] Head->Buf; + AllocatorNode *Next = Head->Next; + delete Head; + Head = Next; + } + } + + char *allocUnalignedBuffer(size_t Length) { + uint8_t *Buf = Head->Buf + Head->Used; + + Head->Used += Length; + if (Head->Used > Head->Capacity) { + // It's possible we need a buffer which is larger than our default unit + // size, so we need to be careful to add a node with capacity that is at + // least as large as what we need. + addNode(std::max(AllocUnit, Length)); + Head->Used = Length; + Buf = Head->Buf; + } + + return reinterpret_cast<char *>(Buf); + } + + template <typename T, typename... Args> T *alloc(Args &&... ConstructorArgs) { + + size_t Size = sizeof(T); + assert(Head && Head->Buf); + + size_t P = (size_t)Head->Buf + Head->Used; + uintptr_t AlignedP = + (((size_t)P + alignof(T) - 1) & ~(size_t)(alignof(T) - 1)); + uint8_t *PP = (uint8_t *)AlignedP; + size_t Adjustment = AlignedP - P; + + Head->Used += Size + Adjustment; + if (Head->Used < Head->Capacity) + return new (PP) T(std::forward<Args>(ConstructorArgs)...); + + addNode(AllocUnit); + Head->Used = Size; + return new (Head->Buf) T(std::forward<Args>(ConstructorArgs)...); + } + +private: + AllocatorNode *Head = nullptr; +}; +} // namespace + +static bool startsWithDigit(StringView S) { + return !S.empty() && std::isdigit(S.front()); +} + +// Writes a space if the last token does not end with a punctuation. +static void outputSpaceIfNecessary(OutputStream &OS) { + if (OS.empty()) + return; + + char C = OS.back(); + if (isalnum(C) || C == '>') + OS << " "; +} + +// Storage classes +enum Qualifiers : uint8_t { + Q_None = 0, + Q_Const = 1 << 0, + Q_Volatile = 1 << 1, + Q_Far = 1 << 2, + Q_Huge = 1 << 3, + Q_Unaligned = 1 << 4, + Q_Restrict = 1 << 5, + Q_Pointer64 = 1 << 6 +}; + +enum class StorageClass : uint8_t { + None, + PrivateStatic, + ProtectedStatic, + PublicStatic, + Global, + FunctionLocalStatic +}; + +enum class QualifierMangleMode { Drop, Mangle, Result }; + +enum class PointerAffinity { Pointer, Reference, RValueReference }; + +// Calling conventions +enum class CallingConv : uint8_t { + None, + Cdecl, + Pascal, + Thiscall, + Stdcall, + Fastcall, + Clrcall, + Eabi, + Vectorcall, + Regcall, +}; + +enum class ReferenceKind : uint8_t { None, LValueRef, RValueRef }; + +// Types +enum class PrimTy : uint8_t { + Unknown, + None, + Function, + Ptr, + MemberPtr, + Array, + + Struct, + Union, + Class, + Enum, + + Void, + Bool, + Char, + Schar, + Uchar, + Char16, + Char32, + Short, + Ushort, + Int, + Uint, + Long, + Ulong, + Int64, + Uint64, + Wchar, + Float, + Double, + Ldouble, + Nullptr +}; + +// Function classes +enum FuncClass : uint8_t { + Public = 1 << 0, + Protected = 1 << 1, + Private = 1 << 2, + Global = 1 << 3, + Static = 1 << 4, + Virtual = 1 << 5, + Far = 1 << 6, +}; + +namespace { + +struct Type; +struct Name; + +struct FunctionParams { + bool IsVariadic = false; + + Type *Current = nullptr; + + FunctionParams *Next = nullptr; +}; + +struct TemplateParams { + bool IsTemplateTemplate = false; + bool IsAliasTemplate = false; + + // Type can be null if this is a template template parameter. In that case + // only Name will be valid. + Type *ParamType = nullptr; + + // Name can be valid if this is a template template parameter (see above) or + // this is a function declaration (e.g. foo<&SomeFunc>). In the latter case + // Name contains the name of the function and Type contains the signature. + Name *ParamName = nullptr; + + TemplateParams *Next = nullptr; +}; + +// The type class. Mangled symbols are first parsed and converted to +// this type and then converted to string. +struct Type { + virtual ~Type() {} + + virtual Type *clone(ArenaAllocator &Arena) const; + + // Write the "first half" of a given type. This is a static functions to + // give the code a chance to do processing that is common to a subset of + // subclasses + static void outputPre(OutputStream &OS, Type &Ty); + + // Write the "second half" of a given type. This is a static functions to + // give the code a chance to do processing that is common to a subset of + // subclasses + static void outputPost(OutputStream &OS, Type &Ty); + + virtual void outputPre(OutputStream &OS); + virtual void outputPost(OutputStream &OS); + + // Primitive type such as Int. + PrimTy Prim = PrimTy::Unknown; + + Qualifiers Quals = Q_None; + StorageClass Storage = StorageClass::None; // storage class +}; + +// Represents an identifier which may be a template. +struct Name { + // Name read from an MangledName string. + StringView Str; + + // Overloaded operators are represented as special BackReferences in mangled + // symbols. If this is an operator name, "op" has an operator name (e.g. + // ">>"). Otherwise, empty. + StringView Operator; + + // Template parameters. Null if not a template. + TemplateParams *TParams = nullptr; + + // Nested BackReferences (e.g. "A::B::C") are represented as a linked list. + Name *Next = nullptr; +}; + +struct PointerType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS) override; + void outputPost(OutputStream &OS) override; + + PointerAffinity Affinity; + + // Represents a type X in "a pointer to X", "a reference to X", + // "an array of X", or "a function returning X". + Type *Pointee = nullptr; +}; + +struct MemberPointerType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS) override; + void outputPost(OutputStream &OS) override; + + Name *MemberName = nullptr; + + // Represents a type X in "a pointer to X", "a reference to X", + // "an array of X", or "a function returning X". + Type *Pointee = nullptr; +}; + +struct FunctionType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS) override; + void outputPost(OutputStream &OS) override; + + // True if this FunctionType instance is the Pointee of a PointerType or + // MemberPointerType. + bool IsFunctionPointer = false; + + Type *ReturnType = nullptr; + // If this is a reference, the type of reference. + ReferenceKind RefKind; + + CallingConv CallConvention; + FuncClass FunctionClass; + + FunctionParams Params; +}; + +struct UdtType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS) override; + + Name *UdtName = nullptr; +}; + +struct ArrayType : public Type { + Type *clone(ArenaAllocator &Arena) const override; + void outputPre(OutputStream &OS) override; + void outputPost(OutputStream &OS) override; + + // Either NextDimension or ElementType will be valid. + ArrayType *NextDimension = nullptr; + uint32_t ArrayDimension = 0; + + Type *ElementType = nullptr; +}; + +} // namespace + +static bool isMemberPointer(StringView MangledName) { + 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: + assert(false && "Ty is not a pointer type!"); + } + + // 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: + assert(false); + } + return false; +} + +static void outputCallingConvention(OutputStream &OS, CallingConv CC) { + outputSpaceIfNecessary(OS); + + switch (CC) { + case CallingConv::Cdecl: + OS << "__cdecl"; + break; + case CallingConv::Fastcall: + OS << "__fastcall"; + break; + case CallingConv::Pascal: + OS << "__pascal"; + break; + case CallingConv::Regcall: + OS << "__regcall"; + break; + case CallingConv::Stdcall: + OS << "__stdcall"; + break; + case CallingConv::Thiscall: + OS << "__thiscall"; + break; + case CallingConv::Eabi: + OS << "__eabi"; + break; + case CallingConv::Vectorcall: + OS << "__vectorcall"; + break; + case CallingConv::Clrcall: + OS << "__clrcall"; + break; + default: + break; + } +} + +static bool startsWithLocalScopePattern(StringView S) { + if (!S.consumeFront('?')) + return false; + if (S.size() < 2) + return false; + + size_t End = S.find('?'); + if (End == StringView::npos) + return false; + StringView Candidate = S.substr(0, End); + if (Candidate.empty()) + return false; + + // \?[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 void outputName(OutputStream &OS, const Name *TheName); + +// Write a function or template parameter list. +static void outputParameterList(OutputStream &OS, + const FunctionParams &Params) { + if (!Params.Current) { + OS << "void"; + return; + } + + const FunctionParams *Head = &Params; + while (Head) { + Type::outputPre(OS, *Head->Current); + Type::outputPost(OS, *Head->Current); + + Head = Head->Next; + + if (Head) + OS << ", "; + } +} + +static void outputParameterList(OutputStream &OS, + const TemplateParams &Params) { + if (!Params.ParamType && !Params.ParamName) { + OS << "<>"; + return; + } + + OS << "<"; + const TemplateParams *Head = &Params; + while (Head) { + // Type can be null if this is a template template parameter, + // and Name can be null if this is a simple type. + + if (Head->ParamType && Head->ParamName) { + // Function pointer. + OS << "&"; + Type::outputPre(OS, *Head->ParamType); + outputName(OS, Head->ParamName); + Type::outputPost(OS, *Head->ParamType); + } else if (Head->ParamType) { + // simple type. + Type::outputPre(OS, *Head->ParamType); + Type::outputPost(OS, *Head->ParamType); + } else { + // Template alias. + outputName(OS, Head->ParamName); + } + + Head = Head->Next; + + if (Head) + OS << ", "; + } + OS << ">"; +} + +static void outputName(OutputStream &OS, const Name *TheName) { + if (!TheName) + return; + + outputSpaceIfNecessary(OS); + + const Name *Previous = nullptr; + // Print out namespaces or outer class BackReferences. + for (; TheName->Next; TheName = TheName->Next) { + Previous = TheName; + OS << TheName->Str; + if (TheName->TParams) + outputParameterList(OS, *TheName->TParams); + OS << "::"; + } + + // Print out a regular name. + if (TheName->Operator.empty()) { + OS << TheName->Str; + if (TheName->TParams) + outputParameterList(OS, *TheName->TParams); + return; + } + + // Print out ctor or dtor. + if (TheName->Operator == "dtor") + OS << "~"; + + if (TheName->Operator == "ctor" || TheName->Operator == "dtor") { + OS << Previous->Str; + if (Previous->TParams) + outputParameterList(OS, *Previous->TParams); + return; + } + + // Print out an overloaded operator. + if (!TheName->Str.empty()) + OS << TheName->Str << "::"; + OS << "operator" << TheName->Operator; +} + +namespace { + +Type *Type::clone(ArenaAllocator &Arena) const { + return Arena.alloc<Type>(*this); +} + +// Write the "first half" of a given type. +void Type::outputPre(OutputStream &OS, Type &Ty) { + // Function types require custom handling of const and static so we + // handle them separately. All other types use the same decoration + // for these modifiers, so handle them here in common code. + if (Ty.Prim == PrimTy::Function) { + Ty.outputPre(OS); + return; + } + + switch (Ty.Storage) { + case StorageClass::PrivateStatic: + case StorageClass::PublicStatic: + case StorageClass::ProtectedStatic: + OS << "static "; + default: + break; + } + Ty.outputPre(OS); + + if (Ty.Quals & Q_Const) { + outputSpaceIfNecessary(OS); + OS << "const"; + } + + if (Ty.Quals & Q_Volatile) { + outputSpaceIfNecessary(OS); + OS << "volatile"; + } + + if (Ty.Quals & Q_Restrict) { + outputSpaceIfNecessary(OS); + OS << "__restrict"; + } +} + +// Write the "second half" of a given type. +void Type::outputPost(OutputStream &OS, Type &Ty) { Ty.outputPost(OS); } + +void Type::outputPre(OutputStream &OS) { + switch (Prim) { + case PrimTy::Void: + OS << "void"; + break; + case PrimTy::Bool: + OS << "bool"; + break; + case PrimTy::Char: + OS << "char"; + break; + case PrimTy::Schar: + OS << "signed char"; + break; + case PrimTy::Uchar: + OS << "unsigned char"; + break; + case PrimTy::Char16: + OS << "char16_t"; + break; + case PrimTy::Char32: + OS << "char32_t"; + break; + case PrimTy::Short: + OS << "short"; + break; + case PrimTy::Ushort: + OS << "unsigned short"; + break; + case PrimTy::Int: + OS << "int"; + break; + case PrimTy::Uint: + OS << "unsigned int"; + break; + case PrimTy::Long: + OS << "long"; + break; + case PrimTy::Ulong: + OS << "unsigned long"; + break; + case PrimTy::Int64: + OS << "__int64"; + break; + case PrimTy::Uint64: + OS << "unsigned __int64"; + break; + case PrimTy::Wchar: + OS << "wchar_t"; + break; + case PrimTy::Float: + OS << "float"; + break; + case PrimTy::Double: + OS << "double"; + break; + case PrimTy::Ldouble: + OS << "long double"; + break; + case PrimTy::Nullptr: + OS << "std::nullptr_t"; + break; + default: + assert(false && "Invalid primitive type!"); + } +} +void Type::outputPost(OutputStream &OS) {} + +Type *PointerType::clone(ArenaAllocator &Arena) const { + return Arena.alloc<PointerType>(*this); +} + +static void outputPointerIndicator(OutputStream &OS, PointerAffinity Affinity, + const Name *MemberName, + const Type *Pointee) { + // "[]" and "()" (for function parameters) take precedence over "*", + // so "int *x(int)" means "x is a function returning int *". We need + // parentheses to supercede the default precedence. (e.g. we want to + // emit something like "int (*x)(int)".) + if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array) { + OS << "("; + if (Pointee->Prim == PrimTy::Function) { + const FunctionType *FTy = static_cast<const FunctionType *>(Pointee); + assert(FTy->IsFunctionPointer); + outputCallingConvention(OS, FTy->CallConvention); + OS << " "; + } + } + + if (MemberName) { + outputName(OS, MemberName); + OS << "::"; + } + + if (Affinity == PointerAffinity::Pointer) + OS << "*"; + else if (Affinity == PointerAffinity::Reference) + OS << "&"; + else + OS << "&&"; +} + +void PointerType::outputPre(OutputStream &OS) { + Type::outputPre(OS, *Pointee); + + outputSpaceIfNecessary(OS); + + if (Quals & Q_Unaligned) + OS << "__unaligned "; + + outputPointerIndicator(OS, Affinity, nullptr, Pointee); + + // FIXME: We should output this, but it requires updating lots of tests. + // if (Ty.Quals & Q_Pointer64) + // OS << " __ptr64"; +} + +void PointerType::outputPost(OutputStream &OS) { + if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array) + OS << ")"; + + Type::outputPost(OS, *Pointee); +} + +Type *MemberPointerType::clone(ArenaAllocator &Arena) const { + return Arena.alloc<MemberPointerType>(*this); +} + +void MemberPointerType::outputPre(OutputStream &OS) { + Type::outputPre(OS, *Pointee); + + outputSpaceIfNecessary(OS); + + outputPointerIndicator(OS, PointerAffinity::Pointer, MemberName, Pointee); + + // FIXME: We should output this, but it requires updating lots of tests. + // if (Ty.Quals & Q_Pointer64) + // OS << " __ptr64"; + if (Quals & Q_Restrict) + OS << " __restrict"; +} + +void MemberPointerType::outputPost(OutputStream &OS) { + if (Pointee->Prim == PrimTy::Function || Pointee->Prim == PrimTy::Array) + OS << ")"; + + Type::outputPost(OS, *Pointee); +} + +Type *FunctionType::clone(ArenaAllocator &Arena) const { + return Arena.alloc<FunctionType>(*this); +} + +void FunctionType::outputPre(OutputStream &OS) { + if (!(FunctionClass & Global)) { + if (FunctionClass & Static) + OS << "static "; + } + + if (ReturnType) { + Type::outputPre(OS, *ReturnType); + OS << " "; + } + + // Function pointers print the calling convention as void (__cdecl *)(params) + // rather than void __cdecl (*)(params). So we need to let the PointerType + // class handle this. + if (!IsFunctionPointer) + outputCallingConvention(OS, CallConvention); +} + +void FunctionType::outputPost(OutputStream &OS) { + OS << "("; + outputParameterList(OS, Params); + OS << ")"; + if (Quals & Q_Const) + OS << " const"; + if (Quals & Q_Volatile) + OS << " volatile"; + if (Quals & Q_Restrict) + OS << " __restrict"; + if (Quals & Q_Unaligned) + OS << " __unaligned"; + + if (RefKind == ReferenceKind::LValueRef) + OS << " &"; + else if (RefKind == ReferenceKind::RValueRef) + OS << " &&"; + + if (ReturnType) + Type::outputPost(OS, *ReturnType); + return; +} + +Type *UdtType::clone(ArenaAllocator &Arena) const { + return Arena.alloc<UdtType>(*this); +} + +void UdtType::outputPre(OutputStream &OS) { + switch (Prim) { + case PrimTy::Class: + OS << "class "; + break; + case PrimTy::Struct: + OS << "struct "; + break; + case PrimTy::Union: + OS << "union "; + break; + case PrimTy::Enum: + OS << "enum "; + break; + default: + assert(false && "Not a udt type!"); + } + + outputName(OS, UdtName); +} + +Type *ArrayType::clone(ArenaAllocator &Arena) const { + return Arena.alloc<ArrayType>(*this); +} + +void ArrayType::outputPre(OutputStream &OS) { + Type::outputPre(OS, *ElementType); +} + +void ArrayType::outputPost(OutputStream &OS) { + if (ArrayDimension > 0) + OS << "[" << ArrayDimension << "]"; + if (NextDimension) + Type::outputPost(OS, *NextDimension); + else if (ElementType) + Type::outputPost(OS, *ElementType); +} + +struct Symbol { + Name *SymbolName = nullptr; + Type *SymbolType = nullptr; +}; + +} // namespace + +namespace { + +// Demangler class takes the main role in demangling symbols. +// It has a set of functions to parse mangled symbols into Type instances. +// It also has a set of functions to cnovert Type instances to strings. +class Demangler { +public: + Demangler() = default; + + // You are supposed to call parse() first and then check if error is true. If + // it is false, call output() to write the formatted name to the given stream. + Symbol *parse(StringView &MangledName); + void output(const Symbol *S, OutputStream &OS); + + // True if an error occurred. + bool Error = false; + +private: + Type *demangleVariableEncoding(StringView &MangledName); + Type *demangleFunctionEncoding(StringView &MangledName); + + Qualifiers demanglePointerExtQualifiers(StringView &MangledName); + + // Parser functions. This is a recursive-descent parser. + Type *demangleType(StringView &MangledName, QualifierMangleMode QMM); + Type *demangleBasicType(StringView &MangledName); + UdtType *demangleClassType(StringView &MangledName); + PointerType *demanglePointerType(StringView &MangledName); + MemberPointerType *demangleMemberPointerType(StringView &MangledName); + FunctionType *demangleFunctionType(StringView &MangledName, bool HasThisQuals, + bool IsFunctionPointer); + + ArrayType *demangleArrayType(StringView &MangledName); + + TemplateParams *demangleTemplateParameterList(StringView &MangledName); + FunctionParams demangleFunctionParameterList(StringView &MangledName); + + int demangleNumber(StringView &MangledName); + + void memorizeString(StringView s); + + /// Allocate a copy of \p Borrowed into memory that we own. + StringView copyString(StringView Borrowed); + + Name *demangleFullyQualifiedTypeName(StringView &MangledName); + Name *demangleFullyQualifiedSymbolName(StringView &MangledName); + + Name *demangleUnqualifiedTypeName(StringView &MangledName); + Name *demangleUnqualifiedSymbolName(StringView &MangledName); + + Name *demangleNameScopeChain(StringView &MangledName, Name *UnqualifiedName); + Name *demangleNameScopePiece(StringView &MangledName); + + Name *demangleBackRefName(StringView &MangledName); + Name *demangleClassTemplateName(StringView &MangledName); + Name *demangleOperatorName(StringView &MangledName); + Name *demangleSimpleName(StringView &MangledName, bool Memorize); + Name *demangleAnonymousNamespaceName(StringView &MangledName); + Name *demangleLocallyScopedNamePiece(StringView &MangledName); + + StringView demangleSimpleString(StringView &MangledName, bool Memorize); + + FuncClass demangleFunctionClass(StringView &MangledName); + CallingConv demangleCallingConvention(StringView &MangledName); + StorageClass demangleVariableStorageClass(StringView &MangledName); + ReferenceKind demangleReferenceKind(StringView &MangledName); + void demangleThrowSpecification(StringView &MangledName); + + std::pair<Qualifiers, bool> demangleQualifiers(StringView &MangledName); + + // Memory allocator. + ArenaAllocator Arena; + + // A single type uses one global back-ref table for all function params. + // This means back-refs can even go "into" other types. Examples: + // + // // Second int* is a back-ref to first. + // void foo(int *, int*); + // + // // Second int* is not a back-ref to first (first is not a function param). + // int* foo(int*); + // + // // Second int* is a back-ref to first (ALL function types share the same + // // back-ref map. + // using F = void(*)(int*); + // F G(int *); + Type *FunctionParamBackRefs[10]; + size_t FunctionParamBackRefCount = 0; + + // The first 10 BackReferences in a mangled name can be back-referenced by + // special name @[0-9]. This is a storage for the first 10 BackReferences. + StringView BackReferences[10]; + size_t BackRefCount = 0; +}; +} // namespace + +StringView Demangler::copyString(StringView Borrowed) { + char *Stable = Arena.allocUnalignedBuffer(Borrowed.size() + 1); + std::strcpy(Stable, Borrowed.begin()); + + return {Stable, Borrowed.size()}; +} + +// Parser entry point. +Symbol *Demangler::parse(StringView &MangledName) { + Symbol *S = Arena.alloc<Symbol>(); + + // MSVC-style mangled symbols must start with '?'. + if (!MangledName.consumeFront("?")) { + S->SymbolName = Arena.alloc<Name>(); + S->SymbolName->Str = MangledName; + S->SymbolType = Arena.alloc<Type>(); + S->SymbolType->Prim = PrimTy::Unknown; + return S; + } + + // What follows is a main symbol name. This may include + // namespaces or class BackReferences. + S->SymbolName = demangleFullyQualifiedSymbolName(MangledName); + + // Read a variable. + S->SymbolType = startsWithDigit(MangledName) + ? demangleVariableEncoding(MangledName) + : demangleFunctionEncoding(MangledName); + + return S; +} + +// <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 + +Type *Demangler::demangleVariableEncoding(StringView &MangledName) { + StorageClass SC = demangleVariableStorageClass(MangledName); + + Type *Ty = demangleType(MangledName, QualifierMangleMode::Drop); + + Ty->Storage = SC; + + // <variable-type> ::= <type> <cvr-qualifiers> + // ::= <type> <pointee-cvr-qualifiers> # pointers, references + switch (Ty->Prim) { + case PrimTy::Ptr: + case PrimTy::MemberPtr: { + Qualifiers ExtraChildQuals = Q_None; + Ty->Quals = + Qualifiers(Ty->Quals | demanglePointerExtQualifiers(MangledName)); + + bool IsMember = false; + std::tie(ExtraChildQuals, IsMember) = demangleQualifiers(MangledName); + + if (Ty->Prim == PrimTy::MemberPtr) { + assert(IsMember); + Name *BackRefName = demangleFullyQualifiedTypeName(MangledName); + (void)BackRefName; + MemberPointerType *MPTy = static_cast<MemberPointerType *>(Ty); + MPTy->Pointee->Quals = Qualifiers(MPTy->Pointee->Quals | ExtraChildQuals); + } else { + PointerType *PTy = static_cast<PointerType *>(Ty); + PTy->Pointee->Quals = Qualifiers(PTy->Pointee->Quals | ExtraChildQuals); + } + + break; + } + default: + Ty->Quals = demangleQualifiers(MangledName).first; + break; + } + + return Ty; +} + +// 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, ... +int Demangler::demangleNumber(StringView &MangledName) { + bool neg = MangledName.consumeFront("?"); + + if (startsWithDigit(MangledName)) { + int32_t Ret = MangledName[0] - '0' + 1; + MangledName = MangledName.dropFront(1); + return neg ? -Ret : Ret; + } + + int Ret = 0; + for (size_t i = 0; i < MangledName.size(); ++i) { + char C = MangledName[i]; + if (C == '@') { + MangledName = MangledName.dropFront(i + 1); + return neg ? -Ret : Ret; + } + if ('A' <= C && C <= 'P') { + Ret = (Ret << 4) + (C - 'A'); + continue; + } + break; + } + + Error = true; + return 0; +} + +// First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9. +// Memorize it. +void Demangler::memorizeString(StringView S) { + if (BackRefCount >= sizeof(BackReferences) / sizeof(*BackReferences)) + return; + for (size_t i = 0; i < BackRefCount; ++i) + if (S == BackReferences[i]) + return; + BackReferences[BackRefCount++] = S; +} + +Name *Demangler::demangleBackRefName(StringView &MangledName) { + assert(startsWithDigit(MangledName)); + + size_t I = MangledName[0] - '0'; + if (I >= BackRefCount) { + Error = true; + return nullptr; + } + + MangledName = MangledName.dropFront(); + Name *Node = Arena.alloc<Name>(); + Node->Str = BackReferences[I]; + return Node; +} + +Name *Demangler::demangleClassTemplateName(StringView &MangledName) { + assert(MangledName.startsWith("?$")); + MangledName.consumeFront("?$"); + + Name *Node = demangleSimpleName(MangledName, false); + Node->TParams = demangleTemplateParameterList(MangledName); + + // Render this class template name into a string buffer so that we can + // memorize it for the purpose of back-referencing. + OutputStream OS = OutputStream::create(nullptr, nullptr, 1024); + outputName(OS, Node); + OS << '\0'; + char *Name = OS.getBuffer(); + + StringView Owned = copyString(Name); + memorizeString(Owned); + std::free(Name); + + return Node; +} + +Name *Demangler::demangleOperatorName(StringView &MangledName) { + assert(MangledName.startsWith('?')); + MangledName.consumeFront('?'); + + auto NameString = [this, &MangledName]() -> StringView { + switch (MangledName.popFront()) { + case '0': + return "ctor"; + case '1': + return "dtor"; + case '2': + return " new"; + case '3': + return " delete"; + case '4': + return "="; + case '5': + return ">>"; + case '6': + return "<<"; + case '7': + return "!"; + case '8': + return "=="; + case '9': + return "!="; + case 'A': + return "[]"; + case 'C': + return "->"; + case 'D': + return "*"; + case 'E': + return "++"; + case 'F': + return "--"; + case 'G': + return "-"; + case 'H': + return "+"; + case 'I': + return "&"; + case 'J': + return "->*"; + case 'K': + return "/"; + case 'L': + return "%"; + case 'M': + return "<"; + case 'N': + return "<="; + case 'O': + return ">"; + case 'P': + return ">="; + case 'Q': + return ","; + case 'R': + return "()"; + case 'S': + return "~"; + case 'T': + return "^"; + case 'U': + return "|"; + case 'V': + return "&&"; + case 'W': + return "||"; + case 'X': + return "*="; + case 'Y': + return "+="; + case 'Z': + return "-="; + case '_': { + if (MangledName.empty()) + break; + + switch (MangledName.popFront()) { + case '0': + return "/="; + case '1': + return "%="; + case '2': + return ">>="; + case '3': + return "<<="; + case '4': + return "&="; + case '5': + return "|="; + case '6': + return "^="; + case 'U': + return " new[]"; + case 'V': + return " delete[]"; + case '_': + if (MangledName.consumeFront("L")) + return " co_await"; + if (MangledName.consumeFront("K")) { + size_t EndPos = MangledName.find('@'); + if (EndPos == StringView::npos) + break; + StringView OpName = demangleSimpleString(MangledName, false); + size_t FullSize = OpName.size() + 3; // <space>""OpName + char *Buffer = Arena.allocUnalignedBuffer(FullSize); + Buffer[0] = ' '; + Buffer[1] = '"'; + Buffer[2] = '"'; + std::memcpy(Buffer + 3, OpName.begin(), OpName.size()); + return {Buffer, FullSize}; + } + } + } + } + Error = true; + return ""; + }; + + Name *Node = Arena.alloc<Name>(); + Node->Operator = NameString(); + return Node; +} + +Name *Demangler::demangleSimpleName(StringView &MangledName, bool Memorize) { + StringView S = demangleSimpleString(MangledName, Memorize); + if (Error) + return nullptr; + + Name *Node = Arena.alloc<Name>(); + Node->Str = S; + return Node; +} + +StringView Demangler::demangleSimpleString(StringView &MangledName, + bool Memorize) { + StringView S; + for (size_t i = 0; i < MangledName.size(); ++i) { + if (MangledName[i] != '@') + continue; + S = MangledName.substr(0, i); + MangledName = MangledName.dropFront(i + 1); + + if (Memorize) + memorizeString(S); + return S; + } + + Error = true; + return {}; +} + +Name *Demangler::demangleAnonymousNamespaceName(StringView &MangledName) { + assert(MangledName.startsWith("?A")); + MangledName.consumeFront("?A"); + + Name *Node = Arena.alloc<Name>(); + Node->Str = "`anonymous namespace'"; + if (MangledName.consumeFront('@')) + return Node; + + Error = true; + return nullptr; +} + +Name *Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) { + assert(startsWithLocalScopePattern(MangledName)); + + Name *Node = Arena.alloc<Name>(); + MangledName.consumeFront('?'); + int ScopeIdentifier = demangleNumber(MangledName); + + // One ? to terminate the number + MangledName.consumeFront('?'); + + assert(!Error); + Symbol *Scope = parse(MangledName); + if (Error) + return nullptr; + + // Render the parent symbol's name into a buffer. + OutputStream OS = OutputStream::create(nullptr, nullptr, 1024); + OS << '`'; + output(Scope, OS); + OS << '\''; + OS << "::`" << ScopeIdentifier << "'"; + OS << '\0'; + char *Result = OS.getBuffer(); + Node->Str = copyString(Result); + std::free(Result); + return Node; +} + +// Parses a type name in the form of A@B@C@@ which represents C::B::A. +Name *Demangler::demangleFullyQualifiedTypeName(StringView &MangledName) { + Name *TypeName = demangleUnqualifiedTypeName(MangledName); + assert(TypeName); + + Name *QualName = demangleNameScopeChain(MangledName, TypeName); + assert(QualName); + return QualName; +} + +// 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. +Name *Demangler::demangleFullyQualifiedSymbolName(StringView &MangledName) { + Name *SymbolName = demangleUnqualifiedSymbolName(MangledName); + assert(SymbolName); + + Name *QualName = demangleNameScopeChain(MangledName, SymbolName); + assert(QualName); + return QualName; +} + +Name *Demangler::demangleUnqualifiedTypeName(StringView &MangledName) { + // 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 demangleClassTemplateName(MangledName); + + return demangleSimpleName(MangledName, true); +} + +Name *Demangler::demangleUnqualifiedSymbolName(StringView &MangledName) { + if (startsWithDigit(MangledName)) + return demangleBackRefName(MangledName); + if (MangledName.startsWith("?$")) + return demangleClassTemplateName(MangledName); + if (MangledName.startsWith('?')) + return demangleOperatorName(MangledName); + return demangleSimpleName(MangledName, true); +} + +Name *Demangler::demangleNameScopePiece(StringView &MangledName) { + if (startsWithDigit(MangledName)) + return demangleBackRefName(MangledName); + + if (MangledName.startsWith("?$")) + return demangleClassTemplateName(MangledName); + + if (MangledName.startsWith("?A")) + return demangleAnonymousNamespaceName(MangledName); + + if (startsWithLocalScopePattern(MangledName)) + return demangleLocallyScopedNamePiece(MangledName); + + return demangleSimpleName(MangledName, true); +} + +Name *Demangler::demangleNameScopeChain(StringView &MangledName, + Name *UnqualifiedName) { + Name *Head = UnqualifiedName; + + while (!MangledName.consumeFront("@")) { + if (MangledName.empty()) { + Error = true; + return nullptr; + } + + assert(!Error); + Name *Elem = demangleNameScopePiece(MangledName); + if (Error) + return nullptr; + + Elem->Next = Head; + Head = Elem; + } + return Head; +} + +FuncClass Demangler::demangleFunctionClass(StringView &MangledName) { + SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName); + RestoreOnError.shouldRestore(false); + + switch (MangledName.popFront()) { + case 'A': + return Private; + case 'B': + return FuncClass(Private | Far); + case 'C': + return FuncClass(Private | Static); + case 'D': + return FuncClass(Private | Static); + case 'E': + return FuncClass(Private | Virtual); + case 'F': + return FuncClass(Private | Virtual); + case 'I': + return Protected; + case 'J': + return FuncClass(Protected | Far); + case 'K': + return FuncClass(Protected | Static); + case 'L': + return FuncClass(Protected | Static | Far); + case 'M': + return FuncClass(Protected | Virtual); + case 'N': + return FuncClass(Protected | Virtual | Far); + case 'Q': + return Public; + case 'R': + return FuncClass(Public | Far); + case 'S': + return FuncClass(Public | Static); + case 'T': + return FuncClass(Public | Static | Far); + case 'U': + return FuncClass(Public | Virtual); + case 'V': + return FuncClass(Public | Virtual | Far); + case 'Y': + return Global; + case 'Z': + return FuncClass(Global | Far); + } + + Error = true; + RestoreOnError.shouldRestore(true); + return Public; +} + +CallingConv Demangler::demangleCallingConvention(StringView &MangledName) { + switch (MangledName.popFront()) { + case 'A': + case 'B': + return CallingConv::Cdecl; + case 'C': + case 'D': + return CallingConv::Pascal; + case 'E': + case 'F': + return CallingConv::Thiscall; + case 'G': + case 'H': + return CallingConv::Stdcall; + case 'I': + case 'J': + return CallingConv::Fastcall; + case 'M': + case 'N': + return CallingConv::Clrcall; + case 'O': + case 'P': + return CallingConv::Eabi; + case 'Q': + return CallingConv::Vectorcall; + } + + return CallingConv::None; +} + +StorageClass Demangler::demangleVariableStorageClass(StringView &MangledName) { + assert(std::isdigit(MangledName.front())); + + switch (MangledName.popFront()) { + case '0': + return StorageClass::PrivateStatic; + case '1': + return StorageClass::ProtectedStatic; + case '2': + return StorageClass::PublicStatic; + case '3': + return StorageClass::Global; + case '4': + return StorageClass::FunctionLocalStatic; + } + Error = true; + return StorageClass::None; +} + +std::pair<Qualifiers, bool> +Demangler::demangleQualifiers(StringView &MangledName) { + + switch (MangledName.popFront()) { + // 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); +} + +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 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"); +} + +// <variable-type> ::= <type> <cvr-qualifiers> +// ::= <type> <pointee-cvr-qualifiers> # pointers, references +Type *Demangler::demangleType(StringView &MangledName, + QualifierMangleMode QMM) { + Qualifiers Quals = Q_None; + bool IsMember = false; + bool IsMemberKnown = false; + if (QMM == QualifierMangleMode::Mangle) { + std::tie(Quals, IsMember) = demangleQualifiers(MangledName); + IsMemberKnown = true; + } else if (QMM == QualifierMangleMode::Result) { + if (MangledName.consumeFront('?')) { + std::tie(Quals, IsMember) = demangleQualifiers(MangledName); + IsMemberKnown = true; + } + } + + Type *Ty = nullptr; + if (isTagType(MangledName)) + Ty = demangleClassType(MangledName); + else if (isPointerType(MangledName)) { + if (!IsMemberKnown) + IsMember = isMemberPointer(MangledName); + + if (IsMember) + Ty = demangleMemberPointerType(MangledName); + else + Ty = demanglePointerType(MangledName); + } else if (isArrayType(MangledName)) + Ty = demangleArrayType(MangledName); + else if (isFunctionType(MangledName)) { + if (MangledName.consumeFront("$$A8@@")) + Ty = demangleFunctionType(MangledName, true, false); + else { + assert(MangledName.startsWith("$$A6")); + MangledName.consumeFront("$$A6"); + Ty = demangleFunctionType(MangledName, false, false); + } + } else { + Ty = demangleBasicType(MangledName); + assert(Ty && !Error); + if (!Ty || Error) + return Ty; + } + + Ty->Quals = Qualifiers(Ty->Quals | Quals); + return Ty; +} + +ReferenceKind Demangler::demangleReferenceKind(StringView &MangledName) { + if (MangledName.consumeFront('G')) + return ReferenceKind::LValueRef; + else if (MangledName.consumeFront('H')) + return ReferenceKind::RValueRef; + return ReferenceKind::None; +} + +void Demangler::demangleThrowSpecification(StringView &MangledName) { + if (MangledName.consumeFront('Z')) + return; + + Error = true; +} + +FunctionType *Demangler::demangleFunctionType(StringView &MangledName, + bool HasThisQuals, + bool IsFunctionPointer) { + FunctionType *FTy = Arena.alloc<FunctionType>(); + FTy->Prim = PrimTy::Function; + FTy->IsFunctionPointer = IsFunctionPointer; + + if (HasThisQuals) { + FTy->Quals = demanglePointerExtQualifiers(MangledName); + FTy->RefKind = demangleReferenceKind(MangledName); + FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers(MangledName).first); + } + + // 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); + + demangleThrowSpecification(MangledName); + + return FTy; +} + +Type *Demangler::demangleFunctionEncoding(StringView &MangledName) { + FuncClass FC = demangleFunctionClass(MangledName); + + bool HasThisQuals = !(FC & (Global | Static)); + FunctionType *FTy = demangleFunctionType(MangledName, HasThisQuals, false); + FTy->FunctionClass = FC; + + return FTy; +} + +// Reads a primitive type. +Type *Demangler::demangleBasicType(StringView &MangledName) { + Type *Ty = Arena.alloc<Type>(); + + if (MangledName.consumeFront("$$T")) { + Ty->Prim = PrimTy::Nullptr; + return Ty; + } + + switch (MangledName.popFront()) { + case 'X': + Ty->Prim = PrimTy::Void; + break; + case 'D': + Ty->Prim = PrimTy::Char; + break; + case 'C': + Ty->Prim = PrimTy::Schar; + break; + case 'E': + Ty->Prim = PrimTy::Uchar; + break; + case 'F': + Ty->Prim = PrimTy::Short; + break; + case 'G': + Ty->Prim = PrimTy::Ushort; + break; + case 'H': + Ty->Prim = PrimTy::Int; + break; + case 'I': + Ty->Prim = PrimTy::Uint; + break; + case 'J': + Ty->Prim = PrimTy::Long; + break; + case 'K': + Ty->Prim = PrimTy::Ulong; + break; + case 'M': + Ty->Prim = PrimTy::Float; + break; + case 'N': + Ty->Prim = PrimTy::Double; + break; + case 'O': + Ty->Prim = PrimTy::Ldouble; + break; + case '_': { + if (MangledName.empty()) { + Error = true; + return nullptr; + } + switch (MangledName.popFront()) { + case 'N': + Ty->Prim = PrimTy::Bool; + break; + case 'J': + Ty->Prim = PrimTy::Int64; + break; + case 'K': + Ty->Prim = PrimTy::Uint64; + break; + case 'W': + Ty->Prim = PrimTy::Wchar; + break; + case 'S': + Ty->Prim = PrimTy::Char16; + break; + case 'U': + Ty->Prim = PrimTy::Char32; + break; + default: + Error = true; + return nullptr; + } + break; + } + default: + Error = true; + return nullptr; + } + return Ty; +} + +UdtType *Demangler::demangleClassType(StringView &MangledName) { + UdtType *UTy = Arena.alloc<UdtType>(); + + switch (MangledName.popFront()) { + case 'T': + UTy->Prim = PrimTy::Union; + break; + case 'U': + UTy->Prim = PrimTy::Struct; + break; + case 'V': + UTy->Prim = PrimTy::Class; + break; + case 'W': + if (MangledName.popFront() != '4') { + Error = true; + return nullptr; + } + UTy->Prim = PrimTy::Enum; + break; + default: + assert(false); + } + + UTy->UdtName = demangleFullyQualifiedTypeName(MangledName); + return UTy; +} + +static std::pair<Qualifiers, PointerAffinity> +demanglePointerCVQualifiers(StringView &MangledName) { + if (MangledName.consumeFront("$$Q")) + return std::make_pair(Q_None, PointerAffinity::RValueReference); + + switch (MangledName.popFront()) { + case 'A': + return std::make_pair(Q_None, PointerAffinity::Reference); + case 'P': + return std::make_pair(Q_None, PointerAffinity::Pointer); + case 'Q': + return std::make_pair(Q_Const, PointerAffinity::Pointer); + case 'R': + return std::make_pair(Q_Volatile, PointerAffinity::Pointer); + case 'S': + return std::make_pair(Qualifiers(Q_Const | Q_Volatile), + PointerAffinity::Pointer); + default: + assert(false && "Ty is not a pointer type!"); + } + return std::make_pair(Q_None, PointerAffinity::Pointer); +} + +// <pointer-type> ::= E? <pointer-cvr-qualifiers> <ext-qualifiers> <type> +// # the E is required for 64-bit non-static pointers +PointerType *Demangler::demanglePointerType(StringView &MangledName) { + PointerType *Pointer = Arena.alloc<PointerType>(); + + std::tie(Pointer->Quals, Pointer->Affinity) = + demanglePointerCVQualifiers(MangledName); + + Pointer->Prim = PrimTy::Ptr; + if (MangledName.consumeFront("6")) { + Pointer->Pointee = demangleFunctionType(MangledName, false, true); + return Pointer; + } + + Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName); + Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals); + + Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Mangle); + return Pointer; +} + +MemberPointerType * +Demangler::demangleMemberPointerType(StringView &MangledName) { + MemberPointerType *Pointer = Arena.alloc<MemberPointerType>(); + Pointer->Prim = PrimTy::MemberPtr; + + PointerAffinity Affinity; + std::tie(Pointer->Quals, Affinity) = demanglePointerCVQualifiers(MangledName); + assert(Affinity == PointerAffinity::Pointer); + + Qualifiers ExtQuals = demanglePointerExtQualifiers(MangledName); + Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals); + + if (MangledName.consumeFront("8")) { + Pointer->MemberName = demangleFullyQualifiedSymbolName(MangledName); + Pointer->Pointee = demangleFunctionType(MangledName, true, true); + } else { + Qualifiers PointeeQuals = Q_None; + bool IsMember = false; + std::tie(PointeeQuals, IsMember) = demangleQualifiers(MangledName); + assert(IsMember); + Pointer->MemberName = demangleFullyQualifiedSymbolName(MangledName); + + Pointer->Pointee = demangleType(MangledName, QualifierMangleMode::Drop); + Pointer->Pointee->Quals = PointeeQuals; + } + + return Pointer; +} + +Qualifiers Demangler::demanglePointerExtQualifiers(StringView &MangledName) { + Qualifiers Quals = Q_None; + if (MangledName.consumeFront('E')) + Quals = Qualifiers(Quals | Q_Pointer64); + if (MangledName.consumeFront('I')) + Quals = Qualifiers(Quals | Q_Restrict); + if (MangledName.consumeFront('F')) + Quals = Qualifiers(Quals | Q_Unaligned); + + return Quals; +} + +ArrayType *Demangler::demangleArrayType(StringView &MangledName) { + assert(MangledName.front() == 'Y'); + MangledName.popFront(); + + int Dimension = demangleNumber(MangledName); + if (Dimension <= 0) { + Error = true; + return nullptr; + } + + ArrayType *ATy = Arena.alloc<ArrayType>(); + ArrayType *Dim = ATy; + for (int I = 0; I < Dimension; ++I) { + Dim->Prim = PrimTy::Array; + Dim->ArrayDimension = demangleNumber(MangledName); + Dim->NextDimension = Arena.alloc<ArrayType>(); + Dim = Dim->NextDimension; + } + + if (MangledName.consumeFront("$$C")) { + if (MangledName.consumeFront("B")) + ATy->Quals = Q_Const; + else if (MangledName.consumeFront("C") || MangledName.consumeFront("D")) + ATy->Quals = Qualifiers(Q_Const | Q_Volatile); + else if (!MangledName.consumeFront("A")) + Error = true; + } + + ATy->ElementType = demangleType(MangledName, QualifierMangleMode::Drop); + Dim->ElementType = ATy->ElementType; + return ATy; +} + +// Reads a function or a template parameters. +FunctionParams +Demangler::demangleFunctionParameterList(StringView &MangledName) { + // Empty parameter list. + if (MangledName.consumeFront('X')) + return {}; + + FunctionParams *Head; + FunctionParams **Current = &Head; + while (!Error && !MangledName.startsWith('@') && + !MangledName.startsWith('Z')) { + + if (startsWithDigit(MangledName)) { + size_t N = MangledName[0] - '0'; + if (N >= FunctionParamBackRefCount) { + Error = true; + return {}; + } + MangledName = MangledName.dropFront(); + + *Current = Arena.alloc<FunctionParams>(); + (*Current)->Current = FunctionParamBackRefs[N]->clone(Arena); + Current = &(*Current)->Next; + continue; + } + + size_t OldSize = MangledName.size(); + + *Current = Arena.alloc<FunctionParams>(); + (*Current)->Current = demangleType(MangledName, QualifierMangleMode::Drop); + + size_t CharsConsumed = OldSize - MangledName.size(); + assert(CharsConsumed != 0); + + // Single-letter types are ignored for backreferences because memorizing + // them doesn't save anything. + if (FunctionParamBackRefCount <= 9 && CharsConsumed > 1) + FunctionParamBackRefs[FunctionParamBackRefCount++] = (*Current)->Current; + + Current = &(*Current)->Next; + } + + if (Error) + return {}; + + // 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 *Head; + + if (MangledName.consumeFront('Z')) { + Head->IsVariadic = true; + return *Head; + } + + Error = true; + return {}; +} + +TemplateParams * +Demangler::demangleTemplateParameterList(StringView &MangledName) { + TemplateParams *Head; + TemplateParams **Current = &Head; + while (!Error && !MangledName.startsWith('@')) { + // Template parameter lists don't participate in back-referencing. + *Current = Arena.alloc<TemplateParams>(); + + // Empty parameter pack. + if (MangledName.consumeFront("$S") || MangledName.consumeFront("$$V") || + MangledName.consumeFront("$$$V")) { + if (!MangledName.startsWith('@')) + Error = true; + continue; + } + + if (MangledName.consumeFront("$$Y")) { + (*Current)->IsTemplateTemplate = true; + (*Current)->IsAliasTemplate = true; + (*Current)->ParamName = demangleFullyQualifiedTypeName(MangledName); + } else if (MangledName.consumeFront("$1?")) { + (*Current)->ParamName = demangleFullyQualifiedSymbolName(MangledName); + (*Current)->ParamType = demangleFunctionEncoding(MangledName); + } else { + (*Current)->ParamType = + demangleType(MangledName, QualifierMangleMode::Drop); + } + + Current = &(*Current)->Next; + } + + if (Error) + return {}; + + // Template parameter lists cannot be variadic, so it can only be terminated + // by @. + if (MangledName.consumeFront('@')) + return Head; + Error = true; + return {}; +} + +void Demangler::output(const Symbol *S, OutputStream &OS) { + // Converts an AST to a string. + // + // Converting an AST representing a C++ type to a string is tricky due + // to the bad grammar of the C++ declaration inherited from C. You have + // to construct a string from inside to outside. For example, if a type + // X is a pointer to a function returning int, the order you create a + // string becomes something like this: + // + // (1) X is a pointer: *X + // (2) (1) is a function returning int: int (*X)() + // + // So you cannot construct a result just by appending strings to a result. + // + // To deal with this, we split the function into two. outputPre() writes + // the "first half" of type declaration, and outputPost() writes the + // "second half". For example, outputPre() writes a return type for a + // function and outputPost() writes an parameter list. + Type::outputPre(OS, *S->SymbolType); + outputName(OS, S->SymbolName); + Type::outputPost(OS, *S->SymbolType); +} + +char *llvm::microsoftDemangle(const char *MangledName, char *Buf, size_t *N, + int *Status) { + Demangler D; + StringView Name{MangledName}; + Symbol *S = D.parse(Name); + + if (D.Error) + *Status = llvm::demangle_invalid_mangled_name; + else + *Status = llvm::demangle_success; + + OutputStream OS = OutputStream::create(Buf, N, 1024); + D.output(S, OS); + OS << '\0'; + return OS.getBuffer(); +} |
