[MS Demangler] Rework the way operators are demangled.

Previously, some of the code for actually parsing mangled
operator names was more like formatting code in nature,
and was interspersed with the demangling code which builds
the AST.  This means that by the time we got to the printing
code, we had lost all information about what type of operator
we had, and all we were left with was a string that we just
had to print.  However, not all operators are actually even
operators.  it's basically just a catch-all mangling for
"special names", and for some of the other types it helps
to know when we're actually doing the printing what it is.

This patch changes the way things work by introducing an
OperatorInfo structure and corresponding enumeration.  When
we demangle we store the enumeration value and demangled
components separately.  This gives more flexibility during
printing.

In doing so, some demanglings of special names which we didn't
previously support come out of this for free, so we now demangle
those.

A few are more complex and are better left for a followup patch
though.

An exhaustive test of every possible operator code is included,
with the ones that don't yet work commented out.

llvm-svn: 340046
This commit is contained in:
Zachary Turner 2018-08-17 16:14:05 +00:00
parent 594d93848a
commit 75b475a1c8
3 changed files with 556 additions and 225 deletions

View File

@ -135,7 +135,7 @@ enum class StorageClass : uint8_t {
ProtectedStatic,
PublicStatic,
Global,
FunctionLocalStatic
FunctionLocalStatic,
};
enum class QualifierMangleMode { Drop, Mangle, Result };
@ -191,7 +191,175 @@ enum class PrimTy : uint8_t {
Float,
Double,
Ldouble,
Nullptr
Nullptr,
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[]
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;
};
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},
{"__K", "operator \"\"", OperatorTy::LiteralOperator},
{"__L", "co_await", OperatorTy::CoAwait},
};
// Function classes
@ -213,7 +381,7 @@ enum NameBackrefBehavior : uint8_t {
NBB_Simple = 1 << 1, // save simple names.
};
enum class SymbolCategory { Unknown, Function, Variable, StringLiteral };
enum class SymbolCategory { Unknown, Function, Variable };
namespace {
@ -287,26 +455,33 @@ struct Type {
// Represents an identifier which may be a template.
struct Name {
virtual ~Name() = default;
bool IsTemplateInstantiation = false;
bool IsOperator = false;
bool IsBackReference = false;
bool IsConversionOperator = false;
bool IsStringLiteral = false;
bool IsLongStringLiteral = false;
// If IsStringLiteral is true, this is the character type.
PrimTy StringLiteralType = PrimTy::None;
bool isStringLiteralOperatorInfo() const;
// Name read from an MangledName string.
StringView Str;
// Template parameters. Only valid if Flags contains NF_TemplateInstantiation.
// 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 {
const OperatorMapEntry *Info = nullptr;
};
struct StringLiteral : public OperatorInfo {
PrimTy CharType;
bool IsTruncated = false;
};
struct PointerType : public Type {
Type *clone(ArenaAllocator &Arena) const override;
void outputPre(OutputStream &OS, NameResolver &Resolver) override;
@ -526,9 +701,8 @@ static void outputParameterList(OutputStream &OS, const FunctionParams &Params,
}
}
static void outputStringLiteral(OutputStream &OS, const Name &TheString) {
assert(TheString.IsStringLiteral);
switch (TheString.StringLiteralType) {
static void outputStringLiteral(OutputStream &OS, const StringLiteral &Str) {
switch (Str.CharType) {
case PrimTy::Wchar:
OS << "const wchar_t * {L\"";
break;
@ -544,8 +718,8 @@ static void outputStringLiteral(OutputStream &OS, const Name &TheString) {
default:
LLVM_BUILTIN_UNREACHABLE;
}
OS << TheString.Str << "\"";
if (TheString.IsLongStringLiteral)
OS << Str.Str << "\"";
if (Str.IsTruncated)
OS << "...";
OS << "}";
}
@ -593,20 +767,20 @@ static void outputParameterList(OutputStream &OS, const TemplateParams &Params,
OS << ">";
}
static void outputNameComponent(OutputStream &OS, bool IsBackReference,
const TemplateParams *TParams, StringView Str,
NameResolver &Resolver) {
if (IsBackReference)
Str = Resolver.resolve(Str);
OS << Str;
if (TParams)
outputParameterList(OS, *TParams, Resolver);
}
static void outputNameComponent(OutputStream &OS, const Name &N,
NameResolver &Resolver) {
if (N.IsConversionOperator) {
OS << " conv";
} else {
StringView S = N.Str;
if (N.IsBackReference)
S = Resolver.resolve(N.Str);
OS << S;
}
if (N.IsTemplateInstantiation && N.TParams)
outputParameterList(OS, *N.TParams, Resolver);
outputNameComponent(OS, N.IsBackReference, N.TParams, N.Str, Resolver);
}
static void outputName(OutputStream &OS, const Name *TheName, const Type *Ty,
@ -630,16 +804,17 @@ static void outputName(OutputStream &OS, const Name *TheName, const Type *Ty,
return;
}
const OperatorInfo &Operator = static_cast<const OperatorInfo &>(*TheName);
// Print out ctor or dtor.
if (TheName->Str == "dtor")
switch (Operator.Info->Operator) {
case OperatorTy::Dtor:
OS << "~";
if (TheName->Str == "ctor" || TheName->Str == "dtor") {
LLVM_FALLTHROUGH;
case OperatorTy::Ctor:
outputNameComponent(OS, *Previous, Resolver);
return;
}
if (TheName->IsConversionOperator) {
break;
case OperatorTy::Conversion:
OS << "operator";
if (TheName->IsTemplateInstantiation && TheName->TParams)
outputParameterList(OS, *TheName->TParams, Resolver);
@ -651,15 +826,33 @@ static void outputName(OutputStream &OS, const Name *TheName, const Type *Ty,
} else {
OS << "<conversion>";
}
} else {
// Print out an overloaded operator.
OS << "operator";
break;
case OperatorTy::StringLiteral: {
const StringLiteral &SL = static_cast<const StringLiteral &>(Operator);
outputStringLiteral(OS, SL);
break;
}
case OperatorTy::LiteralOperator:
OS << Operator.Info->Name;
outputNameComponent(OS, *TheName, Resolver);
break;
default:
OS << Operator.Info->Name;
if (Operator.IsTemplateInstantiation)
outputParameterList(OS, *Operator.TParams, Resolver);
break;
}
}
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);
}
@ -767,6 +960,9 @@ void Type::outputPre(OutputStream &OS, NameResolver &Resolver) {
case PrimTy::Nullptr:
OS << "std::nullptr_t";
break;
case PrimTy::Vbtable:
case PrimTy::Vftable:
break;
default:
assert(false && "Invalid primitive type!");
}
@ -862,9 +1058,11 @@ void FunctionType::outputPre(OutputStream &OS, NameResolver &Resolver) {
if (FunctionClass & Static)
OS << "static ";
}
if (FunctionClass & ExternC) {
if (FunctionClass & ExternC)
OS << "extern \"C\" ";
}
if (FunctionClass & Virtual)
OS << "virtual ";
if (ReturnType) {
Type::outputPre(OS, *ReturnType, Resolver);
@ -997,6 +1195,7 @@ public:
private:
Type *demangleVariableEncoding(StringView &MangledName);
Type *demangleFunctionEncoding(StringView &MangledName);
Type *demangleVtableEncoding(StringView &MangledName);
Qualifiers demanglePointerExtQualifiers(StringView &MangledName);
@ -1034,11 +1233,11 @@ private:
Name *demangleBackRefName(StringView &MangledName);
Name *demangleTemplateInstantiationName(StringView &MangledName,
NameBackrefBehavior NBB);
Name *demangleOperatorName(StringView &MangledName);
OperatorInfo *demangleOperatorName(StringView &MangledName);
Name *demangleSimpleName(StringView &MangledName, bool Memorize);
Name *demangleAnonymousNamespaceName(StringView &MangledName);
Name *demangleLocallyScopedNamePiece(StringView &MangledName);
Name *demangleStringLiteral(StringView &MangledName);
StringLiteral *demangleStringLiteral(StringView &MangledName);
StringView demangleSimpleString(StringView &MangledName, bool Memorize);
@ -1102,26 +1301,31 @@ Symbol *Demangler::parse(StringView &MangledName) {
return S;
}
if (MangledName.consumeFront("?_C@_")) {
// This is a string literal. Just demangle it and return.
S->Category = SymbolCategory::StringLiteral;
S->SymbolName = demangleStringLiteral(MangledName);
S->SymbolType = nullptr;
return S;
}
// What follows is a main symbol name. This may include
// namespaces or class BackReferences.
S->SymbolName = demangleFullyQualifiedSymbolName(MangledName);
if (Error)
return nullptr;
if (S->SymbolName->isStringLiteralOperatorInfo())
return S;
// Read a variable.
if (startsWithDigit(MangledName) && !MangledName.startsWith('9')) {
// 9 is a special marker for an extern "C" function with
// no prototype.
switch (MangledName.front()) {
case '0':
case '1':
case '2':
case '3':
case '4':
S->Category = SymbolCategory::Variable;
S->SymbolType = demangleVariableEncoding(MangledName);
} else {
break;
case '6':
case '7':
S->Category = SymbolCategory::Variable;
S->SymbolType = demangleVtableEncoding(MangledName);
break;
default:
S->Category = SymbolCategory::Function;
S->SymbolType = demangleFunctionEncoding(MangledName);
}
@ -1132,6 +1336,23 @@ Symbol *Demangler::parse(StringView &MangledName) {
return S;
}
Type *Demangler::demangleVtableEncoding(StringView &MangledName) {
Type *Ty = Arena.alloc<Type>();
switch (MangledName.popFront()) {
case '6':
Ty->Prim = PrimTy::Vftable;
break;
case '7':
Ty->Prim = PrimTy::Vbtable;
break;
}
bool IsMember = false;
std::tie(Ty->Quals, IsMember) = demangleQualifiers(MangledName);
Ty->Storage = StorageClass::None;
MangledName.consumeFront('@');
return Ty;
}
// <type-encoding> ::= <storage-class> <variable-type>
// <storage-class> ::= 0 # private static member
// ::= 1 # protected static member
@ -1271,166 +1492,41 @@ Name *Demangler::demangleTemplateInstantiationName(StringView &MangledName,
return Node;
}
Name *Demangler::demangleOperatorName(StringView &MangledName) {
OperatorInfo *Demangler::demangleOperatorName(StringView &MangledName) {
assert(MangledName.startsWith('?'));
MangledName.consumeFront('?');
auto NameString = [this, &MangledName]() -> StringView {
switch (MangledName.popFront()) {
case '0':
return "ctor";
case '1':
return "dtor";
case '2':
return " new";
case '3':
return " delete";
case '4':
return "=";
case '5':
return ">>";
case '6':
return "<<";
case '7':
return "!";
case '8':
return "==";
case '9':
return "!=";
case 'A':
return "[]";
case 'C':
return "->";
case 'D':
return "*";
case 'E':
return "++";
case 'F':
return "--";
case 'G':
return "-";
case 'H':
return "+";
case 'I':
return "&";
case 'J':
return "->*";
case 'K':
return "/";
case 'L':
return "%";
case 'M':
return "<";
case 'N':
return "<=";
case 'O':
return ">";
case 'P':
return ">=";
case 'Q':
return ",";
case 'R':
return "()";
case 'S':
return "~";
case 'T':
return "^";
case 'U':
return "|";
case 'V':
return "&&";
case 'W':
return "||";
case 'X':
return "*=";
case 'Y':
return "+=";
case 'Z':
return "-=";
case '_': {
if (MangledName.empty())
break;
switch (MangledName.popFront()) {
case '0':
return "/=";
case '1':
return "%=";
case '2':
return ">>=";
case '3':
return "<<=";
case '4':
return "&=";
case '5':
return "|=";
case '6':
return "^=";
// case '7': # vftable
// case '8': # vbtable
// case '9': # vcall
// case 'A': # typeof
// case 'B': # local static guard
// case 'D': # vbase destructor
// case 'E': # vector deleting destructor
// case 'F': # default constructor closure
// case 'G': # scalar deleting destructor
// case 'H': # vector constructor iterator
// case 'I': # vector destructor iterator
// case 'J': # vector vbase constructor iterator
// case 'K': # virtual displacement map
// case 'L': # eh vector constructor iterator
// case 'M': # eh vector destructor iterator
// case 'N': # eh vector vbase constructor iterator
// case 'O': # copy constructor closure
// case 'P<name>': # udt returning <name>
// case 'Q': # <unknown>
// case 'R0': # RTTI Type Descriptor
// case 'R1': # RTTI Base Class Descriptor at (a,b,c,d)
// case 'R2': # RTTI Base Class Array
// case 'R3': # RTTI Class Hierarchy Descriptor
// case 'R4': # RTTI Complete Object Locator
// case 'S': # local vftable
// case 'T': # local vftable constructor closure
case 'U':
return " new[]";
case 'V':
return " delete[]";
case '_':
if (MangledName.consumeFront("L"))
return " co_await";
if (MangledName.consumeFront("K")) {
size_t EndPos = MangledName.find('@');
if (EndPos == StringView::npos)
break;
StringView OpName = demangleSimpleString(MangledName, false);
size_t FullSize = OpName.size() + 3; // <space>""OpName
char *Buffer = Arena.allocUnalignedBuffer(FullSize);
Buffer[0] = ' ';
Buffer[1] = '"';
Buffer[2] = '"';
std::memcpy(Buffer + 3, OpName.begin(), OpName.size());
return {Buffer, FullSize};
}
}
}
}
Error = true;
return "";
};
Name *Node = Arena.alloc<Name>();
if (MangledName.consumeFront('B')) {
// Handle conversion operator specially.
Node->IsConversionOperator = true;
} else {
Node->Str = NameString();
const OperatorMapEntry *Entry = nullptr;
for (const auto &MapEntry : OperatorMap) {
if (!MangledName.consumeFront(MapEntry.Prefix))
continue;
Entry = &MapEntry;
break;
}
if (!Entry) {
Error = true;
return nullptr;
}
OperatorInfo *Oper = nullptr;
switch (Entry->Operator) {
case OperatorTy::StringLiteral:
Oper = demangleStringLiteral(MangledName);
break;
case OperatorTy::LiteralOperator:
Oper = Arena.alloc<OperatorInfo>();
Oper->Str = demangleSimpleString(MangledName, false);
break;
default:
Oper = Arena.alloc<OperatorInfo>();
}
Oper->Info = Entry;
Oper->IsOperator = true;
if (Error)
return nullptr;
Node->IsOperator = true;
return Node;
return Oper;
}
Name *Demangler::demangleSimpleName(StringView &MangledName, bool Memorize) {
@ -1601,11 +1697,13 @@ static void outputEscapedChar(OutputStream &OS, unsigned 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 End - StringBytes + 1;
return Count;
}
unsigned countEmbeddedNulls(const uint8_t *StringBytes, unsigned Length) {
@ -1664,7 +1762,7 @@ static unsigned decodeMultiByteChar(const uint8_t *StringBytes,
return Result;
}
Name *Demangler::demangleStringLiteral(StringView &MangledName) {
StringLiteral *Demangler::demangleStringLiteral(StringView &MangledName) {
// This function uses goto, so declare all variables up front.
OutputStream OS;
StringView CRC;
@ -1674,10 +1772,11 @@ Name *Demangler::demangleStringLiteral(StringView &MangledName) {
size_t CrcEndPos = 0;
char *ResultBuffer = nullptr;
Name *Result = Arena.alloc<Name>();
Result->IsStringLiteral = true;
StringLiteral *Result = Arena.alloc<StringLiteral>();
// Prefix indicating the beginning of a string literal
if (!MangledName.consumeFront("@_"))
goto StringLiteralError;
if (MangledName.empty())
goto StringLiteralError;
@ -1708,14 +1807,14 @@ Name *Demangler::demangleStringLiteral(StringView &MangledName) {
OS = OutputStream::create(nullptr, nullptr, 1024);
if (IsWcharT) {
Result->StringLiteralType = PrimTy::Wchar;
Result->CharType = PrimTy::Wchar;
if (StringByteSize > 64)
Result->IsLongStringLiteral = true;
Result->IsTruncated = true;
while (!MangledName.consumeFront('@')) {
assert(StringByteSize >= 2);
wchar_t W = demangleWcharLiteral(MangledName);
if (StringByteSize != 2 || Result->IsLongStringLiteral)
if (StringByteSize != 2 || Result->IsTruncated)
outputEscapedChar(OS, W);
StringByteSize -= 2;
if (Error)
@ -1723,7 +1822,7 @@ Name *Demangler::demangleStringLiteral(StringView &MangledName) {
}
} else {
if (StringByteSize > 32)
Result->IsLongStringLiteral = true;
Result->IsTruncated = true;
constexpr unsigned MaxStringByteLength = 32;
uint8_t StringBytes[MaxStringByteLength];
@ -1739,13 +1838,13 @@ Name *Demangler::demangleStringLiteral(StringView &MangledName) {
assert(StringByteSize % CharBytes == 0);
switch (CharBytes) {
case 1:
Result->StringLiteralType = PrimTy::Char;
Result->CharType = PrimTy::Char;
break;
case 2:
Result->StringLiteralType = PrimTy::Char16;
Result->CharType = PrimTy::Char16;
break;
case 4:
Result->StringLiteralType = PrimTy::Char32;
Result->CharType = PrimTy::Char32;
break;
default:
LLVM_BUILTIN_UNREACHABLE;
@ -1754,7 +1853,7 @@ Name *Demangler::demangleStringLiteral(StringView &MangledName) {
for (unsigned CharIndex = 0; CharIndex < NumChars; ++CharIndex) {
unsigned NextChar =
decodeMultiByteChar(StringBytes, CharIndex, CharBytes);
if (CharIndex + 1 < NumChars || Result->IsLongStringLiteral)
if (CharIndex + 1 < NumChars || Result->IsTruncated)
outputEscapedChar(OS, NextChar);
}
}
@ -1856,7 +1955,11 @@ Name *Demangler::demangleFullyQualifiedSymbolName(StringView &MangledName) {
Name *SymbolName = demangleUnqualifiedSymbolName(MangledName, NBB_Simple);
if (Error)
return nullptr;
// This is a special case that isn't followed by a scope.
assert(SymbolName);
if (SymbolName->isStringLiteralOperatorInfo())
return SymbolName;
Name *QualName = demangleNameScopeChain(MangledName, SymbolName);
if (Error)
@ -2590,10 +2693,6 @@ void Demangler::output(const Symbol *S, OutputStream &OS) {
outputName(OS, S->SymbolName, S->SymbolType, *this);
return;
}
if (S->Category == SymbolCategory::StringLiteral) {
outputStringLiteral(OS, *S->SymbolName);
return;
}
// Converts an AST to a string.
//
@ -2612,9 +2711,12 @@ void Demangler::output(const Symbol *S, OutputStream &OS) {
// the "first half" of type declaration, and outputPost() writes the
// "second half". For example, outputPre() writes a return type for a
// function and outputPost() writes an parameter list.
Type::outputPre(OS, *S->SymbolType, *this);
outputName(OS, S->SymbolName, S->SymbolType, *this);
Type::outputPost(OS, *S->SymbolType, *this);
if (S->SymbolType) {
Type::outputPre(OS, *S->SymbolType, *this);
outputName(OS, S->SymbolName, S->SymbolType, *this);
Type::outputPost(OS, *S->SymbolType, *this);
} else
outputName(OS, S->SymbolName, nullptr, *this);
}
void Demangler::dumpBackReferences() {

View File

@ -0,0 +1,223 @@
; RUN: llvm-undname < %s | FileCheck %s
; CHECK-NOT: Invalid mangled name
??0Base@@QEAA@XZ
; CHECK: __cdecl Base::Base(void)
??1Base@@UEAA@XZ
; CHECK: virtual __cdecl Base::~Base(void)
??2@YAPEAX_K@Z
; CHECK: void * __cdecl operator new(unsigned __int64)
??3@YAXPEAX_K@Z
; CHECK: void __cdecl operator delete(void *, unsigned __int64)
??4Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator=(int)
??6Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator<<(int)
??5Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator>>(int)
??7Base@@QEAAHXZ
; CHECK: int __cdecl Base::operator!(void)
??8Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator==(int)
??9Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator!=(int)
??ABase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator[](int)
??BBase@@QEAAHXZ
; CHECK: __cdecl Base::operator int(void)
??CBase@@QEAAHXZ
; CHECK: int __cdecl Base::operator->(void)
??DBase@@QEAAHXZ
; CHECK: int __cdecl Base::operator*(void)
??EBase@@QEAAHXZ
; CHECK: int __cdecl Base::operator++(void)
??EBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator++(int)
??FBase@@QEAAHXZ
; CHECK: int __cdecl Base::operator--(void)
??FBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator--(int)
??GBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator-(int)
??HBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator+(int)
??IBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator&(int)
??JBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator->*(int)
??KBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator/(int)
??LBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator%(int)
??MBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator<(int)
??NBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator<=(int)
??OBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator>(int)
??PBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator>=(int)
??QBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator,(int)
??RBase@@QEAAHXZ
; CHECK: int __cdecl Base::operator()(void)
??SBase@@QEAAHXZ
; CHECK: int __cdecl Base::operator~(void)
??TBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator^(int)
??UBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator|(int)
??VBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator&&(int)
??WBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator||(int)
??XBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator*=(int)
??YBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator+=(int)
??ZBase@@QEAAHH@Z
; CHECK: int __cdecl Base::operator-=(int)
??_0Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator/=(int)
??_1Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator%=(int)
??_2Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator>>=(int)
??_3Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator<<=(int)
??_4Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator&=(int)
??_5Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator|=(int)
??_6Base@@QEAAHH@Z
; CHECK: int __cdecl Base::operator^=(int)
??_7Base@@6B@
; CHECK: const Base::`vftable'
??_8Middle2@@7B@
; CHECK: const Middle2::`vbtable'
; ??_9Base@@$B7AA
; FIXME: [thunk]: __cdecl Base::`vcall'{8, {flat}}' }'
; ??_B?1??getS@@YAAAUS@@XZ@51
; FIXME: `struct S & __cdecl getS(void)'::`2'::`local static guard'{2}'
??_C@_02PCEFGMJL@hi?$AA@
; CHECK: const char * {"hi"}
??_DDiamond@@QEAAXXZ
; CHECK: void __cdecl Diamond::`vbase dtor'(void)
??_EBase@@UEAAPEAXI@Z
; CHECK: virtual void * __cdecl Base::`vector deleting dtor'(unsigned int)
; ??_EDerived@@$4PPPPPPPM@A@EAAPEAXI@Z
; FIXME: [thunk]:virtual void * __cdecl Derived::`vector deleting dtor'`vtordisp{4294967292, 0}' (unsigned int)
??_F?$SomeTemplate@H@@QAEXXZ
; CHECK: void __thiscall SomeTemplate<int>::`default ctor closure'(void)
??_GBase@@UEAAPEAXI@Z
; CHECK: virtual void * __cdecl Base::`scalar deleting dtor'(unsigned int)
??_H@YAXPEAX_K1P6APEAX0@Z@Z
; CHECK: void __cdecl `vector ctor iterator'(void *, unsigned __int64, unsigned __int64, void * (__cdecl *)(void *))
??_I@YAXPEAX_K1P6AX0@Z@Z
; CHECK: void __cdecl `vector dtor iterator'(void *, unsigned __int64, unsigned __int64, void (__cdecl *)(void *))
??_JBase@@UEAAPEAXI@Z
; CHECK: virtual void * __cdecl Base::`vector vbase ctor iterator'(unsigned int)
??_KBase@@UEAAPEAXI@Z
; CHECK: virtual void * __cdecl Base::`virtual displacement map'(unsigned int)
??_LBase@@UEAAPEAXI@Z
; CHECK: virtual void * __cdecl Base::`eh vector ctor iterator'(unsigned int)
??_MBase@@UEAAPEAXI@Z
; CHECK: virtual void * __cdecl Base::`eh vector dtor iterator'(unsigned int)
??_NBase@@UEAAPEAXI@Z
; CHECK: virtual void * __cdecl Base::`eh vector vbase ctor iterator'(unsigned int)
??_O?$SomeTemplate@H@@QAEXXZ
; CHECK: void __thiscall SomeTemplate<int>::`copy ctor closure'(void)
??_SBase@@6B@
; CHECK: const Base::`local vftable'
??_TDerived@@QEAAXXZ
; CHECK: void __cdecl Derived::`local vftable ctor closure'(void)
??_U@YAPEAX_KAEAVklass@@@Z
; CHECK: void * __cdecl operator new[](unsigned __int64, class klass &)
??_V@YAXPEAXAEAVklass@@@Z
; CHECK: void __cdecl operator delete[](void *, class klass &)
; ??_R0?AUBase@@@8
; FIXME: struct Base `RTTI Type Descriptor'
; ??_R1A@?0A@EA@Base@@8
; FIXME: Base::`RTTI Base Class Descriptor at (0, -1, 0, 64)'
; ??_R2Base@@8
; FIXME: Base::`RTTI Base Class Array'
; ??_R3Base@@8
; FIXME: Base::`RTTI Class Hierarchy Descriptor'
??_R4Base@@6B@
; CHECK: const Base::`RTTI Complete Object Locator'
??__K_deg@@YAHO@Z
; CHECK: int __cdecl operator ""_deg(long double)

View File

@ -514,7 +514,13 @@
; CHECK: const char * {"\x03"}
; CHECK: const char * {"\x02"}
; CHECK: const char * {"\x01"}
; CHECK: const char * {"\x00"}
; The mangling doesn't distinguish between char and char16 types, so even though
; this was originally written as a char * with one embedded null, it mangles
; identically to a char16_t * that is empty. So when demangling, we choose the
; "smartest" one, which happened to be wrong, but it's still a "better"
; demangling.
; CHECK: const char16_t * {u""}
??_C@_13KDLDGPGJ@?$AA?7?$AA?$AA@