| //===- 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 { |
| class ArenaAllocator { |
| struct AllocatorNode { |
| uint8_t *Buf = nullptr; |
| size_t Used = 0; |
| AllocatorNode *Next = nullptr; |
| }; |
| |
| public: |
| ArenaAllocator() : Head(new AllocatorNode) { Head->Buf = new uint8_t[Unit]; } |
| |
| ~ArenaAllocator() { |
| while (Head) { |
| assert(Head->Buf); |
| delete[] Head->Buf; |
| AllocatorNode *Next = Head->Next; |
| delete Head; |
| Head = Next; |
| } |
| } |
| |
| template <typename T, typename... Args> T *alloc(Args &&... ConstructorArgs) { |
| |
| size_t Size = sizeof(T); |
| assert(Size < Unit); |
| 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 < Unit) |
| return new (PP) T(std::forward<Args>(ConstructorArgs)...); |
| |
| AllocatorNode *NewHead = new AllocatorNode; |
| NewHead->Buf = new uint8_t[ArenaAllocator::Unit]; |
| NewHead->Next = Head; |
| Head = NewHead; |
| NewHead->Used = Size; |
| return new (NewHead->Buf) T(std::forward<Args>(ConstructorArgs)...); |
| } |
| |
| private: |
| static constexpr size_t Unit = 4096; |
| |
| 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 }; |
| |
| // 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, |
| Ref, |
| MemberPtr, |
| Array, |
| |
| Struct, |
| Union, |
| Class, |
| Enum, |
| |
| Void, |
| Bool, |
| Char, |
| Schar, |
| Uchar, |
| Short, |
| Ushort, |
| Int, |
| Uint, |
| Long, |
| Ulong, |
| Int64, |
| Uint64, |
| Wchar, |
| Float, |
| Double, |
| Ldouble, |
| }; |
| |
| // 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; |
| |
| // Represents a list of parameters (template params or function arguments. |
| // It's represented as a linked list. |
| struct ParamList { |
| bool IsVariadic = false; |
| |
| Type *Current = nullptr; |
| |
| ParamList *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. |
| ParamList TemplateParams; |
| |
| // 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; |
| |
| // 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; |
| |
| ParamList 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 'A': |
| // 'A' indicates a reference, and you cannot have a reference to a member |
| // function or member variable. |
| 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; |
| } |
| } |
| |
| // Write a function or template parameter list. |
| static void outputParameterList(OutputStream &OS, const ParamList &Params) { |
| if (!Params.Current) { |
| OS << "void"; |
| return; |
| } |
| |
| const ParamList *Head = &Params; |
| while (Head) { |
| Type::outputPre(OS, *Head->Current); |
| Type::outputPost(OS, *Head->Current); |
| |
| Head = Head->Next; |
| |
| if (Head) |
| OS << ", "; |
| } |
| } |
| |
| static void outputTemplateParams(OutputStream &OS, const Name &TheName) { |
| if (!TheName.TemplateParams.Current) |
| return; |
| |
| OS << "<"; |
| outputParameterList(OS, TheName.TemplateParams); |
| OS << ">"; |
| } |
| |
| static void outputName(OutputStream &OS, const Name *TheName) { |
| if (!TheName) |
| return; |
| |
| outputSpaceIfNecessary(OS); |
| |
| // Print out namespaces or outer class BackReferences. |
| for (; TheName->Next; TheName = TheName->Next) { |
| OS << TheName->Str; |
| outputTemplateParams(OS, *TheName); |
| OS << "::"; |
| } |
| |
| // Print out a regular name. |
| if (TheName->Operator.empty()) { |
| OS << TheName->Str; |
| outputTemplateParams(OS, *TheName); |
| return; |
| } |
| |
| // Print out ctor or dtor. |
| if (TheName->Operator == "ctor" || TheName->Operator == "dtor") { |
| OS << TheName->Str; |
| outputTemplateParams(OS, *TheName); |
| OS << "::"; |
| if (TheName->Operator == "dtor") |
| OS << "~"; |
| OS << TheName->Str; |
| outputTemplateParams(OS, *TheName); |
| 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::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; |
| 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 |
| OS << "&"; |
| } |
| |
| void PointerType::outputPre(OutputStream &OS) { |
| Type::outputPre(OS, *Pointee); |
| |
| outputSpaceIfNecessary(OS); |
| |
| if (Quals & Q_Unaligned) |
| OS << "__unaligned "; |
| |
| PointerAffinity Affinity = (Prim == PrimTy::Ptr) ? PointerAffinity::Pointer |
| : PointerAffinity::Reference; |
| |
| 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 (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); |
| } |
| |
| } // 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(OutputStream &OS, StringView s) : OS(OS), MangledName(s) {} |
| |
| // 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. |
| void parse(); |
| void output(); |
| |
| // True if an error occurred. |
| bool Error = false; |
| |
| private: |
| Type *demangleVariableEncoding(); |
| Type *demangleFunctionEncoding(); |
| |
| Qualifiers demanglePointerExtQualifiers(); |
| |
| // Parser functions. This is a recursive-descent parser. |
| Type *demangleType(QualifierMangleMode QMM); |
| Type *demangleBasicType(); |
| UdtType *demangleClassType(); |
| PointerType *demanglePointerType(); |
| MemberPointerType *demangleMemberPointerType(); |
| FunctionType *demangleFunctionType(bool HasThisQuals, bool IsFunctionPointer); |
| |
| ArrayType *demangleArrayType(); |
| |
| ParamList demangleParameterList(); |
| |
| int demangleNumber(); |
| void demangleNamePiece(Name &Node, bool IsHead); |
| |
| StringView demangleString(bool memorize); |
| void memorizeString(StringView s); |
| Name *demangleName(); |
| void demangleOperator(Name *); |
| StringView demangleOperatorName(); |
| FuncClass demangleFunctionClass(); |
| CallingConv demangleCallingConvention(); |
| StorageClass demangleVariableStorageClass(); |
| ReferenceKind demangleReferenceKind(); |
| void demangleThrowSpecification(); |
| |
| std::pair<Qualifiers, bool> demangleQualifiers(); |
| |
| // The result is written to this stream. |
| OutputStream OS; |
| |
| // Mangled symbol. demangle* functions shorten this string |
| // as they parse it. |
| StringView MangledName; |
| |
| // A parsed mangled symbol. |
| Type *SymbolType = nullptr; |
| |
| // The main symbol name. (e.g. "ns::foo" in "int ns::foo()".) |
| Name *SymbolName = nullptr; |
| |
| // Memory allocator. |
| ArenaAllocator Arena; |
| |
| // 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 |
| |
| // Parser entry point. |
| void Demangler::parse() { |
| // MSVC-style mangled symbols must start with '?'. |
| if (!MangledName.consumeFront("?")) { |
| SymbolName = Arena.alloc<Name>(); |
| SymbolName->Str = MangledName; |
| SymbolType = Arena.alloc<Type>(); |
| SymbolType->Prim = PrimTy::Unknown; |
| } |
| |
| // What follows is a main symbol name. This may include |
| // namespaces or class BackReferences. |
| SymbolName = demangleName(); |
| |
| // Read a variable. |
| if (startsWithDigit(MangledName)) { |
| SymbolType = demangleVariableEncoding(); |
| return; |
| } |
| |
| // Read a function. |
| SymbolType = demangleFunctionEncoding(); |
| } |
| |
| // <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() { |
| StorageClass SC = demangleVariableStorageClass(); |
| |
| Type *Ty = demangleType(QualifierMangleMode::Drop); |
| |
| Ty->Storage = SC; |
| |
| // <variable-type> ::= <type> <cvr-qualifiers> |
| // ::= <type> <pointee-cvr-qualifiers> # pointers, references |
| switch (Ty->Prim) { |
| case PrimTy::Ptr: |
| case PrimTy::Ref: |
| case PrimTy::MemberPtr: { |
| Qualifiers ExtraChildQuals = Q_None; |
| Ty->Quals = Qualifiers(Ty->Quals | demanglePointerExtQualifiers()); |
| |
| bool IsMember = false; |
| std::tie(ExtraChildQuals, IsMember) = demangleQualifiers(); |
| |
| if (Ty->Prim == PrimTy::MemberPtr) { |
| assert(IsMember); |
| Name *BackRefName = demangleName(); |
| (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().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() { |
| 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; |
| } |
| |
| // Read until the next '@'. |
| StringView Demangler::demangleString(bool Memorize) { |
| for (size_t i = 0; i < MangledName.size(); ++i) { |
| if (MangledName[i] != '@') |
| continue; |
| StringView ret = MangledName.substr(0, i); |
| MangledName = MangledName.dropFront(i + 1); |
| |
| if (Memorize) |
| memorizeString(ret); |
| return ret; |
| } |
| |
| Error = true; |
| return ""; |
| } |
| |
| // 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; |
| } |
| |
| void Demangler::demangleNamePiece(Name &Node, bool IsHead) { |
| if (startsWithDigit(MangledName)) { |
| size_t I = MangledName[0] - '0'; |
| if (I >= BackRefCount) { |
| Error = true; |
| return; |
| } |
| MangledName = MangledName.dropFront(); |
| Node.Str = BackReferences[I]; |
| } else if (MangledName.consumeFront("?$")) { |
| // Class template. |
| Node.Str = demangleString(false); |
| Node.TemplateParams = demangleParameterList(); |
| } else if (!IsHead && MangledName.consumeFront("?A")) { |
| // Anonymous namespace starts with ?A. So does overloaded operator[], |
| // but the distinguishing factor is that namespace themselves are not |
| // mangled, only the variables and functions inside of them are. So |
| // an anonymous namespace will never occur as the first item in the |
| // name. |
| Node.Str = "`anonymous namespace'"; |
| if (!MangledName.consumeFront('@')) { |
| Error = true; |
| return; |
| } |
| } else if (MangledName.consumeFront("?")) { |
| // Overloaded operator. |
| demangleOperator(&Node); |
| } else { |
| // Non-template functions or classes. |
| Node.Str = demangleString(true); |
| } |
| } |
| |
| // Parses a name in the form of A@B@C@@ which represents C::B::A. |
| Name *Demangler::demangleName() { |
| Name *Head = nullptr; |
| |
| while (!MangledName.consumeFront("@")) { |
| Name *Elem = Arena.alloc<Name>(); |
| |
| assert(!Error); |
| demangleNamePiece(*Elem, Head == nullptr); |
| if (Error) |
| return nullptr; |
| |
| Elem->Next = Head; |
| Head = Elem; |
| if (MangledName.empty()) { |
| Error = true; |
| return nullptr; |
| } |
| } |
| |
| return Head; |
| } |
| |
| void Demangler::demangleOperator(Name *OpName) { |
| OpName->Operator = demangleOperatorName(); |
| if (!Error && !MangledName.empty() && MangledName.front() != '@') |
| demangleNamePiece(*OpName, false); |
| } |
| |
| StringView Demangler::demangleOperatorName() { |
| SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName); |
| RestoreOnError.shouldRestore(false); |
| |
| 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"; |
| } |
| } |
| } |
| |
| Error = true; |
| RestoreOnError.shouldRestore(true); |
| return ""; |
| } |
| |
| FuncClass Demangler::demangleFunctionClass() { |
| 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() { |
| 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() { |
| 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() { |
| |
| switch (MangledName.popFront()) { |
| // Member qualifiers |
| case 'Q': |
| return std::make_pair(Q_None, true); |
| case 'R': |
| return std::make_pair(Q_Const, true); |
| case 'S': |
| return std::make_pair(Q_Volatile, true); |
| case 'T': |
| return std::make_pair(Qualifiers(Q_Const | Q_Volatile), true); |
| // Non-Member qualifiers |
| case 'A': |
| return std::make_pair(Q_None, false); |
| case 'B': |
| return std::make_pair(Q_Const, false); |
| case 'C': |
| return std::make_pair(Q_Volatile, false); |
| case 'D': |
| return std::make_pair(Qualifiers(Q_Const | Q_Volatile), false); |
| } |
| Error = true; |
| return std::make_pair(Q_None, false); |
| } |
| |
| // <variable-type> ::= <type> <cvr-qualifiers> |
| // ::= <type> <pointee-cvr-qualifiers> # pointers, references |
| Type *Demangler::demangleType(QualifierMangleMode QMM) { |
| Qualifiers Quals = Q_None; |
| bool IsMember = false; |
| bool IsMemberKnown = false; |
| if (QMM == QualifierMangleMode::Mangle) { |
| std::tie(Quals, IsMember) = demangleQualifiers(); |
| IsMemberKnown = true; |
| } else if (QMM == QualifierMangleMode::Result) { |
| if (MangledName.consumeFront('?')) { |
| std::tie(Quals, IsMember) = demangleQualifiers(); |
| IsMemberKnown = true; |
| } |
| } |
| |
| Type *Ty = nullptr; |
| switch (MangledName.front()) { |
| case 'T': // union |
| case 'U': // struct |
| case 'V': // class |
| case 'W': // enum |
| Ty = demangleClassType(); |
| break; |
| case 'A': // foo & |
| case 'P': // foo * |
| case 'Q': // foo *const |
| case 'R': // foo *volatile |
| case 'S': // foo *const volatile |
| if (!IsMemberKnown) |
| IsMember = isMemberPointer(MangledName); |
| if (IsMember) |
| Ty = demangleMemberPointerType(); |
| else |
| Ty = demanglePointerType(); |
| break; |
| case 'Y': |
| Ty = demangleArrayType(); |
| break; |
| default: |
| Ty = demangleBasicType(); |
| break; |
| } |
| Ty->Quals = Qualifiers(Ty->Quals | Quals); |
| return Ty; |
| } |
| |
| ReferenceKind Demangler::demangleReferenceKind() { |
| if (MangledName.consumeFront('G')) |
| return ReferenceKind::LValueRef; |
| else if (MangledName.consumeFront('H')) |
| return ReferenceKind::RValueRef; |
| return ReferenceKind::None; |
| } |
| |
| void Demangler::demangleThrowSpecification() { |
| if (MangledName.consumeFront('Z')) |
| return; |
| |
| Error = true; |
| } |
| |
| FunctionType *Demangler::demangleFunctionType(bool HasThisQuals, |
| bool IsFunctionPointer) { |
| FunctionType *FTy = Arena.alloc<FunctionType>(); |
| FTy->Prim = PrimTy::Function; |
| FTy->IsFunctionPointer = IsFunctionPointer; |
| |
| if (HasThisQuals) { |
| FTy->Quals = demanglePointerExtQualifiers(); |
| FTy->RefKind = demangleReferenceKind(); |
| FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers().first); |
| } |
| |
| // Fields that appear on both member and non-member functions. |
| FTy->CallConvention = demangleCallingConvention(); |
| |
| // <return-type> ::= <type> |
| // ::= @ # structors (they have no declared return type) |
| bool IsStructor = MangledName.consumeFront('@'); |
| if (!IsStructor) |
| FTy->ReturnType = demangleType(QualifierMangleMode::Result); |
| |
| FTy->Params = demangleParameterList(); |
| |
| demangleThrowSpecification(); |
| |
| return FTy; |
| } |
| |
| Type *Demangler::demangleFunctionEncoding() { |
| FuncClass FC = demangleFunctionClass(); |
| |
| bool HasThisQuals = !(FC & (Global | Static)); |
| FunctionType *FTy = demangleFunctionType(HasThisQuals, false); |
| FTy->FunctionClass = FC; |
| |
| return FTy; |
| } |
| |
| // Reads a primitive type. |
| Type *Demangler::demangleBasicType() { |
| Type *Ty = Arena.alloc<Type>(); |
| |
| 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; |
| default: |
| assert(false); |
| } |
| break; |
| } |
| } |
| return Ty; |
| } |
| |
| UdtType *Demangler::demangleClassType() { |
| 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 = demangleName(); |
| return UTy; |
| } |
| |
| static std::pair<Qualifiers, PointerAffinity> |
| demanglePointerCVQualifiers(StringView &MangledName) { |
| 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() { |
| PointerType *Pointer = Arena.alloc<PointerType>(); |
| |
| PointerAffinity Affinity; |
| std::tie(Pointer->Quals, Affinity) = demanglePointerCVQualifiers(MangledName); |
| |
| Pointer->Prim = |
| (Affinity == PointerAffinity::Pointer) ? PrimTy::Ptr : PrimTy::Ref; |
| if (MangledName.consumeFront("6")) { |
| Pointer->Pointee = demangleFunctionType(false, true); |
| return Pointer; |
| } |
| |
| Qualifiers ExtQuals = demanglePointerExtQualifiers(); |
| Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals); |
| |
| Pointer->Pointee = demangleType(QualifierMangleMode::Mangle); |
| return Pointer; |
| } |
| |
| MemberPointerType *Demangler::demangleMemberPointerType() { |
| 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(); |
| Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals); |
| |
| if (MangledName.consumeFront("8")) { |
| Pointer->MemberName = demangleName(); |
| Pointer->Pointee = demangleFunctionType(true, true); |
| } else { |
| Qualifiers PointeeQuals = Q_None; |
| bool IsMember = false; |
| std::tie(PointeeQuals, IsMember) = demangleQualifiers(); |
| assert(IsMember); |
| Pointer->MemberName = demangleName(); |
| |
| Pointer->Pointee = demangleType(QualifierMangleMode::Drop); |
| Pointer->Pointee->Quals = PointeeQuals; |
| } |
| |
| return Pointer; |
| } |
| |
| Qualifiers Demangler::demanglePointerExtQualifiers() { |
| 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() { |
| assert(MangledName.front() == 'Y'); |
| MangledName.popFront(); |
| |
| int Dimension = demangleNumber(); |
| 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(); |
| 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(QualifierMangleMode::Drop); |
| Dim->ElementType = ATy->ElementType; |
| return ATy; |
| } |
| |
| // Reads a function or a template parameters. |
| ParamList Demangler::demangleParameterList() { |
| // Within the same parameter list, you can backreference the first 10 types. |
| Type *BackRef[10]; |
| int Idx = 0; |
| |
| // Empty parameter list. |
| // FIXME: Will this cause problems if demangleParameterList() is called in the |
| // context of a template parameter list? |
| if (MangledName.consumeFront('X')) |
| return {}; |
| |
| ParamList *Head; |
| ParamList **Current = &Head; |
| while (!Error && !MangledName.startsWith('@') && |
| !MangledName.startsWith('Z')) { |
| if (startsWithDigit(MangledName)) { |
| int N = MangledName[0] - '0'; |
| if (N >= Idx) { |
| Error = true; |
| return {}; |
| } |
| MangledName = MangledName.dropFront(); |
| |
| *Current = Arena.alloc<ParamList>(); |
| (*Current)->Current = BackRef[N]->clone(Arena); |
| Current = &(*Current)->Next; |
| continue; |
| } |
| |
| size_t ArrayDimension = MangledName.size(); |
| |
| *Current = Arena.alloc<ParamList>(); |
| (*Current)->Current = demangleType(QualifierMangleMode::Drop); |
| |
| // Single-letter types are ignored for backreferences because |
| // memorizing them doesn't save anything. |
| if (Idx <= 9 && ArrayDimension - MangledName.size() > 1) |
| BackRef[Idx++] = (*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 {}; |
| } |
| |
| void Demangler::output() { |
| // 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, *SymbolType); |
| outputName(OS, SymbolName); |
| Type::outputPost(OS, *SymbolType); |
| |
| // Null terminate the buffer. |
| OS << '\0'; |
| } |
| |
| char *llvm::microsoftDemangle(const char *MangledName, char *Buf, size_t *N, |
| int *Status) { |
| OutputStream OS = OutputStream::create(Buf, N, 1024); |
| |
| Demangler D(OS, StringView(MangledName)); |
| D.parse(); |
| |
| if (D.Error) |
| *Status = llvm::demangle_invalid_mangled_name; |
| else |
| *Status = llvm::demangle_success; |
| |
| D.output(); |
| return OS.getBuffer(); |
| } |