| //===- 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(); | |
| } |