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