| //===- 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 "llvm/Demangle/Compiler.h" |
| #include "llvm/Demangle/StringView.h" |
| #include "llvm/Demangle/Utility.h" |
| |
| #include <array> |
| #include <cctype> |
| #include <cstdio> |
| #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, |
| Custom, |
| Vftable, |
| Vbtable, |
| LocalStaticGuard |
| }; |
| |
| enum class OperatorTy : uint8_t { |
| Ctor, // ?0 # Foo::Foo() |
| Dtor, // ?1 # Foo::~Foo() |
| New, // ?2 # operator new |
| Delete, // ?3 # operator delete |
| Assign, // ?4 # operator= |
| RightShift, // ?5 # operator>> |
| LeftShift, // ?6 # operator<< |
| LogicalNot, // ?7 # operator! |
| Equals, // ?8 # operator== |
| NotEquals, // ?9 # operator!= |
| ArraySubscript, // ?A # operator[] |
| Conversion, // ?B # Foo::operator <type>() |
| Pointer, // ?C # operator-> |
| Dereference, // ?D # operator* |
| Increment, // ?E # operator++ |
| Decrement, // ?F # operator-- |
| Minus, // ?G # operator- |
| Plus, // ?H # operator+ |
| BitwiseAnd, // ?I # operator& |
| MemberPointer, // ?J # operator->* |
| Divide, // ?K # operator/ |
| Modulus, // ?L # operator% |
| LessThan, // ?M operator< |
| LessThanEqual, // ?N operator<= |
| GreaterThan, // ?O operator> |
| GreaterThanEqual, // ?P operator>= |
| Comma, // ?Q operator, |
| Parens, // ?R operator() |
| BitwiseNot, // ?S operator~ |
| BitwiseXor, // ?T operator^ |
| BitwiseOr, // ?U operator| |
| LogicalAnd, // ?V operator&& |
| LogicalOr, // ?W operator|| |
| TimesEqual, // ?X operator*= |
| PlusEqual, // ?Y operator+= |
| MinusEqual, // ?Z operator-= |
| DivEqual, // ?_0 operator/= |
| ModEqual, // ?_1 operator%= |
| RshEqual, // ?_2 operator>>= |
| LshEqual, // ?_3 operator<<= |
| BitwiseAndEqual, // ?_4 operator&= |
| BitwiseOrEqual, // ?_5 operator|= |
| BitwiseXorEqual, // ?_6 operator^= |
| Vftable, // ?_7 # vftable |
| Vbtable, // ?_8 # vbtable |
| Vcall, // ?_9 # vcall |
| Typeof, // ?_A # typeof |
| LocalStaticGuard, // ?_B # local static guard |
| StringLiteral, // ?_C # string literal |
| VbaseDtor, // ?_D # vbase destructor |
| VecDelDtor, // ?_E # vector deleting destructor |
| DefaultCtorClosure, // ?_F # default constructor closure |
| ScalarDelDtor, // ?_G # scalar deleting destructor |
| VecCtorIter, // ?_H # vector constructor iterator |
| VecDtorIter, // ?_I # vector destructor iterator |
| VecVbaseCtorIter, // ?_J # vector vbase constructor iterator |
| VdispMap, // ?_K # virtual displacement map |
| EHVecCtorIter, // ?_L # eh vector constructor iterator |
| EHVecDtorIter, // ?_M # eh vector destructor iterator |
| EHVecVbaseCtorIter, // ?_N # eh vector vbase constructor iterator |
| CopyCtorClosure, // ?_O # copy constructor closure |
| UdtReturning, // ?_P<name> # udt returning <name> |
| Unknown, // ?_Q # <unknown> |
| RttiTypeDescriptor, // ?_R0 # RTTI Type Descriptor |
| RttiBaseClassDescriptor, // ?_R1 # RTTI Base Class Descriptor at (a,b,c,d) |
| RttiBaseClassArray, // ?_R2 # RTTI Base Class Array |
| RttiClassHierarchyDescriptor, // ?_R3 # RTTI Class Hierarchy Descriptor |
| RttiCompleteObjLocator, // ?_R4 # RTTI Complete Object Locator |
| LocalVftable, // ?_S # local vftable |
| LocalVftableCtorClosure, // ?_T # local vftable constructor closure |
| ArrayNew, // ?_U operator new[] |
| ArrayDelete, // ?_V operator delete[] |
| ManVectorCtorIter, // ?__A managed vector ctor iterator |
| ManVectorDtorIter, // ?__B managed vector dtor iterator |
| EHVectorCopyCtorIter, // ?__C EH vector copy ctor iterator |
| EHVectorVbaseCopyCtorIter, // ?__D EH vector vbase copy ctor iterator |
| DynamicInitializer, // ?__E dynamic initializer for `T' |
| DynamicAtexitDestructor, // ?__F dynamic atexit destructor for `T' |
| VectorCopyCtorIter, // ?__G vector copy constructor iterator |
| VectorVbaseCopyCtorIter, // ?__H vector vbase copy constructor iterator |
| ManVectorVbaseCopyCtorIter, // ?__I managed vector vbase copy constructor |
| // iterator |
| LocalStaticThreadGuard, // ?__J local static thread guard |
| LiteralOperator, // ?__K operator ""_name |
| CoAwait, // ?__L co_await |
| Spaceship, // operator<=> |
| }; |
| |
| // A map to translate from operator prefix to operator type. |
| struct OperatorMapEntry { |
| StringView Prefix; |
| StringView Name; |
| OperatorTy Operator; |
| }; |
| |
| // The entries here must be in the same order as the enumeration so that it can |
| // be indexed by enum value. |
| OperatorMapEntry OperatorMap[] = { |
| {"0", " <ctor>", OperatorTy::Ctor}, |
| {"1", " <dtor>", OperatorTy::Dtor}, |
| {"2", "operator new", OperatorTy::New}, |
| {"3", "operator delete", OperatorTy::Delete}, |
| {"4", "operator=", OperatorTy::Assign}, |
| {"5", "operator>>", OperatorTy::RightShift}, |
| {"6", "operator<<", OperatorTy::LeftShift}, |
| {"7", "operator!", OperatorTy::LogicalNot}, |
| {"8", "operator==", OperatorTy::Equals}, |
| {"9", "operator!=", OperatorTy::NotEquals}, |
| {"A", "operator[]", OperatorTy::ArraySubscript}, |
| {"B", "operator <conversion>", OperatorTy::Conversion}, |
| {"C", "operator->", OperatorTy::Pointer}, |
| {"D", "operator*", OperatorTy::Dereference}, |
| {"E", "operator++", OperatorTy::Increment}, |
| {"F", "operator--", OperatorTy::Decrement}, |
| {"G", "operator-", OperatorTy::Minus}, |
| {"H", "operator+", OperatorTy::Plus}, |
| {"I", "operator&", OperatorTy::BitwiseAnd}, |
| {"J", "operator->*", OperatorTy::MemberPointer}, |
| {"K", "operator/", OperatorTy::Divide}, |
| {"L", "operator%", OperatorTy::Modulus}, |
| {"M", "operator<", OperatorTy::LessThan}, |
| {"N", "operator<=", OperatorTy::LessThanEqual}, |
| {"O", "operator>", OperatorTy::GreaterThan}, |
| {"P", "operator>=", OperatorTy::GreaterThanEqual}, |
| {"Q", "operator,", OperatorTy::Comma}, |
| {"R", "operator()", OperatorTy::Parens}, |
| {"S", "operator~", OperatorTy::BitwiseNot}, |
| {"T", "operator^", OperatorTy::BitwiseXor}, |
| {"U", "operator|", OperatorTy::BitwiseOr}, |
| {"V", "operator&&", OperatorTy::LogicalAnd}, |
| {"W", "operator||", OperatorTy::LogicalOr}, |
| {"X", "operator*=", OperatorTy::TimesEqual}, |
| {"Y", "operator+=", OperatorTy::PlusEqual}, |
| {"Z", "operator-=", OperatorTy::MinusEqual}, |
| {"_0", "operator/=", OperatorTy::DivEqual}, |
| {"_1", "operator%=", OperatorTy::ModEqual}, |
| {"_2", "operator>>=", OperatorTy::RshEqual}, |
| {"_3", "operator<<=", OperatorTy::LshEqual}, |
| {"_4", "operator&=", OperatorTy::BitwiseAndEqual}, |
| {"_5", "operator|=", OperatorTy::BitwiseOrEqual}, |
| {"_6", "operator^=", OperatorTy::BitwiseXorEqual}, |
| {"_7", "`vftable'", OperatorTy::Vftable}, |
| {"_8", "`vbtable'", OperatorTy::Vbtable}, |
| {"_9", "`vcall'", OperatorTy::Vcall}, |
| {"_A", "`typeof'", OperatorTy::Typeof}, |
| {"_B", "`local static guard'", OperatorTy::LocalStaticGuard}, |
| {"_C", "`string'", OperatorTy::StringLiteral}, |
| {"_D", "`vbase dtor'", OperatorTy::VbaseDtor}, |
| {"_E", "`vector deleting dtor'", OperatorTy::VecDelDtor}, |
| {"_F", "`default ctor closure'", OperatorTy::DefaultCtorClosure}, |
| {"_G", "`scalar deleting dtor'", OperatorTy::ScalarDelDtor}, |
| {"_H", "`vector ctor iterator'", OperatorTy::VecCtorIter}, |
| {"_I", "`vector dtor iterator'", OperatorTy::VecDtorIter}, |
| {"_J", "`vector vbase ctor iterator'", OperatorTy::VecVbaseCtorIter}, |
| {"_K", "`virtual displacement map'", OperatorTy::VdispMap}, |
| {"_L", "`eh vector ctor iterator'", OperatorTy::EHVecCtorIter}, |
| {"_M", "`eh vector dtor iterator'", OperatorTy::EHVecDtorIter}, |
| {"_N", "`eh vector vbase ctor iterator'", OperatorTy::EHVecVbaseCtorIter}, |
| {"_O", "`copy ctor closure'", OperatorTy::CopyCtorClosure}, |
| {"_P", "`udt returning'", OperatorTy::UdtReturning}, |
| {"_Q", "`unknown'", OperatorTy::Unknown}, |
| {"_R0", "`RTTI Type Descriptor'", OperatorTy::RttiTypeDescriptor}, |
| {"_R1", "RTTI Base Class Descriptor", OperatorTy::RttiBaseClassDescriptor}, |
| {"_R2", "`RTTI Base Class Array'", OperatorTy::RttiBaseClassArray}, |
| {"_R3", "`RTTI Class Hierarchy Descriptor'", |
| OperatorTy::RttiClassHierarchyDescriptor}, |
| {"_R4", "`RTTI Complete Object Locator'", |
| OperatorTy::RttiCompleteObjLocator}, |
| {"_S", "`local vftable'", OperatorTy::LocalVftable}, |
| {"_T", "`local vftable ctor closure'", OperatorTy::LocalVftableCtorClosure}, |
| {"_U", "operator new[]", OperatorTy::ArrayNew}, |
| {"_V", "operator delete[]", OperatorTy::ArrayDelete}, |
| {"__A", "managed vector ctor iterator", OperatorTy::ManVectorCtorIter}, |
| {"__B", "managed vector dtor iterator", OperatorTy::ManVectorDtorIter}, |
| {"__C", "EH vector copy ctor iterator", OperatorTy::EHVectorCopyCtorIter}, |
| {"__D", "EH vector vbase copy ctor iterator", |
| OperatorTy::EHVectorVbaseCopyCtorIter}, |
| {"__E", "dynamic initializer", OperatorTy::DynamicInitializer}, |
| {"__F", "dynamic atexit destructor", OperatorTy::DynamicAtexitDestructor}, |
| {"__G", "vector copy ctor iterator", OperatorTy::VectorCopyCtorIter}, |
| {"__H", "vector vbase copy constructor iterator", |
| OperatorTy::VectorVbaseCopyCtorIter}, |
| {"__I", "managed vector vbase copy constructor iterator", |
| OperatorTy::ManVectorVbaseCopyCtorIter}, |
| {"__J", "local static thread guard", OperatorTy::LocalStaticThreadGuard}, |
| {"__K", "operator \"\"", OperatorTy::LiteralOperator}, |
| {"__L", "co_await", OperatorTy::CoAwait}, |
| }; |
| |
| // Function classes |
| enum FuncClass : uint16_t { |
| None = 0, |
| Public = 1 << 0, |
| Protected = 1 << 1, |
| Private = 1 << 2, |
| Global = 1 << 3, |
| Static = 1 << 4, |
| Virtual = 1 << 5, |
| Far = 1 << 6, |
| ExternC = 1 << 7, |
| NoPrototype = 1 << 8, |
| VirtualThisAdjust = 1 << 9, |
| VirtualThisAdjustEx = 1 << 10, |
| StaticThisAdjust = 1 << 11 |
| }; |
| |
| enum NameBackrefBehavior : uint8_t { |
| NBB_None = 0, // don't save any names as backrefs. |
| NBB_Template = 1 << 0, // save template instanations. |
| NBB_Simple = 1 << 1, // save simple names. |
| }; |
| |
| enum class SymbolCategory { |
| Unknown, |
| NamedFunction, |
| NamedVariable, |
| UnnamedFunction, |
| UnnamedVariable, |
| SpecialOperator |
| }; |
| |
| namespace { |
| |
| struct Type; |
| struct Name; |
| |
| struct FunctionParams { |
| bool IsVariadic = false; |
| |
| Type *Current = nullptr; |
| |
| FunctionParams *Next = nullptr; |
| }; |
| |
| struct TemplateParams { |
| bool IsTemplateTemplate = false; |
| bool IsAliasTemplate = false; |
| bool IsIntegerLiteral = false; |
| bool IntegerLiteralIsNegative = false; |
| bool IsEmptyParameterPack = false; |
| bool PointerToSymbol = false; |
| bool NullptrLiteral = false; |
| bool DataMemberPointer = false; |
| bool ReferenceToSymbol = false; |
| |
| int ThunkOffsetCount = 0; |
| std::array<int64_t, 3> ThunkOffsets; |
| |
| // If IsIntegerLiteral is true, this is a non-type template parameter |
| // whose value is contained in this field. |
| uint64_t IntegralValue = 0; |
| |
| // 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; |
| StringView Custom; |
| StorageClass Storage = StorageClass::None; // storage class |
| }; |
| |
| // Represents an identifier which may be a template. |
| struct Name { |
| virtual ~Name() = default; |
| |
| bool IsTemplateInstantiation = false; |
| bool IsOperator = false; |
| bool IsBackReference = false; |
| |
| // Name read from an MangledName string. |
| StringView Str; |
| |
| // Template parameters. Only valid if IsTemplateInstantiation is true. |
| TemplateParams *TParams = nullptr; |
| |
| // Nested BackReferences (e.g. "A::B::C") are represented as a linked list. |
| Name *Next = nullptr; |
| }; |
| |
| struct OperatorInfo : public Name { |
| explicit OperatorInfo(const OperatorMapEntry &Info) : Info(&Info) { |
| this->IsOperator = true; |
| } |
| explicit OperatorInfo(OperatorTy OpType) |
| : OperatorInfo(OperatorMap[(int)OpType]) {} |
| |
| const OperatorMapEntry *Info = nullptr; |
| bool IsIndirectTable = false; |
| }; |
| |
| struct IndirectTable : public OperatorInfo { |
| explicit IndirectTable(const OperatorMapEntry &Info) : OperatorInfo(Info) { |
| this->IsOperator = true; |
| this->IsIndirectTable = true; |
| } |
| explicit IndirectTable(OperatorTy OpType) |
| : IndirectTable(OperatorMap[(int)OpType]) {} |
| |
| const Name *TableLocation = nullptr; |
| const Name *TableTarget = nullptr; |
| }; |
| |
| struct StringLiteral : public OperatorInfo { |
| StringLiteral() : OperatorInfo(OperatorTy::StringLiteral) {} |
| |
| PrimTy CharType; |
| bool IsTruncated = false; |
| }; |
| |
| struct RttiBaseClassDescriptor : public OperatorInfo { |
| RttiBaseClassDescriptor() |
| : OperatorInfo(OperatorTy::RttiBaseClassDescriptor) {} |
| |
| uint32_t NVOffset = 0; |
| int32_t VBPtrOffset = 0; |
| uint32_t VBTableOffset = 0; |
| uint32_t Flags = 0; |
| }; |
| |
| struct LocalStaticGuardVariable : public OperatorInfo { |
| LocalStaticGuardVariable() : OperatorInfo(OperatorTy::LocalStaticGuard) {} |
| |
| uint32_t ScopeIndex = 0; |
| bool IsVisible = false; |
| }; |
| |
| struct VirtualMemberPtrThunk : public OperatorInfo { |
| VirtualMemberPtrThunk() : OperatorInfo(OperatorTy::Vcall) {} |
| |
| uint64_t OffsetInVTable = 0; |
| CallingConv CC = CallingConv::Cdecl; |
| }; |
| |
| 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 { |
| struct ThisAdjustor { |
| uint32_t StaticOffset = 0; |
| int32_t VBPtrOffset = 0; |
| int32_t VBOffsetOffset = 0; |
| int32_t VtordispOffset = 0; |
| }; |
| |
| 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; |
| bool IsThunk = false; |
| |
| Type *ReturnType = nullptr; |
| // If this is a reference, the type of reference. |
| ReferenceKind RefKind; |
| |
| CallingConv CallConvention; |
| FuncClass FunctionClass; |
| |
| // Valid if IsThunk is true. |
| ThisAdjustor *ThisAdjust = nullptr; |
| |
| FunctionParams Params; |
| }; |
| |
| struct UdtType : public Type { |
| Type *clone(ArenaAllocator &Arena) const override; |
| void outputPre(OutputStream &OS) override; |
| |
| Name *UdtName = nullptr; |
| }; |
| |
| struct ArrayDimension { |
| uint64_t Dim = 0; |
| ArrayDimension *Next = 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. |
| ArrayDimension *Dims = nullptr; |
| |
| 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; |
| } |
| |
| // 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 outputStringLiteral(OutputStream &OS, const StringLiteral &Str) { |
| switch (Str.CharType) { |
| case PrimTy::Wchar: |
| OS << "const wchar_t * {L\""; |
| break; |
| case PrimTy::Char: |
| OS << "const char * {\""; |
| break; |
| case PrimTy::Char16: |
| OS << "const char16_t * {u\""; |
| break; |
| case PrimTy::Char32: |
| OS << "const char32_t * {U\""; |
| break; |
| default: |
| LLVM_BUILTIN_UNREACHABLE; |
| } |
| OS << Str.Str << "\""; |
| if (Str.IsTruncated) |
| OS << "..."; |
| OS << "}"; |
| } |
| |
| static void outputName(OutputStream &OS, const Name *TheName, const Type *Ty); |
| |
| static void outputParameterList(OutputStream &OS, |
| const TemplateParams &Params) { |
| if (Params.IsEmptyParameterPack) { |
| 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->IsIntegerLiteral) { |
| if (Head->IntegerLiteralIsNegative) |
| OS << '-'; |
| OS << Head->IntegralValue; |
| } else if (Head->PointerToSymbol || Head->ReferenceToSymbol) { |
| if (Head->NullptrLiteral) |
| OS << "nullptr"; |
| else { |
| if (Head->ThunkOffsetCount > 0) |
| OS << "{"; |
| else if (Head->PointerToSymbol) |
| OS << "&"; |
| if (Head->ParamType) |
| Type::outputPre(OS, *Head->ParamType); |
| outputName(OS, Head->ParamName, Head->ParamType); |
| if (Head->ParamType) |
| Type::outputPost(OS, *Head->ParamType); |
| if (Head->ThunkOffsetCount > 0) { |
| for (int I = 0; I < Head->ThunkOffsetCount; ++I) { |
| OS << ", " << Head->ThunkOffsets[I]; |
| } |
| OS << "}"; |
| } |
| } |
| } else if (Head->DataMemberPointer) { |
| OS << "{" << Head->ThunkOffsets[0]; |
| for (int I = 1; I < Head->ThunkOffsetCount; ++I) |
| OS << ", " << Head->ThunkOffsets[I]; |
| OS << "}"; |
| } else if (Head->ParamType) { |
| // simple type. |
| Type::outputPre(OS, *Head->ParamType); |
| Type::outputPost(OS, *Head->ParamType); |
| } else { |
| // Template alias. |
| outputName(OS, Head->ParamName, Head->ParamType); |
| } |
| |
| Head = Head->Next; |
| |
| if (Head) |
| OS << ", "; |
| } |
| OS << ">"; |
| } |
| |
| static void outputQualifiers(OutputStream &OS, Qualifiers Q) { |
| if (Q & Q_Const) { |
| outputSpaceIfNecessary(OS); |
| OS << "const"; |
| } |
| |
| if (Q & Q_Volatile) { |
| outputSpaceIfNecessary(OS); |
| OS << "volatile"; |
| } |
| |
| if (Q & Q_Restrict) { |
| outputSpaceIfNecessary(OS); |
| OS << "__restrict"; |
| } |
| } |
| |
| static void outputNameComponent(OutputStream &OS, const Name &N) { |
| OS << N.Str; |
| |
| if (N.IsTemplateInstantiation && N.TParams) |
| outputParameterList(OS, *N.TParams); |
| } |
| |
| static const OperatorInfo *lastComponentAsOperator(const Name *TheName) { |
| if (!TheName) |
| return nullptr; |
| while (TheName->Next) |
| TheName = TheName->Next; |
| if (TheName->IsOperator) |
| return static_cast<const OperatorInfo *>(TheName); |
| return nullptr; |
| } |
| |
| static void outputName(OutputStream &OS, const Name *TheName, const Type *Ty) { |
| if (!TheName) |
| return; |
| |
| outputSpaceIfNecessary(OS); |
| |
| const OperatorInfo *Operator = lastComponentAsOperator(TheName); |
| const VirtualMemberPtrThunk *Thunk = nullptr; |
| bool PrintLastScopeSeparator = true; |
| if (Operator) { |
| if (Operator->IsIndirectTable) { |
| const IndirectTable *Table = static_cast<const IndirectTable *>(Operator); |
| outputName(OS, Table->TableLocation, nullptr); |
| OS << "{for `"; |
| outputName(OS, Table->TableTarget, nullptr); |
| OS << "'}"; |
| return; |
| } |
| if (Operator->Info->Operator == OperatorTy::Vcall) { |
| Thunk = static_cast<const VirtualMemberPtrThunk *>(Operator); |
| OS << "[thunk]: "; |
| outputCallingConvention(OS, Thunk->CC); |
| OS << " "; |
| } else if (Operator->Info->Operator == OperatorTy::DynamicInitializer) { |
| OS << "`dynamic initializer for '"; |
| PrintLastScopeSeparator = false; |
| } else if (Operator->Info->Operator == |
| OperatorTy::DynamicAtexitDestructor) { |
| OS << "`dynamic atexit destructor for '"; |
| PrintLastScopeSeparator = false; |
| } |
| } |
| |
| const Name *Previous = nullptr; |
| // Print out namespaces or outer class BackReferences. |
| for (; TheName->Next; TheName = TheName->Next) { |
| Previous = TheName; |
| outputNameComponent(OS, *TheName); |
| if (TheName->Next != Operator || PrintLastScopeSeparator) |
| OS << "::"; |
| } |
| |
| // Print out a regular name. |
| if (!TheName->IsOperator) { |
| outputNameComponent(OS, *TheName); |
| return; |
| } |
| |
| |
| // Print out ctor or dtor. |
| switch (Operator->Info->Operator) { |
| case OperatorTy::Dtor: |
| OS << "~"; |
| LLVM_FALLTHROUGH; |
| case OperatorTy::Ctor: |
| // Output the class name with template arguments a second time. |
| outputNameComponent(OS, *Previous); |
| |
| // Structors don't have a name, so outputting the name here actually is a |
| // no-op. But for template constructors, it needs to output the template |
| // argument list. e.g. |
| // |
| // template<typename T> |
| // struct Foo { |
| // template<typename U> |
| // Foo(U); |
| // }; |
| // should demangle as -- for example -- Foo<int><double>(double); |
| outputNameComponent(OS, *TheName); |
| break; |
| case OperatorTy::Conversion: |
| OS << "operator"; |
| if (TheName->IsTemplateInstantiation && TheName->TParams) |
| outputParameterList(OS, *TheName->TParams); |
| OS << " "; |
| if (Ty) { |
| const FunctionType *FTy = static_cast<const FunctionType *>(Ty); |
| Type::outputPre(OS, *FTy->ReturnType); |
| Type::outputPost(OS, *FTy->ReturnType); |
| } else { |
| OS << "<conversion>"; |
| } |
| break; |
| case OperatorTy::LiteralOperator: |
| OS << Operator->Info->Name; |
| outputNameComponent(OS, *TheName); |
| break; |
| case OperatorTy::RttiBaseClassDescriptor: { |
| const RttiBaseClassDescriptor &BCD = |
| static_cast<const RttiBaseClassDescriptor &>(*Operator); |
| OS << "`" << Operator->Info->Name << " at ("; |
| OS << BCD.NVOffset << ", " << BCD.VBPtrOffset << ", " << BCD.VBTableOffset |
| << ", " << BCD.Flags; |
| OS << ")'"; |
| break; |
| } |
| case OperatorTy::Vcall: { |
| OS << "`vcall'{"; |
| OS << Thunk->OffsetInVTable << ", {flat}}"; |
| break; |
| } |
| case OperatorTy::DynamicInitializer: |
| case OperatorTy::DynamicAtexitDestructor: |
| OS << "''"; |
| break; |
| |
| case OperatorTy::LocalStaticGuard: { |
| const LocalStaticGuardVariable &LSG = |
| static_cast<const LocalStaticGuardVariable &>(*Operator); |
| OS << Operator->Info->Name; |
| if (LSG.ScopeIndex > 0) |
| OS << "{" << LSG.ScopeIndex << "}"; |
| break; |
| } |
| default: |
| OS << Operator->Info->Name; |
| if (Operator->IsTemplateInstantiation) |
| outputParameterList(OS, *Operator->TParams); |
| break; |
| } |
| } |
| |
| static void outputSpecialOperator(OutputStream &OS, const Name *OuterName) { |
| assert(OuterName); |
| // The last component should be an operator. |
| const OperatorInfo *Operator = lastComponentAsOperator(OuterName); |
| |
| assert(Operator->IsOperator); |
| const OperatorInfo &Oper = static_cast<const OperatorInfo &>(*Operator); |
| switch (Oper.Info->Operator) { |
| case OperatorTy::StringLiteral: { |
| const StringLiteral &SL = static_cast<const StringLiteral &>(Oper); |
| outputStringLiteral(OS, SL); |
| break; |
| } |
| case OperatorTy::Vcall: { |
| const VirtualMemberPtrThunk &Thunk = |
| static_cast<const VirtualMemberPtrThunk &>(Oper); |
| OS << "[thunk]: "; |
| outputCallingConvention(OS, Thunk.CC); |
| OS << " "; |
| // Print out namespaces or outer class BackReferences. |
| const Name *N = OuterName; |
| for (; N->Next; N = N->Next) { |
| outputNameComponent(OS, *N); |
| OS << "::"; |
| } |
| OS << "`vcall'{"; |
| OS << Thunk.OffsetInVTable << ", {flat}}"; |
| break; |
| } |
| default: |
| // There are no other special operator categories. |
| LLVM_BUILTIN_UNREACHABLE; |
| } |
| } |
| |
| 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); |
| |
| outputQualifiers(OS, Ty.Quals); |
| } |
| |
| // 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; |
| case PrimTy::Custom: |
| OS << Custom; |
| break; |
| case PrimTy::Vbtable: |
| case PrimTy::Vftable: |
| 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, Pointee); |
| 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"; |
| } |
| |
| 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 & StaticThisAdjust) || (FunctionClass & VirtualThisAdjust)) |
| OS << "[thunk]: "; |
| |
| if (!(FunctionClass & Global)) { |
| if (FunctionClass & Static) |
| OS << "static "; |
| } |
| if (FunctionClass & ExternC) |
| OS << "extern \"C\" "; |
| |
| if (FunctionClass & Virtual) |
| OS << "virtual "; |
| |
| 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) { |
| // extern "C" functions don't have a prototype. |
| if (FunctionClass & NoPrototype) |
| return; |
| |
| if (FunctionClass & StaticThisAdjust) { |
| OS << "`adjustor{" << ThisAdjust->StaticOffset << "}'"; |
| } else if (FunctionClass & VirtualThisAdjust) { |
| if (FunctionClass & VirtualThisAdjustEx) { |
| OS << "`vtordispex{" << ThisAdjust->VBPtrOffset << ", " |
| << ThisAdjust->VBOffsetOffset << ", " << ThisAdjust->VtordispOffset |
| << ", " << ThisAdjust->StaticOffset << "}'"; |
| } else { |
| OS << "`vtordisp{" << ThisAdjust->VtordispOffset << ", " |
| << ThisAdjust->StaticOffset << "}'"; |
| } |
| } |
| |
| 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, this); |
| } |
| |
| 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) { |
| ArrayDimension *D = Dims; |
| while (D) { |
| OS << "["; |
| if (D->Dim > 0) |
| OS << D->Dim; |
| OS << "]"; |
| D = D->Next; |
| } |
| |
| Type::outputPost(OS, *ElementType); |
| } |
| |
| struct Symbol { |
| SymbolCategory Category; |
| |
| Qualifiers SymbolQuals = Q_None; |
| Name *SymbolName = nullptr; |
| Type *SymbolType = nullptr; |
| }; |
| |
| } // namespace |
| |
| namespace { |
| |
| struct BackrefContext { |
| static constexpr size_t Max = 10; |
| |
| Type *FunctionParams[Max]; |
| size_t FunctionParamCount = 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 Names[Max]; |
| size_t NamesCount = 0; |
| }; |
| |
| // 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; |
| virtual ~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); |
| Symbol *parseOperator(StringView &MangledName); |
| |
| void output(const Symbol *S, OutputStream &OS); |
| |
| // True if an error occurred. |
| bool Error = false; |
| |
| void dumpBackReferences(); |
| |
| private: |
| std::pair<SymbolCategory, Type *> |
| demangleSymbolCategoryAndType(StringView &MangledName); |
| |
| Type *demangleVariableEncoding(StringView &MangledName, StorageClass SC); |
| Type *demangleFunctionEncoding(StringView &MangledName); |
| uint64_t demangleThunkThisAdjust(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); |
| |
| std::pair<uint64_t, bool> demangleNumber(StringView &MangledName); |
| uint64_t demangleUnsigned(StringView &MangledName); |
| int64_t demangleSigned(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, bool Memorize); |
| Name *demangleUnqualifiedSymbolName(StringView &MangledName, |
| NameBackrefBehavior NBB); |
| |
| Name *demangleNameScopeChain(StringView &MangledName, Name *UnqualifiedName); |
| Name *demangleNameScopePiece(StringView &MangledName); |
| |
| Name *demangleBackRefName(StringView &MangledName); |
| Name *demangleTemplateInstantiationName(StringView &MangledName, |
| NameBackrefBehavior NBB); |
| std::pair<OperatorTy, Name *> demangleOperatorName(StringView &MangledName, |
| bool FullyQualified); |
| Name *demangleSimpleName(StringView &MangledName, bool Memorize); |
| Name *demangleAnonymousNamespaceName(StringView &MangledName); |
| Name *demangleLocallyScopedNamePiece(StringView &MangledName); |
| StringLiteral *demangleStringLiteral(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); |
| wchar_t demangleWcharLiteral(StringView &MangledName); |
| uint8_t demangleCharLiteral(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 *); |
| BackrefContext Backrefs; |
| }; |
| } // namespace |
| |
| StringView Demangler::copyString(StringView Borrowed) { |
| char *Stable = Arena.allocUnalignedBuffer(Borrowed.size() + 1); |
| std::strcpy(Stable, Borrowed.begin()); |
| |
| return {Stable, Borrowed.size()}; |
| } |
| |
| Symbol *Demangler::parseOperator(StringView &MangledName) { |
| Symbol *S = Arena.alloc<Symbol>(); |
| |
| bool IsMember = false; |
| OperatorTy OTy; |
| std::tie(OTy, S->SymbolName) = demangleOperatorName(MangledName, true); |
| switch (OTy) { |
| case OperatorTy::StringLiteral: |
| S->Category = SymbolCategory::SpecialOperator; |
| break; |
| case OperatorTy::Vcall: |
| S->Category = SymbolCategory::UnnamedFunction; |
| break; |
| case OperatorTy::LocalStaticGuard: |
| S->Category = SymbolCategory::UnnamedVariable; |
| break; |
| case OperatorTy::Vftable: // Foo@@6B@ |
| case OperatorTy::LocalVftable: // Foo@@6B@ |
| case OperatorTy::RttiCompleteObjLocator: // Foo@@6B@ |
| case OperatorTy::Vbtable: // Foo@@7B@ |
| S->Category = SymbolCategory::UnnamedVariable; |
| switch (MangledName.popFront()) { |
| case '6': |
| case '7': { |
| std::tie(S->SymbolQuals, IsMember) = demangleQualifiers(MangledName); |
| if (!MangledName.consumeFront('@')) { |
| IndirectTable *Table = Arena.alloc<IndirectTable>(OTy); |
| Table->TableTarget = demangleFullyQualifiedTypeName(MangledName); |
| Table->TableLocation = S->SymbolName; |
| S->SymbolName = Table; |
| } |
| break; |
| } |
| default: |
| Error = true; |
| break; |
| } |
| break; |
| case OperatorTy::RttiTypeDescriptor: // <type>@@8 |
| S->Category = SymbolCategory::UnnamedVariable; |
| S->SymbolType = demangleType(MangledName, QualifierMangleMode::Result); |
| if (Error) |
| break; |
| if (!MangledName.consumeFront("@8")) |
| Error = true; |
| if (!MangledName.empty()) |
| Error = true; |
| break; |
| default: |
| if (!Error) |
| std::tie(S->Category, S->SymbolType) = |
| demangleSymbolCategoryAndType(MangledName); |
| break; |
| } |
| |
| return (Error) ? nullptr : S; |
| } |
| |
| std::pair<SymbolCategory, Type *> |
| Demangler::demangleSymbolCategoryAndType(StringView &MangledName) { |
| // Read a variable. |
| switch (MangledName.front()) { |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| return std::make_pair( |
| SymbolCategory::NamedVariable, |
| demangleVariableEncoding(MangledName, |
| demangleVariableStorageClass(MangledName))); |
| case '8': |
| MangledName.consumeFront('8'); |
| return std::pair<SymbolCategory, Type *>(SymbolCategory::UnnamedVariable, |
| nullptr); |
| } |
| return std::make_pair(SymbolCategory::NamedFunction, |
| demangleFunctionEncoding(MangledName)); |
| } |
| |
| // Parser entry point. |
| Symbol *Demangler::parse(StringView &MangledName) { |
| // We can't demangle MD5 names, just output them as-is. |
| // Also, MSVC-style mangled symbols must start with '?'. |
| if (MangledName.startsWith("??@") || !MangledName.startsWith('?')) { |
| Symbol *S = Arena.alloc<Symbol>(); |
| S->Category = SymbolCategory::Unknown; |
| S->SymbolName = Arena.alloc<Name>(); |
| S->SymbolName->Str = MangledName; |
| S->SymbolType = nullptr; |
| MangledName = StringView(); |
| return S; |
| } |
| |
| MangledName.consumeFront('?'); |
| |
| // ?$ is a template instantiation, but all other names that start with ? are |
| // operators / special names. |
| if (MangledName.startsWith('?') && !MangledName.startsWith("?$")) |
| return parseOperator(MangledName); |
| |
| Symbol *S = Arena.alloc<Symbol>(); |
| // What follows is a main symbol name. This may include namespaces or class |
| // back references. |
| S->SymbolName = demangleFullyQualifiedSymbolName(MangledName); |
| if (Error) |
| return nullptr; |
| |
| std::tie(S->Category, S->SymbolType) = |
| demangleSymbolCategoryAndType(MangledName); |
| |
| if (Error) |
| return nullptr; |
| |
| 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) { |
| 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, ... |
| std::pair<uint64_t, bool> Demangler::demangleNumber(StringView &MangledName) { |
| bool IsNegative = MangledName.consumeFront('?'); |
| |
| if (startsWithDigit(MangledName)) { |
| uint64_t Ret = MangledName[0] - '0' + 1; |
| MangledName = MangledName.dropFront(1); |
| return {Ret, IsNegative}; |
| } |
| |
| uint64_t Ret = 0; |
| for (size_t i = 0; i < MangledName.size(); ++i) { |
| char C = MangledName[i]; |
| if (C == '@') { |
| MangledName = MangledName.dropFront(i + 1); |
| return {Ret, IsNegative}; |
| } |
| if ('A' <= C && C <= 'P') { |
| Ret = (Ret << 4) + (C - 'A'); |
| continue; |
| } |
| break; |
| } |
| |
| Error = true; |
| return {0ULL, false}; |
| } |
| |
| uint64_t Demangler::demangleUnsigned(StringView &MangledName) { |
| bool IsNegative = false; |
| uint64_t Number = 0; |
| std::tie(Number, IsNegative) = demangleNumber(MangledName); |
| if (IsNegative) |
| Error = true; |
| return Number; |
| } |
| |
| int64_t Demangler::demangleSigned(StringView &MangledName) { |
| bool IsNegative = false; |
| uint64_t Number = 0; |
| std::tie(Number, IsNegative) = demangleNumber(MangledName); |
| if (Number > INT64_MAX) |
| Error = true; |
| int64_t I = static_cast<int64_t>(Number); |
| return IsNegative ? -I : I; |
| } |
| |
| // First 10 strings can be referenced by special BackReferences ?0, ?1, ..., ?9. |
| // Memorize it. |
| void Demangler::memorizeString(StringView S) { |
| if (Backrefs.NamesCount >= BackrefContext::Max) |
| return; |
| for (size_t i = 0; i < Backrefs.NamesCount; ++i) |
| if (S == Backrefs.Names[i]) |
| return; |
| Backrefs.Names[Backrefs.NamesCount++] = S; |
| } |
| |
| Name *Demangler::demangleBackRefName(StringView &MangledName) { |
| assert(startsWithDigit(MangledName)); |
| |
| size_t I = MangledName[0] - '0'; |
| if (I >= Backrefs.NamesCount) { |
| Error = true; |
| return nullptr; |
| } |
| |
| MangledName = MangledName.dropFront(); |
| Name *Node = Arena.alloc<Name>(); |
| Node->Str = Backrefs.Names[I]; |
| return Node; |
| } |
| |
| Name *Demangler::demangleTemplateInstantiationName(StringView &MangledName, |
| NameBackrefBehavior NBB) { |
| assert(MangledName.startsWith("?$")); |
| MangledName.consumeFront("?$"); |
| |
| BackrefContext OuterContext; |
| std::swap(OuterContext, Backrefs); |
| |
| Name *Node = demangleUnqualifiedSymbolName(MangledName, NBB_Simple); |
| if (!Error) |
| Node->TParams = demangleTemplateParameterList(MangledName); |
| |
| std::swap(OuterContext, Backrefs); |
| if (Error) |
| return nullptr; |
| |
| Node->IsTemplateInstantiation = true; |
| |
| if (NBB & NBB_Template) { |
| // 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, nullptr); |
| OS << '\0'; |
| char *Name = OS.getBuffer(); |
| |
| StringView Owned = copyString(Name); |
| memorizeString(Owned); |
| std::free(Name); |
| } |
| |
| return Node; |
| } |
| |
| std::pair<OperatorTy, Name *> |
| Demangler::demangleOperatorName(StringView &MangledName, bool FullyQualified) { |
| assert(MangledName.startsWith('?')); |
| MangledName.consumeFront('?'); |
| |
| const OperatorMapEntry *Entry = nullptr; |
| for (const auto &MapEntry : OperatorMap) { |
| if (!MangledName.consumeFront(MapEntry.Prefix)) |
| continue; |
| Entry = &MapEntry; |
| break; |
| } |
| if (!Entry) { |
| Error = true; |
| return std::make_pair(OperatorTy::Unknown, nullptr); |
| } |
| |
| Name *N = nullptr; |
| switch (Entry->Operator) { |
| case OperatorTy::Vftable: // Foo@@6B@ |
| case OperatorTy::LocalVftable: // Foo@@6B@ |
| case OperatorTy::RttiCompleteObjLocator: // Foo@@6B@ |
| case OperatorTy::Vbtable: { // Foo@@7B@ |
| N = Arena.alloc<OperatorInfo>(*Entry); |
| if (FullyQualified) |
| N = demangleNameScopeChain(MangledName, N); |
| break; |
| } |
| |
| case OperatorTy::StringLiteral: |
| N = demangleStringLiteral(MangledName); |
| break; |
| case OperatorTy::LiteralOperator: |
| N = Arena.alloc<OperatorInfo>(*Entry); |
| N->Str = demangleSimpleString(MangledName, false); |
| if (!MangledName.consumeFront('@')) |
| Error = true; |
| break; |
| case OperatorTy::RttiBaseClassDescriptor: { |
| RttiBaseClassDescriptor *Temp = Arena.alloc<RttiBaseClassDescriptor>(); |
| Temp->NVOffset = demangleUnsigned(MangledName); |
| Temp->VBPtrOffset = demangleSigned(MangledName); |
| Temp->VBTableOffset = demangleUnsigned(MangledName); |
| Temp->Flags = demangleUnsigned(MangledName); |
| N = (FullyQualified) ? demangleNameScopeChain(MangledName, Temp) : Temp; |
| break; |
| } |
| case OperatorTy::Vcall: { |
| VirtualMemberPtrThunk *Temp = Arena.alloc<VirtualMemberPtrThunk>(); |
| N = demangleNameScopeChain(MangledName, Temp); |
| if (Error) |
| break; |
| if (!MangledName.consumeFront("$B")) |
| Error = true; |
| Temp->OffsetInVTable = demangleUnsigned(MangledName); |
| if (!MangledName.consumeFront('A')) |
| Error = true; |
| Temp->CC = demangleCallingConvention(MangledName); |
| break; |
| } |
| case OperatorTy::RttiTypeDescriptor: |
| // This one is just followed by a type, not a name scope. |
| N = Arena.alloc<OperatorInfo>(*Entry); |
| break; |
| case OperatorTy::LocalStaticGuard: { |
| LocalStaticGuardVariable *Temp = Arena.alloc<LocalStaticGuardVariable>(); |
| N = (FullyQualified) ? demangleNameScopeChain(MangledName, Temp) : Temp; |
| if (MangledName.consumeFront("4IA")) |
| Temp->IsVisible = false; |
| else if (MangledName.consumeFront("5")) |
| Temp->IsVisible = true; |
| else |
| Error = true; |
| if (!MangledName.empty()) |
| Temp->ScopeIndex = demangleUnsigned(MangledName); |
| break; |
| } |
| default: |
| N = Arena.alloc<OperatorInfo>(*Entry); |
| N = (FullyQualified) ? demangleNameScopeChain(MangledName, N) : N; |
| break; |
| } |
| if (Error) |
| return std::make_pair(OperatorTy::Unknown, nullptr); |
| |
| return std::make_pair(Entry->Operator, N); |
| } |
| |
| 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; |
| } |
| |
| static bool isRebasedHexDigit(char C) { return (C >= 'A' && C <= 'P'); } |
| |
| static uint8_t rebasedHexDigitToNumber(char C) { |
| assert(isRebasedHexDigit(C)); |
| return (C <= 'J') ? (C - 'A') : (10 + C - 'K'); |
| } |
| |
| uint8_t Demangler::demangleCharLiteral(StringView &MangledName) { |
| if (!MangledName.startsWith('?')) |
| return MangledName.popFront(); |
| |
| MangledName = MangledName.dropFront(); |
| if (MangledName.empty()) |
| goto CharLiteralError; |
| |
| if (MangledName.consumeFront('$')) { |
| // Two hex digits |
| if (MangledName.size() < 2) |
| goto CharLiteralError; |
| StringView Nibbles = MangledName.substr(0, 2); |
| if (!isRebasedHexDigit(Nibbles[0]) || !isRebasedHexDigit(Nibbles[1])) |
| goto CharLiteralError; |
| // Don't append the null terminator. |
| uint8_t C1 = rebasedHexDigitToNumber(Nibbles[0]); |
| uint8_t C2 = rebasedHexDigitToNumber(Nibbles[1]); |
| MangledName = MangledName.dropFront(2); |
| return (C1 << 4) | C2; |
| } |
| |
| if (startsWithDigit(MangledName)) { |
| const char *Lookup = ",/\\:. \n\t'-"; |
| char C = Lookup[MangledName[0] - '0']; |
| MangledName = MangledName.dropFront(); |
| return C; |
| } |
| |
| if (MangledName[0] >= 'a' && MangledName[0] <= 'z') { |
| char Lookup[26] = {'\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', |
| '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', |
| '\xEF', '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5', |
| '\xF6', '\xF7', '\xF8', '\xF9', '\xFA'}; |
| char C = Lookup[MangledName[0] - 'a']; |
| MangledName = MangledName.dropFront(); |
| return C; |
| } |
| |
| if (MangledName[0] >= 'A' && MangledName[0] <= 'Z') { |
| char Lookup[26] = {'\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', |
| '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', |
| '\xCF', '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', |
| '\xD6', '\xD7', '\xD8', '\xD9', '\xDA'}; |
| char C = Lookup[MangledName[0] - 'A']; |
| MangledName = MangledName.dropFront(); |
| return C; |
| } |
| |
| CharLiteralError: |
| Error = true; |
| return '\0'; |
| } |
| |
| wchar_t Demangler::demangleWcharLiteral(StringView &MangledName) { |
| uint8_t C1, C2; |
| |
| C1 = demangleCharLiteral(MangledName); |
| if (Error) |
| goto WCharLiteralError; |
| C2 = demangleCharLiteral(MangledName); |
| if (Error) |
| goto WCharLiteralError; |
| |
| return ((wchar_t)C1 << 8) | (wchar_t)C2; |
| |
| WCharLiteralError: |
| Error = true; |
| return L'\0'; |
| } |
| |
| static void writeHexDigit(char *Buffer, uint8_t Digit) { |
| assert(Digit <= 15); |
| *Buffer = (Digit < 10) ? ('0' + Digit) : ('A' + Digit - 10); |
| } |
| |
| static void outputHex(OutputStream &OS, unsigned C) { |
| if (C == 0) { |
| OS << "\\x00"; |
| return; |
| } |
| // It's easier to do the math if we can work from right to left, but we need |
| // to print the numbers from left to right. So render this into a temporary |
| // buffer first, then output the temporary buffer. Each byte is of the form |
| // \xAB, which means that each byte needs 4 characters. Since there are at |
| // most 4 bytes, we need a 4*4+1 = 17 character temporary buffer. |
| char TempBuffer[17]; |
| |
| ::memset(TempBuffer, 0, sizeof(TempBuffer)); |
| constexpr int MaxPos = 15; |
| |
| int Pos = MaxPos - 1; |
| while (C != 0) { |
| for (int I = 0; I < 2; ++I) { |
| writeHexDigit(&TempBuffer[Pos--], C % 16); |
| C /= 16; |
| } |
| TempBuffer[Pos--] = 'x'; |
| TempBuffer[Pos--] = '\\'; |
| assert(Pos >= 0); |
| } |
| OS << StringView(&TempBuffer[Pos + 1]); |
| } |
| |
| static void outputEscapedChar(OutputStream &OS, unsigned C) { |
| switch (C) { |
| case '\'': // single quote |
| OS << "\\\'"; |
| return; |
| case '\"': // double quote |
| OS << "\\\""; |
| return; |
| case '\\': // backslash |
| OS << "\\\\"; |
| return; |
| case '\a': // bell |
| OS << "\\a"; |
| return; |
| case '\b': // backspace |
| OS << "\\b"; |
| return; |
| case '\f': // form feed |
| OS << "\\f"; |
| return; |
| case '\n': // new line |
| OS << "\\n"; |
| return; |
| case '\r': // carriage return |
| OS << "\\r"; |
| return; |
| case '\t': // tab |
| OS << "\\t"; |
| return; |
| case '\v': // vertical tab |
| OS << "\\v"; |
| return; |
| default: |
| break; |
| } |
| |
| if (C > 0x1F && C < 0x7F) { |
| // Standard ascii char. |
| OS << (char)C; |
| return; |
| } |
| |
| outputHex(OS, C); |
| } |
| |
| unsigned countTrailingNullBytes(const uint8_t *StringBytes, int Length) { |
| const uint8_t *End = StringBytes + Length - 1; |
| unsigned Count = 0; |
| while (Length > 0 && *End == 0) { |
| --Length; |
| --End; |
| ++Count; |
| } |
| return Count; |
| } |
| |
| unsigned countEmbeddedNulls(const uint8_t *StringBytes, unsigned Length) { |
| unsigned Result = 0; |
| for (unsigned I = 0; I < Length; ++I) { |
| if (*StringBytes++ == 0) |
| ++Result; |
| } |
| return Result; |
| } |
| |
| unsigned guessCharByteSize(const uint8_t *StringBytes, unsigned NumChars, |
| unsigned NumBytes) { |
| assert(NumBytes > 0); |
| |
| // If the number of bytes is odd, this is guaranteed to be a char string. |
| if (NumBytes % 2 == 1) |
| return 1; |
| |
| // All strings can encode at most 32 bytes of data. If it's less than that, |
| // then we encoded the entire string. In this case we check for a 1-byte, |
| // 2-byte, or 4-byte null terminator. |
| if (NumBytes < 32) { |
| unsigned TrailingNulls = countTrailingNullBytes(StringBytes, NumChars); |
| if (TrailingNulls >= 4) |
| return 4; |
| if (TrailingNulls >= 2) |
| return 2; |
| return 1; |
| } |
| |
| // The whole string was not able to be encoded. Try to look at embedded null |
| // terminators to guess. The heuristic is that we count all embedded null |
| // terminators. If more than 2/3 are null, it's a char32. If more than 1/3 |
| // are null, it's a char16. Otherwise it's a char8. This obviously isn't |
| // perfect and is biased towards languages that have ascii alphabets, but this |
| // was always going to be best effort since the encoding is lossy. |
| unsigned Nulls = countEmbeddedNulls(StringBytes, NumChars); |
| if (Nulls >= 2 * NumChars / 3) |
| return 4; |
| if (Nulls >= NumChars / 3) |
| return 2; |
| return 1; |
| } |
| |
| static unsigned decodeMultiByteChar(const uint8_t *StringBytes, |
| unsigned CharIndex, unsigned CharBytes) { |
| assert(CharBytes == 1 || CharBytes == 2 || CharBytes == 4); |
| unsigned Offset = CharIndex * CharBytes; |
| unsigned Result = 0; |
| StringBytes = StringBytes + Offset; |
| for (unsigned I = 0; I < CharBytes; ++I) { |
| unsigned C = static_cast<unsigned>(StringBytes[I]); |
| Result |= C << (8 * I); |
| } |
| return Result; |
| } |
| |
| StringLiteral *Demangler::demangleStringLiteral(StringView &MangledName) { |
| // This function uses goto, so declare all variables up front. |
| OutputStream OS; |
| StringView CRC; |
| uint64_t StringByteSize; |
| bool IsWcharT = false; |
| bool IsNegative = false; |
| size_t CrcEndPos = 0; |
| char *ResultBuffer = nullptr; |
| |
| StringLiteral *Result = Arena.alloc<StringLiteral>(); |
| |
| // Prefix indicating the beginning of a string literal |
| if (!MangledName.consumeFront("@_")) |
| goto StringLiteralError; |
| if (MangledName.empty()) |
| goto StringLiteralError; |
| |
| // Char Type (regular or wchar_t) |
| switch (MangledName.popFront()) { |
| case '1': |
| IsWcharT = true; |
| LLVM_FALLTHROUGH; |
| case '0': |
| break; |
| default: |
| goto StringLiteralError; |
| } |
| |
| // Encoded Length |
| std::tie(StringByteSize, IsNegative) = demangleNumber(MangledName); |
| if (Error || IsNegative) |
| goto StringLiteralError; |
| |
| // CRC 32 (always 8 characters plus a terminator) |
| CrcEndPos = MangledName.find('@'); |
| if (CrcEndPos == StringView::npos) |
| goto StringLiteralError; |
| CRC = MangledName.substr(0, CrcEndPos); |
| MangledName = MangledName.dropFront(CrcEndPos + 1); |
| if (MangledName.empty()) |
| goto StringLiteralError; |
| |
| OS = OutputStream::create(nullptr, nullptr, 1024); |
| if (IsWcharT) { |
| Result->CharType = PrimTy::Wchar; |
| if (StringByteSize > 64) |
| Result->IsTruncated = true; |
| |
| while (!MangledName.consumeFront('@')) { |
| assert(StringByteSize >= 2); |
| wchar_t W = demangleWcharLiteral(MangledName); |
| if (StringByteSize != 2 || Result->IsTruncated) |
| outputEscapedChar(OS, W); |
| StringByteSize -= 2; |
| if (Error) |
| goto StringLiteralError; |
| } |
| } else { |
| if (StringByteSize > 32) |
| Result->IsTruncated = true; |
| |
| constexpr unsigned MaxStringByteLength = 32; |
| uint8_t StringBytes[MaxStringByteLength]; |
| |
| unsigned BytesDecoded = 0; |
| while (!MangledName.consumeFront('@')) { |
| assert(StringByteSize >= 1); |
| StringBytes[BytesDecoded++] = demangleCharLiteral(MangledName); |
| } |
| |
| unsigned CharBytes = |
| guessCharByteSize(StringBytes, BytesDecoded, StringByteSize); |
| assert(StringByteSize % CharBytes == 0); |
| switch (CharBytes) { |
| case 1: |
| Result->CharType = PrimTy::Char; |
| break; |
| case 2: |
| Result->CharType = PrimTy::Char16; |
| break; |
| case 4: |
| Result->CharType = PrimTy::Char32; |
| break; |
| default: |
| LLVM_BUILTIN_UNREACHABLE; |
| } |
| const unsigned NumChars = BytesDecoded / CharBytes; |
| for (unsigned CharIndex = 0; CharIndex < NumChars; ++CharIndex) { |
| unsigned NextChar = |
| decodeMultiByteChar(StringBytes, CharIndex, CharBytes); |
| if (CharIndex + 1 < NumChars || Result->IsTruncated) |
| outputEscapedChar(OS, NextChar); |
| } |
| } |
| |
| OS << '\0'; |
| ResultBuffer = OS.getBuffer(); |
| Result->Str = copyString(ResultBuffer); |
| std::free(ResultBuffer); |
| return Result; |
| |
| StringLiteralError: |
| Error = true; |
| return nullptr; |
| } |
| |
| 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'"; |
| size_t EndPos = MangledName.find('@'); |
| if (EndPos == StringView::npos) { |
| Error = true; |
| return nullptr; |
| } |
| StringView NamespaceKey = MangledName.substr(0, EndPos); |
| memorizeString(NamespaceKey); |
| MangledName = MangledName.substr(EndPos + 1); |
| return Node; |
| } |
| |
| Name *Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) { |
| assert(startsWithLocalScopePattern(MangledName)); |
| |
| Name *Node = Arena.alloc<Name>(); |
| MangledName.consumeFront('?'); |
| auto Number = demangleNumber(MangledName); |
| assert(!Number.second); |
| |
| // 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 << "::`" << Number.first << "'"; |
| 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, true); |
| if (Error) |
| return nullptr; |
| assert(TypeName); |
| |
| Name *QualName = demangleNameScopeChain(MangledName, TypeName); |
| if (Error) |
| return nullptr; |
| 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) { |
| // This is the final component of a symbol name (i.e. the leftmost component |
| // of a mangled name. Since the only possible template instantiation that |
| // can appear in this context is a function template, and since those are |
| // not saved for the purposes of name backreferences, only backref simple |
| // names. |
| Name *SymbolName = demangleUnqualifiedSymbolName(MangledName, NBB_Simple); |
| if (Error) |
| return nullptr; |
| |
| Name *QualName = demangleNameScopeChain(MangledName, SymbolName); |
| if (Error) |
| return nullptr; |
| assert(QualName); |
| return QualName; |
| } |
| |
| Name *Demangler::demangleUnqualifiedTypeName(StringView &MangledName, |
| bool Memorize) { |
| // An inner-most name can be a back-reference, because a fully-qualified name |
| // (e.g. Scope + Inner) can contain other fully qualified names inside of |
| // them (for example template parameters), and these nested parameters can |
| // refer to previously mangled types. |
| if (startsWithDigit(MangledName)) |
| return demangleBackRefName(MangledName); |
| |
| if (MangledName.startsWith("?$")) |
| return demangleTemplateInstantiationName(MangledName, NBB_Template); |
| |
| return demangleSimpleName(MangledName, Memorize); |
| } |
| |
| Name *Demangler::demangleUnqualifiedSymbolName(StringView &MangledName, |
| NameBackrefBehavior NBB) { |
| if (startsWithDigit(MangledName)) |
| return demangleBackRefName(MangledName); |
| if (MangledName.startsWith("?$")) |
| return demangleTemplateInstantiationName(MangledName, NBB); |
| if (MangledName.startsWith('?')) |
| return demangleOperatorName(MangledName, false).second; |
| return demangleSimpleName(MangledName, (NBB & NBB_Simple) != 0); |
| } |
| |
| Name *Demangler::demangleNameScopePiece(StringView &MangledName) { |
| if (startsWithDigit(MangledName)) |
| return demangleBackRefName(MangledName); |
| |
| if (MangledName.startsWith("?$")) |
| return demangleTemplateInstantiationName(MangledName, NBB_Template); |
| |
| if (MangledName.startsWith("?A")) |
| return demangleAnonymousNamespaceName(MangledName); |
| |
| if (startsWithLocalScopePattern(MangledName)) |
| return demangleLocallyScopedNamePiece(MangledName); |
| |
| return demangleSimpleName(MangledName, 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 '9': |
| return FuncClass(ExternC | NoPrototype); |
| 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 FuncClass(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 'O': |
| return FuncClass(Protected | Virtual | StaticThisAdjust); |
| case 'P': |
| return FuncClass(Protected | Virtual | StaticThisAdjust | Far); |
| case 'Q': |
| return FuncClass(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 'W': |
| return FuncClass(Public | Virtual | StaticThisAdjust); |
| case 'X': |
| return FuncClass(Public | Virtual | StaticThisAdjust | Far); |
| case 'Y': |
| return FuncClass(Global); |
| case 'Z': |
| return FuncClass(Global | Far); |
| case '$': { |
| FuncClass VFlag = VirtualThisAdjust; |
| if (MangledName.consumeFront('R')) |
| VFlag = FuncClass(VFlag | VirtualThisAdjustEx); |
| |
| switch (MangledName.popFront()) { |
| case '0': |
| return FuncClass(Private | Virtual | VFlag); |
| case '1': |
| return FuncClass(Private | Virtual | VFlag | Far); |
| case '2': |
| return FuncClass(Protected | Virtual | VFlag); |
| case '3': |
| return FuncClass(Protected | Virtual | VFlag | Far); |
| case '4': |
| return FuncClass(Public | Virtual | VFlag); |
| case '5': |
| return FuncClass(Public | Virtual | VFlag | 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; |
| if (QMM == QualifierMangleMode::Mangle) { |
| std::tie(Quals, IsMember) = demangleQualifiers(MangledName); |
| } else if (QMM == QualifierMangleMode::Result) { |
| if (MangledName.consumeFront('?')) |
| std::tie(Quals, IsMember) = demangleQualifiers(MangledName); |
| } |
| |
| Type *Ty = nullptr; |
| if (isTagType(MangledName)) |
| Ty = demangleClassType(MangledName); |
| else if (isPointerType(MangledName)) { |
| if (isMemberPointer(MangledName)) |
| 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 ExtraFlags = FuncClass::None; |
| if (MangledName.consumeFront("$$J0")) |
| ExtraFlags = FuncClass::ExternC; |
| |
| FuncClass FC = demangleFunctionClass(MangledName); |
| FC = FuncClass(ExtraFlags | FC); |
| |
| FunctionType::ThisAdjustor *Adjustor = nullptr; |
| if (FC & FuncClass::StaticThisAdjust) { |
| Adjustor = Arena.alloc<FunctionType::ThisAdjustor>(); |
| Adjustor->StaticOffset = demangleSigned(MangledName); |
| } else if (FC & FuncClass::VirtualThisAdjust) { |
| Adjustor = Arena.alloc<FunctionType::ThisAdjustor>(); |
| if (FC & FuncClass::VirtualThisAdjustEx) { |
| Adjustor->VBPtrOffset = demangleSigned(MangledName); |
| Adjustor->VBOffsetOffset = demangleSigned(MangledName); |
| } |
| Adjustor->VtordispOffset = demangleSigned(MangledName); |
| Adjustor->StaticOffset = demangleSigned(MangledName); |
| } |
| |
| FunctionType *FTy = nullptr; |
| if (FC & NoPrototype) { |
| // This is an extern "C" function whose full signature hasn't been mangled. |
| // This happens when we need to mangle a local symbol inside of an extern |
| // "C" function. |
| FTy = Arena.alloc<FunctionType>(); |
| } else { |
| bool HasThisQuals = !(FC & (Global | Static)); |
| FTy = demangleFunctionType(MangledName, HasThisQuals, false); |
| } |
| FTy->ThisAdjust = Adjustor; |
| 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; |
| } |
| if (MangledName.consumeFront("?")) { |
| Ty->Prim = PrimTy::Custom; |
| Ty->Custom = demangleSimpleString(MangledName, false); |
| if (!MangledName.consumeFront('@')) { |
| Error = true; |
| return 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(); |
| |
| uint64_t Rank = 0; |
| bool IsNegative = false; |
| std::tie(Rank, IsNegative) = demangleNumber(MangledName); |
| if (IsNegative || Rank == 0) { |
| Error = true; |
| return nullptr; |
| } |
| |
| ArrayType *ATy = Arena.alloc<ArrayType>(); |
| ATy->Prim = PrimTy::Array; |
| ATy->Dims = Arena.alloc<ArrayDimension>(); |
| ArrayDimension *Dim = ATy->Dims; |
| for (uint64_t I = 0; I < Rank; ++I) { |
| std::tie(Dim->Dim, IsNegative) = demangleNumber(MangledName); |
| if (IsNegative) { |
| Error = true; |
| return nullptr; |
| } |
| if (I + 1 < Rank) { |
| Dim->Next = Arena.alloc<ArrayDimension>(); |
| Dim = Dim->Next; |
| } |
| } |
| |
| if (MangledName.consumeFront("$$C")) { |
| bool IsMember = false; |
| std::tie(ATy->Quals, IsMember) = demangleQualifiers(MangledName); |
| if (IsMember) { |
| Error = true; |
| return nullptr; |
| } |
| } |
| |
| ATy->ElementType = demangleType(MangledName, QualifierMangleMode::Drop); |
| return ATy; |
| } |
| |
| // Reads a function 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 >= Backrefs.FunctionParamCount) { |
| Error = true; |
| return {}; |
| } |
| MangledName = MangledName.dropFront(); |
| |
| *Current = Arena.alloc<FunctionParams>(); |
| (*Current)->Current = Backrefs.FunctionParams[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 (Backrefs.FunctionParamCount <= 9 && CharsConsumed > 1) |
| Backrefs.FunctionParams[Backrefs.FunctionParamCount++] = |
| (*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>(); |
| |
| TemplateParams &TP = **Current; |
| |
| // Empty parameter pack. |
| if (MangledName.consumeFront("$S") || MangledName.consumeFront("$$V") || |
| MangledName.consumeFront("$$$V")) { |
| TP.IsEmptyParameterPack = true; |
| } else if (MangledName.consumeFront("$$Y")) { |
| // Template alias |
| TP.IsTemplateTemplate = true; |
| TP.IsAliasTemplate = true; |
| TP.ParamName = demangleFullyQualifiedTypeName(MangledName); |
| } else if (MangledName.consumeFront("$$B")) { |
| // Array |
| TP.ParamType = demangleType(MangledName, QualifierMangleMode::Drop); |
| } else if (MangledName.consumeFront("$$C")) { |
| // Type has qualifiers. |
| TP.ParamType = demangleType(MangledName, QualifierMangleMode::Mangle); |
| } else if (MangledName.startsWith("$1") || MangledName.startsWith("$H") || |
| MangledName.startsWith("$I") || MangledName.startsWith("$J")) { |
| MangledName = MangledName.dropFront(); |
| // 1 - single inheritance <name> |
| // H - multiple inheritance <name> <number> |
| // I - virtual inheritance <name> <number> <number> <number> |
| // J - unspecified inheritance <name> <number> <number> <number> |
| char InheritanceSpecifier = MangledName.popFront(); |
| // Pointer to member |
| Symbol *S = MangledName.startsWith('?') ? parse(MangledName) : nullptr; |
| switch (InheritanceSpecifier) { |
| case 'J': |
| TP.ThunkOffsets[TP.ThunkOffsetCount++] = demangleSigned(MangledName); |
| LLVM_FALLTHROUGH; |
| case 'I': |
| TP.ThunkOffsets[TP.ThunkOffsetCount++] = demangleSigned(MangledName); |
| LLVM_FALLTHROUGH; |
| case 'H': |
| TP.ThunkOffsets[TP.ThunkOffsetCount++] = demangleSigned(MangledName); |
| LLVM_FALLTHROUGH; |
| case '1': |
| break; |
| default: |
| Error = true; |
| break; |
| } |
| TP.PointerToSymbol = true; |
| if (S) { |
| TP.ParamName = S->SymbolName; |
| TP.ParamType = S->SymbolType; |
| } else |
| TP.NullptrLiteral = true; |
| } else if (MangledName.startsWith("$E?")) { |
| MangledName.consumeFront("$E"); |
| // Reference to symbol |
| Symbol *S = parse(MangledName); |
| TP.ParamName = S->SymbolName; |
| TP.ParamType = S->SymbolType; |
| TP.ReferenceToSymbol = true; |
| } else if (MangledName.startsWith("$F") || MangledName.startsWith("$G")) { |
| // Data member pointer. |
| MangledName = MangledName.dropFront(); |
| char InheritanceSpecifier = MangledName.popFront(); |
| |
| switch (InheritanceSpecifier) { |
| case 'G': |
| TP.ThunkOffsets[TP.ThunkOffsetCount++] = demangleSigned(MangledName); |
| LLVM_FALLTHROUGH; |
| case 'F': |
| TP.ThunkOffsets[TP.ThunkOffsetCount++] = demangleSigned(MangledName); |
| TP.ThunkOffsets[TP.ThunkOffsetCount++] = demangleSigned(MangledName); |
| LLVM_FALLTHROUGH; |
| case '0': |
| break; |
| default: |
| Error = true; |
| break; |
| } |
| TP.DataMemberPointer = true; |
| |
| } else if (MangledName.consumeFront("$0")) { |
| // Integral non-type template parameter |
| bool IsNegative = false; |
| uint64_t Value = 0; |
| std::tie(Value, IsNegative) = demangleNumber(MangledName); |
| |
| TP.IsIntegerLiteral = true; |
| TP.IntegerLiteralIsNegative = IsNegative; |
| TP.IntegralValue = Value; |
| } else { |
| TP.ParamType = demangleType(MangledName, QualifierMangleMode::Drop); |
| } |
| if (Error) |
| return nullptr; |
| |
| Current = &TP.Next; |
| } |
| |
| if (Error) |
| return nullptr; |
| |
| // Template parameter lists cannot be variadic, so it can only be terminated |
| // by @. |
| if (MangledName.consumeFront('@')) |
| return Head; |
| Error = true; |
| return nullptr; |
| } |
| |
| void Demangler::output(const Symbol *S, OutputStream &OS) { |
| if (S->Category == SymbolCategory::Unknown) { |
| outputName(OS, S->SymbolName, S->SymbolType); |
| return; |
| } |
| |
| if (S->Category == SymbolCategory::SpecialOperator) { |
| outputSpecialOperator(OS, S->SymbolName); |
| return; |
| } |
| |
| // 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. |
| if (S->SymbolType) { |
| Type::outputPre(OS, *S->SymbolType); |
| outputName(OS, S->SymbolName, S->SymbolType); |
| Type::outputPost(OS, *S->SymbolType); |
| } else { |
| outputQualifiers(OS, S->SymbolQuals); |
| outputName(OS, S->SymbolName, nullptr); |
| } |
| } |
| |
| void Demangler::dumpBackReferences() { |
| std::printf("%d function parameter backreferences\n", |
| (int)Backrefs.FunctionParamCount); |
| |
| // Create an output stream so we can render each type. |
| OutputStream OS = OutputStream::create(nullptr, 0, 1024); |
| for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) { |
| OS.setCurrentPosition(0); |
| |
| Type *T = Backrefs.FunctionParams[I]; |
| Type::outputPre(OS, *T); |
| Type::outputPost(OS, *T); |
| |
| std::printf(" [%d] - %.*s\n", (int)I, (int)OS.getCurrentPosition(), |
| OS.getBuffer()); |
| } |
| std::free(OS.getBuffer()); |
| |
| if (Backrefs.FunctionParamCount > 0) |
| std::printf("\n"); |
| std::printf("%d name backreferences\n", (int)Backrefs.NamesCount); |
| for (size_t I = 0; I < Backrefs.NamesCount; ++I) { |
| std::printf(" [%d] - %.*s\n", (int)I, (int)Backrefs.Names[I].size(), |
| Backrefs.Names[I].begin()); |
| } |
| if (Backrefs.NamesCount > 0) |
| std::printf("\n"); |
| } |
| |
| char *llvm::microsoftDemangle(const char *MangledName, char *Buf, size_t *N, |
| int *Status, MSDemangleFlags Flags) { |
| Demangler D; |
| StringView Name{MangledName}; |
| Symbol *S = D.parse(Name); |
| |
| if (Flags & MSDF_DumpBackrefs) |
| D.dumpBackReferences(); |
| OutputStream OS = OutputStream::create(Buf, N, 1024); |
| if (D.Error) { |
| OS << MangledName; |
| *Status = llvm::demangle_invalid_mangled_name; |
| } else { |
| D.output(S, OS); |
| *Status = llvm::demangle_success; |
| } |
| |
| OS << '\0'; |
| return OS.getBuffer(); |
| } |