mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
9be9e38aa6
Previously if you had something like this: template<typename T> struct Foo { template<typename U> Foo(U); }; Foo F(3.7); this would mangle as ??$?0N@?$Foo@H@@QEAA@N@Z and this would be demangled as: undname: __cdecl Foo<int>::Foo<int><double>(double) llvm-undname: __cdecl Foo<int>::Foo<int>(double) Note the lack of the constructor template parameter in our demangling. This patch makes it so we print the constructor argument list. llvm-svn: 340356
3212 lines
93 KiB
C++
3212 lines
93 KiB
C++
//===- 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;
|
|
|
|
bool isStringLiteralOperatorInfo() const;
|
|
|
|
// 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 {
|
|
|
|
bool Name::isStringLiteralOperatorInfo() const {
|
|
if (!IsOperator)
|
|
return false;
|
|
const OperatorInfo &O = static_cast<const OperatorInfo &>(*this);
|
|
return O.Info->Operator == OperatorTy::StringLiteral;
|
|
}
|
|
|
|
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();
|
|
}
|