[MS Demangler] Demangle pointers to member functions.
authorZachary Turner <zturner@google.com>
Thu, 26 Jul 2018 20:20:10 +0000 (20:20 +0000)
committerZachary Turner <zturner@google.com>
Thu, 26 Jul 2018 20:20:10 +0000 (20:20 +0000)
After this patch, we can now properly demangle pointers to member
functions.  The calling convention is located in the wrong place,
but this will be fixed in a followup since it also affects non
member function pointers.

Differential Revision: https://reviews.llvm.org/D49639

llvm-svn: 338065

llvm/lib/Demangle/MicrosoftDemangle.cpp
llvm/lib/Demangle/StringView.h
llvm/test/Demangle/ms-basic.test
llvm/test/Demangle/ms-mangle.test

index b6eaa4b..1bf94ff 100644 (file)
@@ -187,6 +187,8 @@ struct Type;
 // Represents a list of parameters (template params or function arguments.
 // It's represented as a linked list.
 struct ParamList {
+  bool IsVariadic = false;
+
   Type *Current = nullptr;
 
   ParamList *Next = nullptr;
@@ -382,6 +384,11 @@ static void outputCallingConvention(OutputStream &OS, CallingConv CC) {
 
 // Write a function or template parameter list.
 static void outputParameterList(OutputStream &OS, const ParamList &Params) {
+  if (!Params.Current) {
+    OS << "void";
+    return;
+  }
+
   const ParamList *Head = &Params;
   while (Head) {
     Type::outputPre(OS, *Head->Current);
@@ -621,8 +628,10 @@ void FunctionType::outputPre(OutputStream &OS) {
       OS << "static ";
   }
 
-  if (ReturnType)
+  if (ReturnType) {
     Type::outputPre(OS, *ReturnType);
+    OS << " ";
+  }
 
   outputCallingConvention(OS, CallConvention);
 }
@@ -635,6 +644,9 @@ void FunctionType::outputPost(OutputStream &OS) {
     OS << " const";
   if (Quals & Q_Volatile)
     OS << " volatile";
+
+  if (ReturnType)
+    Type::outputPost(OS, *ReturnType);
   return;
 }
 
@@ -711,6 +723,7 @@ private:
   UdtType *demangleClassType();
   PointerType *demanglePointerType();
   MemberPointerType *demangleMemberPointerType();
+  FunctionType *demangleFunctionType(bool HasThisQuals);
 
   ArrayType *demangleArrayType();
 
@@ -724,10 +737,11 @@ private:
   Name *demangleName();
   void demangleOperator(Name *);
   StringView demangleOperatorName();
-  int demangleFunctionClass();
+  FuncClass demangleFunctionClass();
   CallingConv demangleCallingConvention();
   StorageClass demangleVariableStorageClass();
   ReferenceKind demangleReferenceKind();
+  void demangleThrowSpecification();
 
   std::pair<Qualifiers, bool> demangleQualifiers();
 
@@ -904,10 +918,6 @@ void Demangler::demangleNamePiece(Name &Node, bool IsHead) {
     // Class template.
     Node.Str = demangleString(false);
     Node.TemplateParams = demangleParameterList();
-    if (!MangledName.consumeFront('@')) {
-      Error = true;
-      return;
-    }
   } else if (!IsHead && MangledName.consumeFront("?A")) {
     // Anonymous namespace starts with ?A.  So does overloaded operator[],
     // but the distinguishing factor is that namespace themselves are not
@@ -1067,7 +1077,7 @@ StringView Demangler::demangleOperatorName() {
   return "";
 }
 
-int Demangler::demangleFunctionClass() {
+FuncClass Demangler::demangleFunctionClass() {
   SwapAndRestore<StringView> RestoreOnError(MangledName, MangledName);
   RestoreOnError.shouldRestore(false);
 
@@ -1075,48 +1085,48 @@ int Demangler::demangleFunctionClass() {
   case 'A':
     return Private;
   case 'B':
-    return Private | Far;
+    return FuncClass(Private | Far);
   case 'C':
-    return Private | Static;
+    return FuncClass(Private | Static);
   case 'D':
-    return Private | Static;
+    return FuncClass(Private | Static);
   case 'E':
-    return Private | Virtual;
+    return FuncClass(Private | Virtual);
   case 'F':
-    return Private | Virtual;
+    return FuncClass(Private | Virtual);
   case 'I':
     return Protected;
   case 'J':
-    return Protected | Far;
+    return FuncClass(Protected | Far);
   case 'K':
-    return Protected | Static;
+    return FuncClass(Protected | Static);
   case 'L':
-    return Protected | Static | Far;
+    return FuncClass(Protected | Static | Far);
   case 'M':
-    return Protected | Virtual;
+    return FuncClass(Protected | Virtual);
   case 'N':
-    return Protected | Virtual | Far;
+    return FuncClass(Protected | Virtual | Far);
   case 'Q':
     return Public;
   case 'R':
-    return Public | Far;
+    return FuncClass(Public | Far);
   case 'S':
-    return Public | Static;
+    return FuncClass(Public | Static);
   case 'T':
-    return Public | Static | Far;
+    return FuncClass(Public | Static | Far);
   case 'U':
-    return Public | Virtual;
+    return FuncClass(Public | Virtual);
   case 'V':
-    return Public | Virtual | Far;
+    return FuncClass(Public | Virtual | Far);
   case 'Y':
     return Global;
   case 'Z':
-    return Global | Far;
+    return FuncClass(Global | Far);
   }
 
   Error = true;
   RestoreOnError.shouldRestore(true);
-  return 0;
+  return Public;
 }
 
 CallingConv Demangler::demangleCallingConvention() {
@@ -1241,15 +1251,6 @@ Type *Demangler::demangleType(QualifierMangleMode QMM) {
   return Ty;
 }
 
-static bool functionHasThisPtr(const FunctionType &Ty) {
-  assert(Ty.Prim == PrimTy::Function);
-  if (Ty.FunctionClass & Global)
-    return false;
-  if (Ty.FunctionClass & Static)
-    return false;
-  return true;
-}
-
 ReferenceKind Demangler::demangleReferenceKind() {
   if (MangledName.consumeFront('G'))
     return ReferenceKind::LValueRef;
@@ -1258,12 +1259,18 @@ ReferenceKind Demangler::demangleReferenceKind() {
   return ReferenceKind::None;
 }
 
-Type *Demangler::demangleFunctionEncoding() {
-  FunctionType *FTy = Arena.alloc<FunctionType>();
+void Demangler::demangleThrowSpecification() {
+  if (MangledName.consumeFront('Z'))
+    return;
 
+  Error = true;
+}
+
+FunctionType *Demangler::demangleFunctionType(bool HasThisQuals) {
+  FunctionType *FTy = Arena.alloc<FunctionType>();
   FTy->Prim = PrimTy::Function;
-  FTy->FunctionClass = (FuncClass)demangleFunctionClass();
-  if (functionHasThisPtr(*FTy)) {
+
+  if (HasThisQuals) {
     FTy->Quals = demanglePointerExtQualifiers();
     FTy->RefKind = demangleReferenceKind();
     FTy->Quals = Qualifiers(FTy->Quals | demangleQualifiers().first);
@@ -1280,6 +1287,18 @@ Type *Demangler::demangleFunctionEncoding() {
 
   FTy->Params = demangleParameterList();
 
+  demangleThrowSpecification();
+
+  return FTy;
+}
+
+Type *Demangler::demangleFunctionEncoding() {
+  FuncClass FC = demangleFunctionClass();
+
+  bool HasThisQuals = !(FC & (Global | Static));
+  FunctionType *FTy = demangleFunctionType(HasThisQuals);
+  FTy->FunctionClass = FC;
+
   return FTy;
 }
 
@@ -1445,14 +1464,20 @@ MemberPointerType *Demangler::demangleMemberPointerType() {
   Qualifiers ExtQuals = demanglePointerExtQualifiers();
   Pointer->Quals = Qualifiers(Pointer->Quals | ExtQuals);
 
-  Qualifiers PointeeQuals = Q_None;
-  bool IsMember = false;
-  std::tie(PointeeQuals, IsMember) = demangleQualifiers();
-  assert(IsMember);
-  Pointer->MemberName = demangleName();
+  if (MangledName.consumeFront("8")) {
+    Pointer->MemberName = demangleName();
+    Pointer->Pointee = demangleFunctionType(true);
+  } else {
+    Qualifiers PointeeQuals = Q_None;
+    bool IsMember = false;
+    std::tie(PointeeQuals, IsMember) = demangleQualifiers();
+    assert(IsMember);
+    Pointer->MemberName = demangleName();
+
+    Pointer->Pointee = demangleType(QualifierMangleMode::Drop);
+    Pointer->Pointee->Quals = PointeeQuals;
+  }
 
-  Pointer->Pointee = demangleType(QualifierMangleMode::Drop);
-  Pointer->Pointee->Quals = PointeeQuals;
   return Pointer;
 }
 
@@ -1507,6 +1532,12 @@ ParamList Demangler::demangleParameterList() {
   Type *BackRef[10];
   int Idx = 0;
 
+  // Empty parameter list.
+  // FIXME: Will this cause problems if demangleParameterList() is called in the
+  // context of a template parameter list?
+  if (MangledName.consumeFront('X'))
+    return {};
+
   ParamList *Head;
   ParamList **Current = &Head;
   while (!Error && !MangledName.startsWith('@') &&
@@ -1537,7 +1568,22 @@ ParamList Demangler::demangleParameterList() {
     Current = &(*Current)->Next;
   }
 
-  return *Head;
+  if (Error)
+    return {};
+
+  // A non-empty parameter list is terminated by either 'Z' (variadic) parameter
+  // list or '@' (non variadic).  Careful not to consume "@Z", as in that case
+  // the following Z could be a throw specifier.
+  if (MangledName.consumeFront('@'))
+    return *Head;
+
+  if (MangledName.consumeFront('Z')) {
+    Head->IsVariadic = true;
+    return *Head;
+  }
+
+  Error = true;
+  return {};
 }
 
 void Demangler::output() {
index 55cab48..3416db2 100644 (file)
@@ -45,7 +45,7 @@ public:
 
   StringView dropFront(size_t N = 1) const {
     if (N >= size())
-      N = size() - 1;
+      N = size();
     return StringView(First + N, Last);
   }
 
index 17694b9..2811017 100644 (file)
 
 
 ??4klass@@QEAAAEBV0@AEBV0@@Z
-; CHECK: class klass const &__cdecl klass::operator=(class klass const &)
+; CHECK: class klass const & __cdecl klass::operator=(class klass const &)
 
 ??7klass@@QEAA_NXZ
 ; CHECK: bool __cdecl klass::operator!(void)
 ; CHECK: int __cdecl klass::operator^=(int)
 
 ??6@YAAEBVklass@@AEBV0@H@Z
-; CHECK: class klass const &__cdecl operator<<(class klass const &, int)
+; CHECK: class klass const & __cdecl operator<<(class klass const &, int)
 
 ??5@YAAEBVklass@@AEBV0@_K@Z
-; CHECK: class klass const &__cdecl operator>>(class klass const &, unsigned __int64)
+; CHECK: class klass const & __cdecl operator>>(class klass const &, unsigned __int64)
 
 ??2@YAPEAX_KAEAVklass@@@Z
-; CHECK: void *__cdecl operator new(unsigned __int64, class klass &)
+; CHECK: void * __cdecl operator new(unsigned __int64, class klass &)
 
 ??_U@YAPEAX_KAEAVklass@@@Z
-; CHECK: void *__cdecl operator new[](unsigned __int64, class klass &)
+; CHECK: void * __cdecl operator new[](unsigned __int64, class klass &)
 
 ??3@YAXPEAXAEAVklass@@@Z
 ; CHECK: void __cdecl operator delete(void *, class klass &)
index 2266ef8..d6ec36c 100644 (file)
 ; CHECK: int __cdecl foo::operator+(int)
 
 ?static_method@foo@@SAPAV1@XZ
-; CHECK: static class foo *__cdecl foo::static_method(void)
+; CHECK: static class foo * __cdecl foo::static_method(void)
 
 ?static_method@foo@@SAPEAV1@XZ
-; CHECK: static class foo *__cdecl foo::static_method(void)
+; CHECK: static class foo * __cdecl foo::static_method(void)
 
 ?g@bar@@2HA
 ; CHECK: static int bar::g
 ?j@@3P6GHCE@ZA
 ; CHECK: int __stdcall (*j)(signed char, unsigned char)
 
+?funptr@@YAP6AHXZXZ
+; CHECK: int __cdecl (* __cdecl funptr(void))(void)
+
 ?k@@3PTfoo@@DT1@
 ; CHECK: char const volatile foo::*k
 
 ?k@@3PETfoo@@DET1@
 ; CHECK: char const volatile foo::*k
 
-; FIXME: We don't support member function pointers yet.
-; ?l@@3P8foo@@AEHH@ZQ1@
-; FIXME: int __thiscall (foo::*l)(int)
+?l@@3P8foo@@AEHH@ZQ1@
+; CHECK: int __thiscall (foo::*l)(int)
 
 ?g_cInt@@3HB
 ; CHECK: int const g_cInt
 ; CHECK: void __cdecl zeta(int __cdecl (*)(int, int))
 
 ??2@YAPAXI@Z
-; CHECK: void *__cdecl operator new(unsigned int)
+; CHECK: void * __cdecl operator new(unsigned int)
 
 ??3@YAXPAX@Z
 ; CHECK: void __cdecl operator delete(void *)
 
 ??_U@YAPAXI@Z
-; CHECK: void *__cdecl operator new[](unsigned int)
+; CHECK: void * __cdecl operator new[](unsigned int)
 
 ??_V@YAXPAX@Z
 ; CHECK: void __cdecl operator delete[](void *)
 ; ?color4@@3QAY02$$CBNA
 ; FIXME-EXTRACONST: double const (*color4)[3]
 
-; FIXME-MEMBERPTR: We don't support member pointers yet.
-; ?memptr1@@3RESB@@HES1
-; FIXME-MEMBERPTR: volatile int B::*memptr2
+?memptr1@@3RESB@@HES1@
+; CHECK: int volatile B::*volatile memptr1
 
-; ?memptr2@@3PESB@@HES1
-; FIXME: volatile int B::*memptr2
+?memptr2@@3PESB@@HES1@
+; CHECK: int volatile B::*memptr2
 
-; ?memptr3@@3REQB@@HEQ1
-; FIXME-MEMBERPTR: int B::* volatile memptr3
+?memptr3@@3REQB@@HEQ1@
+; CHECK: int B::*volatile memptr3
 
-; ?funmemptr1@@3RESB@@R6AHXZES1
-; FIXME-MEMBERPTR: int __cdecl (* volatile B::* volatile funmemptr1)(void)
+?funmemptr1@@3RESB@@R6AHXZES1@
+; CHECK: int __cdecl (*volatile B::*volatile funmemptr1)(void)
 
-; ?funmemptr2@@3PESB@@R6AHXZES1
-; FIXME-MEMBERPTR: int __cdecl (* volatile B::*funmemptr2)(void)
+?funmemptr2@@3PESB@@R6AHXZES1@
+; CHECK: int __cdecl (*volatile B::*funmemptr2)(void)
 
-; ?funmemptr3@@3REQB@@P6AHXZEQ1
-; FIXME-MEMBERPTR: int __cdecl (* B::*volatile funmemptr3)(void)
+?funmemptr3@@3REQB@@P6AHXZEQ1@
+; CHECK: int __cdecl (*B::*volatile funmemptr3)(void)
 
-; ?memptrtofun1@@3R8B@@EAAXXZEQ1
-; FIXME-MEMBERPTR: void __cdecl (B::*volatile memptrtofun1)(void)
+?memptrtofun1@@3R8B@@EAAXXZEQ1@
+; CHECK: void __cdecl (B::*volatile memptrtofun1)(void)
 
-; ?memptrtofun2@@3P8B@@EAAXXZEQ1
-; FIXME-MEMBERPTR: void __cdecl (B::*memptrtofun2)(void)
+?memptrtofun2@@3P8B@@EAAXXZEQ1@
+; CHECK: void __cdecl (B::*memptrtofun2)(void)
 
-; ?memptrtofun3@@3P8B@@EAAXXZEQ1
-; FIXME-MEMBERPTR: void __cdecl (B::*memptrtofun3)(void)
+?memptrtofun3@@3P8B@@EAAXXZEQ1@
+; CHECK: void __cdecl (B::*memptrtofun3)(void)
 
-; ?memptrtofun4@@3R8B@@EAAHXZEQ1
-; FIXME-MEMBERPTR: int __cdecl (B::* volatile memptrtofun4)(void)
+?memptrtofun4@@3R8B@@EAAHXZEQ1@
+; CHECK: int __cdecl (B::*volatile memptrtofun4)(void)
 
-; ?memptrtofun5@@3P8B@@EAA?CHXZEQ1
-; FIXME-MEMBERPTR: int volatile __cdecl (B::*memptrtofun5)(void)
+?memptrtofun5@@3P8B@@EAA?CHXZEQ1@
+; CHECK: int volatile __cdecl (B::*memptrtofun5)(void)
 
-; ?memptrtofun6@@3P8B@@EAA?BHXZEQ1
-; FIXME-MEMBERPTR: int const __cdecl (B::*memptrtofun6)(void)
+?memptrtofun6@@3P8B@@EAA?BHXZEQ1@
+; CHECK: int const __cdecl (B::*memptrtofun6)(void)
 
-; ?memptrtofun7@@3R8B@@EAAP6AHXZXZEQ1
-; FIXME-MEMBERPTR: int __cdecl (*(__cdecl B::*volatile memptrtofun7)(void))(void)
+?memptrtofun7@@3R8B@@EAAP6AHXZXZEQ1@
+; CHECK: int __cdecl (* __cdecl (B::*volatile memptrtofun7)(void))(void)
 
-; ?memptrtofun8@@3P8B@@EAAR6AHXZXZEQ1
-; FIXME-MEMBERPTR: int __cdecl (*(__cdecl B::*memptrtofun8)(void))(void)
+?memptrtofun8@@3P8B@@EAAR6AHXZXZEQ1@
+; CHECK: int __cdecl (*volatile __cdecl (B::*memptrtofun8)(void))(void)
 
-; ?memptrtofun9@@3P8B@@EAAQ6AHXZXZEQ1
-; FIXME-MEMBERPTR: int __cdecl(*(__cdecl B::*memptrtofun9)(void))(void)
+?memptrtofun9@@3P8B@@EAAQ6AHXZXZEQ1@
+; CHECK: int __cdecl (*const __cdecl (B::*memptrtofun9)(void))(void)
 
 
 ?fooE@@YA?AW4E@@XZ
 ; FIXME-EXTERNC: int `extern_c_func'::`2'::local
 
 ??2OverloadedNewDelete@@SAPAXI@Z
-; CHECK: static void *__cdecl OverloadedNewDelete::operator new(unsigned int)
+; CHECK: static void * __cdecl OverloadedNewDelete::operator new(unsigned int)
 
 
 ??_UOverloadedNewDelete@@SAPAXI@Z
-; CHECK: static void *__cdecl OverloadedNewDelete::operator new[](unsigned int)
+; CHECK: static void * __cdecl OverloadedNewDelete::operator new[](unsigned int)
 
 ??3OverloadedNewDelete@@SAXPAX@Z
 ; CHECK: static void __cdecl OverloadedNewDelete::operator delete(void *)
 ; CHECK: int __thiscall OverloadedNewDelete::operator+(int)
 
 ??2OverloadedNewDelete@@SAPEAX_K@Z
-; CHECK: static void *__cdecl OverloadedNewDelete::operator new(unsigned __int64)
+; CHECK: static void * __cdecl OverloadedNewDelete::operator new(unsigned __int64)
 
 ??_UOverloadedNewDelete@@SAPEAX_K@Z
-; CHECK: static void *__cdecl OverloadedNewDelete::operator new[](unsigned __int64)
+; CHECK: static void * __cdecl OverloadedNewDelete::operator new[](unsigned __int64)
 
 ??3OverloadedNewDelete@@SAXPEAX@Z
 ; CHECK: static void __cdecl OverloadedNewDelete::operator delete(void *)
 
 
 ??2TypedefNewDelete@@SAPAXI@Z
-; CHECK: static void *__cdecl TypedefNewDelete::operator new(unsigned int)
+; CHECK: static void * __cdecl TypedefNewDelete::operator new(unsigned int)
 
 
 ??_UTypedefNewDelete@@SAPAXI@Z
-; CHECK: static void *__cdecl TypedefNewDelete::operator new[](unsigned int)
+; CHECK: static void * __cdecl TypedefNewDelete::operator new[](unsigned int)
 
 ??3TypedefNewDelete@@SAXPAX@Z
 ; CHECK: static void __cdecl TypedefNewDelete::operator delete(void *)