Index: include/lldb/Symbol/ClangASTContext.h =================================================================== --- include/lldb/Symbol/ClangASTContext.h +++ include/lldb/Symbol/ClangASTContext.h @@ -831,6 +831,8 @@ bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial); + void AddMethodOverridesForCXXRecordType(lldb::opaque_compiler_type_t type); + // C++ Base Classes clang::CXXBaseSpecifier * CreateBaseClassSpecifier(lldb::opaque_compiler_type_t type, Index: lit/SymbolFile/PDB/Inputs/ClassLayoutTest.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/Inputs/ClassLayoutTest.cpp @@ -0,0 +1,111 @@ +// To avoid linking MSVC specific libs, we don't test virtual/override methods +// that needs vftable support in this file. + +// Enum. +enum Enum { RED, GREEN, BLUE }; +Enum EnumVar; + +// Union. +union Union { + short Row; + unsigned short Col; + int Line : 16; // Test named bitfield. + short : 8; // Unnamed bitfield symbol won't be generated in PDB. + long Table; +}; +Union UnionVar; + +// Struct. +struct Struct; +typedef Struct StructTypedef; + +struct Struct { + bool A; + unsigned char UCharVar; + unsigned int UIntVar; + long long LongLongVar; + Enum EnumVar; // Test struct has UDT member. + int array[10]; +}; +struct Struct StructVar; + +struct _List; // Forward declaration. +struct Complex { + struct _List *array[90]; + struct { // Test unnamed struct. MSVC treats it as `int x` + int x; + }; + union { // Test unnamed union. MSVC treats it as `int a; float b;` + int a; + float b; + }; +}; +struct Complex c; + +struct _List { // Test doubly linked list. + struct _List *current; + struct _List *previous; + struct _List *next; +}; +struct _List ListVar; + +typedef struct { + int a; +} UnnamedStruct; // Test unnamed typedef-ed struct. +UnnamedStruct UnnanmedVar; + +// Class. +namespace MemberTest { +class Base { +public: + Base() {} + ~Base() {} + +public: + int Get() { return 0; } + +protected: + int a; +}; +class Friend { +public: + int f() { return 3; } +}; +class Class : public Base { // Test base class. + friend Friend; + static int m_static; // Test static member variable. +public: + Class() : m_public(), m_private(), m_protected() {} + explicit Class(int a) { m_public = a; } // Test first reference of m_public. + ~Class() {} + + static int StaticMemberFunc(int a, ...) { + return 1; + } // Test static member function. + int Get() { return 1; } + int f(Friend c) { return c.f(); } + inline bool operator==(const Class &rhs) const // Test operator. + { + return (m_public == rhs.m_public); + } + +public: + int m_public; + struct Struct m_struct; + +private: + Union m_union; + int m_private; + +protected: + friend class Friend; + int m_protected; +}; +} // namespace MemberTest + +int main() { + MemberTest::Base B1; + B1.Get(); + MemberTest::Class::StaticMemberFunc(1, 10, 2); + return 0; +} Index: lit/SymbolFile/PDB/Inputs/PointerTypeTest.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/Inputs/PointerTypeTest.cpp @@ -0,0 +1,23 @@ +int main() { + // Test pointer to array. + int array[2][4]; + int(*array_pointer)[2][4] = &array; + + struct ST { + int a; + int f(int x) { return 1; } + }; + + ST s = {10}; + + // Test pointer to a local. + int *p_int = &s.a; + + // Test pointer to data member. + int ST::*p_member_field = &ST::a; + + // Test pointer to member function. + int (ST::*p_member_method)(int) = &ST::f; + + return 0; +} Index: lit/SymbolFile/PDB/Inputs/UdtLayoutTest.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/Inputs/UdtLayoutTest.cpp @@ -0,0 +1,61 @@ +struct A { + explicit A(int u) { _u._u3 = u; } + A(const A &) = default; + virtual ~A() = default; + +private: + union U { + char _u1; + short _u2; + int _u3; + }; + + A::U _u; +}; + +#pragma pack(push, 1) +template struct B : public virtual A { + B(char a, unsigned short b, int c) : A(a + b + c), _a(a), _b(b), _c(c) {} + +private: + char _a; + unsigned short : 3; + unsigned short _b : 6; + unsigned short : 4; + int _c; +}; +#pragma pack(pop) + +#pragma pack(push, 16) +class C : private virtual B<0>, public virtual B<1>, private B<2>, public B<3> { +public: + C(char x, char y, char z) + : A(x - y + z), B<0>(x, y, z), B<1>(x * 2, y * 2, z * 2), + B<2>(x * 3, y * 3, z * 3), B<3>(x * 4, y * 4, z * 4), _x(x * 5), + _y(y * 5), _z(z * 5) {} + + static int abc; + +private: + int _x; + short _y; + char _z; +}; +int C::abc = 123; +#pragma pack(pop) + +class List { +public: + List() = default; + List(List *p, List *n, C v) : Prev(p), Next(n), Value(v) {} + +private: + List *Prev = nullptr; + List *Next = nullptr; + C Value{1, 2, 3}; +}; + +int main() { + List ls[16]; + return 0; +} Index: lit/SymbolFile/PDB/Inputs/UdtLayoutTest.script =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/Inputs/UdtLayoutTest.script @@ -0,0 +1,4 @@ +breakpoint set --file UdtLayoutTest.cpp --line 60 +run +target variable +frame variable Index: lit/SymbolFile/PDB/class-layout.test =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/class-layout.test @@ -0,0 +1,92 @@ +REQUIRES: windows +RUN: clang-cl -m32 /Z7 /c /GS- %S/Inputs/ClassLayoutTest.cpp /o %T/ClassLayoutTest.cpp.obj +RUN: link %T/ClassLayoutTest.cpp.obj /DEBUG /nodefaultlib /ENTRY:main /OUT:%T/ClassLayoutTest.cpp.exe +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=ENUM %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=UNION %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=STRUCT %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=COMPLEX %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=LIST %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=UNNAMED-STRUCT %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=BASE %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=FRIEND %s +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=CLASS %s + +CHECK: Module [[MOD:.*]] +CHECK: {{^[0-9A-F]+}}: SymbolVendor ([[MOD]]) +CHECK: {{^[0-9A-F]+}}: CompileUnit{{[{]0x[0-9a-f]+[}]}}, language = "c++", file = '{{.*}}\ClassLayoutTest.cpp' + +ENUM: name = "Enum", size = 4, decl = ClassLayoutTest.cpp:5 +ENUM-SAME: enum Enum { +ENUM: RED, +ENUM: GREEN, +ENUM: BLUE +ENUM:} + +UNION: name = "Union", size = 4, decl = ClassLayoutTest.cpp:9 +UNION-SAME: union Union { +UNION: short Row; +UNION: unsigned short Col; +UNION: int Line : 16; +UNION: long Table; +UNION:} + +STRUCT: name = "Struct", size = 64, decl = ClassLayoutTest.cpp:22 +STRUCT-SAME: struct Struct { +STRUCT: bool A; +STRUCT: unsigned char UCharVar; +STRUCT: unsigned int UIntVar; +STRUCT: long long LongLongVar; +STRUCT: Enum EnumVar; +STRUCT: int array[10]; +STRUCT:} + +COMPLEX: name = "Complex", size = 368, decl = ClassLayoutTest.cpp:33 +COMPLEX-SAME: struct Complex { +COMPLEX: _List *array[90]; +COMPLEX: int x; +COMPLEX: int a; +COMPLEX: float b; +COMPLEX:} + +LIST: name = "_List", size = 12, decl = ClassLayoutTest.cpp:45 +LIST-SAME: struct _List { +LIST: _List *current; +LIST: _List *previous; +LIST: _List *next; +LIST:} + +UNNAMED-STRUCT: name = "UnnamedStruct", size = 4, decl = ClassLayoutTest.cpp:52 +UNNAMED-STRUCT-SAME: struct UnnamedStruct { +UNNAMED-STRUCT: int a; +UNNAMED-STRUCT:} + +BASE: name = "MemberTest::Base", size = 4, decl = ClassLayoutTest.cpp:59 +BASE-SAME: class MemberTest::Base { +BASE: int a; +BASE: void {{.*}}Base(); +BASE: {{.*}}~Base(); +BASE: int {{.*}}Get(); +BASE:} + +FRIEND: name = "MemberTest::Friend", size = 1, decl = ClassLayoutTest.cpp:70 +FRIEND-SAME: class MemberTest::Friend { +FRIEND: int f(); +FRIEND: } + +CLASS: name = "MemberTest::Class", size = 88, decl = ClassLayoutTest.cpp:74 +CLASS-SAME: class MemberTest::Class : public MemberTest::Base { +CLASS: static int m_static; +CLASS: int m_public; +CLASS: Struct m_struct; +CLASS: Union m_union; +CLASS: int m_private; +CLASS: int m_protected; +CLASS: void Class(); +CLASS: void Class(int); +CLASS: ~MemberTest::Class(); +CLASS: static int {{.*}}StaticMemberFunc(int, ...); +CLASS: int Get(); +CLASS: int f(MemberTest::Friend); +CLASS: bool operator==(const MemberTest::Class &) +CLASS:} Index: lit/SymbolFile/PDB/pointers.test =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/pointers.test @@ -0,0 +1,38 @@ +REQUIRES: windows +RUN: clang-cl -m32 /Z7 /c /GS- %S/Inputs/PointerTypeTest.cpp /o %T/PointerTypeTest.cpp.obj +RUN: link %T/PointerTypeTest.cpp.obj /DEBUG /nodefaultlib /ENTRY:main /OUT:%T/PointerTypeTest.cpp.exe +RUN: lldb-test symbols %T/PointerTypeTest.cpp.exe | FileCheck %s +RUN: lldb-test symbols %T/PointerTypeTest.cpp.exe | FileCheck --check-prefix=MAIN-ST-F %s +RUN: lldb-test symbols %T/PointerTypeTest.cpp.exe | FileCheck --check-prefix=MAIN-ST %s +RUN: lldb-test symbols %T/PointerTypeTest.cpp.exe | FileCheck --check-prefix=MAIN %s +RUN: lldb-test symbols %T/PointerTypeTest.cpp.exe | FileCheck --check-prefix=F %s + +CHECK: Module [[MOD:.*]] +CHECK: {{^[0-9A-F]+}}: CompileUnit{{[{]0x[0-9a-f]+[}]}}, language = "c++", file = '{{.*}}\PointerTypeTest.cpp' + +MAIN-ST-F: name = "main::ST::f" +MAIN-ST-F-SAME: decl = PointerTypeTest.cpp:8 +MAIN-ST-F-SAME: compiler_type = {{.*}} int (int) + +MAIN-ST: name = "main::ST", size = 4, decl = PointerTypeTest.cpp:6, compiler_type = {{.*}} struct main::ST { +MAIN-ST-NEXT: int a; +MAIN-ST-NEXT: int {{.*}}f(int); +MAIN-ST-NEXT:} + +MAIN: Function{[[FID1:.*]]}, mangled = _main +MAIN-NEXT: Block{[[FID1]]} +MAIN: Variable{{.*}}, name = "array_pointer" +MAIN-SAME: (int (*)[2][4]), scope = local +MAIN: Variable{{.*}}, name = "p_int" +MAIN-SAME: (int *), scope = local +MAIN: Variable{{.*}}, name = "p_member_field" +MAIN-SAME: (int main::ST::*), scope = local +MAIN: Variable{{.*}}, name = "p_member_method" +MAIN-SAME: (int (main::ST::*)(int)), scope = local + +F: Function{[[FID2:.*]]}, demangled = {{.*}}f(int) +F-NEXT: Block{[[FID2]]} +F: Variable{{.*}}, name = "this" +F-SAME: (main::ST *), scope = parameter, location = {{.*}}, artificial +F: Variable{{.*}}, name = "x" +F-SAME: (int), scope = parameter, decl = PointerTypeTest.cpp:8 Index: lit/SymbolFile/PDB/udt-layout.test =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/udt-layout.test @@ -0,0 +1,51 @@ +REQUIRES: windows +RUN: clang-cl /Zi %S/Inputs/UdtLayoutTest.cpp /o %t.exe +RUN: %lldb -b -s %S/Inputs/UdtLayoutTest.script -- %t.exe | FileCheck %s + +CHECK:(int) int C::abc = 123 +CHECK:(List [16]) ls = { +CHECK: [15] = { +CHECK: Prev = 0x00000000 +CHECK: Next = 0x00000000 +CHECK: Value = { +CHECK: B<0> = { +CHECK: A = { +CHECK: _u = (_u1 = '\x02', _u2 = 2, _u3 = 2) +CHECK: } +CHECK: _a = '\x01' +CHECK: _b = 2 +CHECK: _c = 3 +CHECK: } +CHECK: B<1> = { +CHECK: A = { +CHECK: _u = (_u1 = '\x02', _u2 = 2, _u3 = 2) +CHECK: } +CHECK: _a = '\x02' +CHECK: _b = 4 +CHECK: _c = 6 +CHECK: } +CHECK: B<2> = { +CHECK: A = { +CHECK: _u = (_u1 = '\x02', _u2 = 2, _u3 = 2) +CHECK: } +CHECK: _a = '\x03' +CHECK: _b = 6 +CHECK: _c = 9 +CHECK: } +CHECK: B<3> = { +CHECK: A = { +CHECK: _u = (_u1 = '\x02', _u2 = 2, _u3 = 2) +CHECK: } +CHECK: _a = '\x04' +CHECK: _b = 8 +CHECK: _c = 12 +CHECK: } +CHECK: A = { +CHECK: _u = (_u1 = '\x02', _u2 = 2, _u3 = 2) +CHECK: } +CHECK: _x = 5 +CHECK: _y = 10 +CHECK: _z = '\x0f' +CHECK: } +CHECK: } +CHECK:} Index: source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2108,95 +2108,6 @@ return template_param_infos.args.size() == template_param_infos.names.size(); } -// Checks whether m1 is an overload of m2 (as opposed to an override). This is -// called by addOverridesForMethod to distinguish overrides (which share a -// vtable entry) from overloads (which require distinct entries). -static bool isOverload(clang::CXXMethodDecl *m1, clang::CXXMethodDecl *m2) { - // FIXME: This should detect covariant return types, but currently doesn't. - lldbassert(&m1->getASTContext() == &m2->getASTContext() && - "Methods should have the same AST context"); - clang::ASTContext &context = m1->getASTContext(); - - const auto *m1Type = - llvm::cast( - context.getCanonicalType(m1->getType())); - - const auto *m2Type = - llvm::cast( - context.getCanonicalType(m2->getType())); - - auto compareArgTypes = - [&context](const clang::QualType &m1p, const clang::QualType &m2p) { - return context.hasSameType(m1p.getUnqualifiedType(), - m2p.getUnqualifiedType()); - }; - - // FIXME: In C++14 and later, we can just pass m2Type->param_type_end() - // as a fourth parameter to std::equal(). - return (m1->getNumParams() != m2->getNumParams()) || - !std::equal(m1Type->param_type_begin(), m1Type->param_type_end(), - m2Type->param_type_begin(), compareArgTypes); -} - -// If decl is a virtual method, walk the base classes looking for methods that -// decl overrides. This table of overridden methods is used by IRGen to -// determine the vtable layout for decl's parent class. -static void addOverridesForMethod(clang::CXXMethodDecl *decl) { - if (!decl->isVirtual()) - return; - - clang::CXXBasePaths paths; - - auto find_overridden_methods = - [decl](const clang::CXXBaseSpecifier *specifier, clang::CXXBasePath &path) { - if (auto *base_record = - llvm::dyn_cast( - specifier->getType()->getAs()->getDecl())) { - - clang::DeclarationName name = decl->getDeclName(); - - // If this is a destructor, check whether the base class destructor is - // virtual. - if (name.getNameKind() == clang::DeclarationName::CXXDestructorName) - if (auto *baseDtorDecl = base_record->getDestructor()) { - if (baseDtorDecl->isVirtual()) { - path.Decls = baseDtorDecl; - return true; - } else - return false; - } - - // Otherwise, search for name in the base class. - for (path.Decls = base_record->lookup(name); !path.Decls.empty(); - path.Decls = path.Decls.slice(1)) { - if (auto *method_decl = - llvm::dyn_cast(path.Decls.front())) - if (method_decl->isVirtual() && !isOverload(decl, method_decl)) { - path.Decls = method_decl; - return true; - } - } - } - - return false; - }; - - if (decl->getParent()->lookupInBases(find_overridden_methods, paths)) { - for (auto *overridden_decl : paths.found_decls()) - decl->addOverriddenMethod( - llvm::cast(overridden_decl)); - } -} - -// If clang_type is a CXXRecordDecl, builds the method override list for each -// of its virtual methods. -static void addMethodOverrides(ClangASTContext &ast, CompilerType &clang_type) { - if (auto *record = - ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType())) - for (auto *method : record->methods()) - addOverridesForMethod(method); -} - bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type, CompilerType &clang_type) { @@ -2405,7 +2316,7 @@ } } - addMethodOverrides(m_ast, clang_type); + m_ast.AddMethodOverridesForCXXRecordType(clang_type.GetOpaqueQualType()); ClangASTContext::BuildIndirectFields(clang_type); ClangASTContext::CompleteTagDeclarationDefinition(clang_type); Index: source/Plugins/SymbolFile/PDB/PDBASTParser.h =================================================================== --- source/Plugins/SymbolFile/PDB/PDBASTParser.h +++ source/Plugins/SymbolFile/PDB/PDBASTParser.h @@ -28,9 +28,14 @@ namespace llvm { namespace pdb { +template class ConcreteSymbolEnumerator; + class PDBSymbol; class PDBSymbolData; +class PDBSymbolFunc; +class PDBSymbolTypeBaseClass; class PDBSymbolTypeBuiltin; +class PDBSymbolTypeUDT; } // namespace pdb } // namespace llvm @@ -40,13 +45,46 @@ ~PDBASTParser(); lldb::TypeSP CreateLLDBTypeFromPDBType(const llvm::pdb::PDBSymbol &type); + bool CompleteTypeFromPDB(lldb_private::CompilerType &compiler_type); + + lldb_private::ClangASTImporter &GetClangASTImporter() { + return m_ast_importer; + } private: + typedef llvm::DenseMap + ClangTypeToUidMap; + typedef llvm::pdb::ConcreteSymbolEnumerator + PDBDataSymbolEnumerator; + typedef llvm::pdb::ConcreteSymbolEnumerator + PDBBaseClassSymbolEnumerator; + typedef llvm::pdb::ConcreteSymbolEnumerator + PDBFuncSymbolEnumerator; + bool AddEnumValue(lldb_private::CompilerType enum_type, const llvm::pdb::PDBSymbolData &data) const; + bool CompleteTypeFromUDT(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &compiler_type, + llvm::pdb::PDBSymbolTypeUDT &udt); + void AddRecordMembers( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + PDBDataSymbolEnumerator &members_enum, + lldb_private::ClangASTImporter::LayoutInfo &layout_info) const; + void AddRecordBases( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + int record_kind, + PDBBaseClassSymbolEnumerator &bases_enum, + lldb_private::ClangASTImporter::LayoutInfo &layout_info) const; + void AddRecordMethods( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + PDBFuncSymbolEnumerator &methods_enum) const; lldb_private::ClangASTContext &m_ast; lldb_private::ClangASTImporter m_ast_importer; + ClangTypeToUidMap m_forward_decl_clang_type_to_uid; }; #endif // LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H Index: source/Plugins/SymbolFile/PDB/PDBASTParser.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/PDBASTParser.cpp +++ source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -9,11 +9,15 @@ #include "PDBASTParser.h" +#include "SymbolFilePDB.h" + #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "lldb/Core/Module.h" #include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/Declaration.h" #include "lldb/Symbol/SymbolFile.h" @@ -48,8 +52,9 @@ return clang::TTK_Union; case PDB_UdtType::Interface: return clang::TTK_Interface; + default: + llvm_unreachable("unsuported PDB UDT type"); } - return -1; } lldb::Encoding TranslateBuiltinEncoding(PDB_BuiltinType type) { @@ -199,6 +204,68 @@ decl.SetLine(first_line_up->getLineNumber()); return true; } + +AccessType TranslateMemberAccess(PDB_MemberAccess access) { + switch (access) { + case PDB_MemberAccess::Private: + return eAccessPrivate; + case PDB_MemberAccess::Protected: + return eAccessProtected; + case PDB_MemberAccess::Public: + return eAccessPublic; + default: + return eAccessNone; + } +} + +AccessType GetDefaultAccessibilityForUdtKind(PDB_UdtType udt_kind) { + switch (udt_kind) { + case PDB_UdtType::Struct: + case PDB_UdtType::Union: + return eAccessPublic; + case PDB_UdtType::Class: + case PDB_UdtType::Interface: + return eAccessPrivate; + default: + llvm_unreachable("unsupported PDB UDT type"); + } +} + +AccessType GetAccessibilityForUdt(const PDBSymbolTypeUDT &udt) { + AccessType access = TranslateMemberAccess(udt.getAccess()); + if (access != lldb::eAccessNone || !udt.isNested()) + return access; + + auto parent = udt.getClassParent(); + if (!parent) + return lldb::eAccessNone; + + auto parent_udt = llvm::dyn_cast(parent.get()); + if (!parent_udt) + return lldb::eAccessNone; + + return GetDefaultAccessibilityForUdtKind(parent_udt->getUdtKind()); +} + +clang::MSInheritanceAttr::Spelling GetMSInheritance( + const PDBSymbolTypeUDT &udt) { + int base_count = 0; + bool has_virtual = false; + + auto bases_enum = udt.findAllChildren(); + if (bases_enum) { + while (auto base = bases_enum->getNext()) { + base_count++; + has_virtual |= base->isVirtualBaseClass(); + } + } + + if (has_virtual) + return clang::MSInheritanceAttr::Keyword_virtual_inheritance; + if (base_count > 1) + return clang::MSInheritanceAttr::Keyword_multiple_inheritance; + return clang::MSInheritanceAttr::Keyword_single_inheritance; +} } // namespace PDBASTParser::PDBASTParser(lldb_private::ClangASTContext &ast) : m_ast(ast) {} @@ -216,29 +283,90 @@ Declaration decl; switch (type.getSymTag()) { + case PDB_SymType::BaseClass: { + auto symbol_file = m_ast.GetSymbolFile(); + if (!symbol_file) + return nullptr; + + auto ty = symbol_file->ResolveTypeUID(type.getRawSymbol().getTypeId()); + return ty ? ty->shared_from_this() : nullptr; + } break; case PDB_SymType::UDT: { auto udt = llvm::dyn_cast(&type); assert(udt); - AccessType access = lldb::eAccessPublic; - PDB_UdtType udt_kind = udt->getUdtKind(); - auto tag_type_kind = TranslateUdtKind(udt_kind); - if (tag_type_kind == -1) + + // Note that, unnamed UDT being typedef-ed is generated as a UDT symbol + // other than a Typedef symbol in PDB. For example, + // typedef union { short Row; short Col; } Union; + // is generated as a named UDT in PDB: + // union Union { short Row; short Col; } + // Such symbols will be handled here. + + // Some UDT with trival ctor has zero length. Just ignore. + if (udt->getLength() == 0) + return nullptr; + + // Ignore unnamed-tag UDTs. + if (udt->getName().empty()) return nullptr; - if (udt_kind == PDB_UdtType::Class) - access = lldb::eAccessPrivate; + auto access = GetAccessibilityForUdt(*udt); + + auto tag_type_kind = TranslateUdtKind(udt->getUdtKind()); + + ClangASTMetadata metadata; + metadata.SetUserID(type.getSymIndexId()); + metadata.SetIsDynamicCXXType(false); CompilerType clang_type = m_ast.CreateRecordType( tu_decl_ctx, access, udt->getName().c_str(), tag_type_kind, - lldb::eLanguageTypeC_plus_plus, nullptr); + lldb::eLanguageTypeC_plus_plus, &metadata); + assert(clang_type.IsValid()); + + if (udt->isConstType()) + clang_type = clang_type.AddConstModifier(); + + if (udt->isVolatileType()) + clang_type = clang_type.AddVolatileModifier(); + + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + assert(record_decl); + auto inheritance_attr = clang::MSInheritanceAttr::CreateImplicit( + *m_ast.getASTContext(), GetMSInheritance(*udt)); + record_decl->addAttr(inheritance_attr); + + ClangASTContext::StartTagDeclarationDefinition(clang_type); - m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); + Type::ResolveStateTag type_resolve_state_tag; + auto children = udt->findAllChildren(); + if (!children || children->getChildCount() == 0) { + // PDB does not have symbol of forwarder. We assume we get an udt w/o any + // fields. Just complete it at this point. + ClangASTContext::CompleteTagDeclarationDefinition(clang_type); + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false); + + type_resolve_state_tag = Type::eResolveStateFull; + } else { + // Add the type to the forward declarations. It will help us to avoid + // an endless recursion in CompleteTypeFromUdt function. + auto clang_type_removed_fast_quals = + ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType(); + m_forward_decl_clang_type_to_uid[clang_type_removed_fast_quals] = + type.getSymIndexId(); + + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); + + type_resolve_state_tag = Type::eResolveStateForward; + } + + GetDeclarationForSymbol(type, decl); return std::make_shared( type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(udt->getName()), udt->getLength(), nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, clang_type, - lldb_private::Type::eResolveStateForward); + type_resolve_state_tag); } break; case PDB_SymType::Enum: { auto enum_type = llvm::dyn_cast(&type); @@ -441,6 +569,26 @@ if (!pointee_type) return nullptr; + if (pointer_type->isPointerToDataMember() || + pointer_type->isPointerToMemberFunction()) { + auto class_parent_uid = pointer_type->getRawSymbol().getClassParentId(); + auto class_parent_type = + m_ast.GetSymbolFile()->ResolveTypeUID(class_parent_uid); + assert(class_parent_type); + + CompilerType pointer_ast_type; + pointer_ast_type = ClangASTContext::CreateMemberPointerType( + class_parent_type->GetLayoutCompilerType(), + pointee_type->GetForwardCompilerType()); + assert(pointer_ast_type); + + return std::make_shared( + pointer_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(), + pointer_type->getLength(), nullptr, LLDB_INVALID_UID, + lldb_private::Type::eEncodingIsUID, decl, pointer_ast_type, + lldb_private::Type::eResolveStateForward); + } + CompilerType pointer_ast_type; pointer_ast_type = pointee_type->GetFullCompilerType(); if (pointer_type->isReference()) @@ -471,6 +619,47 @@ return nullptr; } +bool PDBASTParser::CompleteTypeFromPDB( + lldb_private::CompilerType &compiler_type) { + if (GetClangASTImporter().CanImport(compiler_type)) + return GetClangASTImporter().CompleteType(compiler_type); + + // Remove the type from the forward declarations to avoid + // an endless recursion for types like a linked list. + CompilerType compiler_type_no_qualifiers = + ClangUtil::RemoveFastQualifiers(compiler_type); + auto uid_it = m_forward_decl_clang_type_to_uid.find( + compiler_type_no_qualifiers.GetOpaqueQualType()); + if (uid_it == m_forward_decl_clang_type_to_uid.end()) + return true; + + auto symbol_file = static_cast(m_ast.GetSymbolFile()); + if (!symbol_file) + return false; + + std::unique_ptr symbol = + symbol_file->GetPDBSession().getSymbolById(uid_it->getSecond()); + if (!symbol) + return false; + + m_forward_decl_clang_type_to_uid.erase(uid_it); + + ClangASTContext::SetHasExternalStorage(compiler_type.GetOpaqueQualType(), + false); + + switch (symbol->getSymTag()) { + case PDB_SymType::UDT: { + auto udt = llvm::dyn_cast(symbol.get()); + if (!udt) + return false; + + return CompleteTypeFromUDT(*symbol_file, compiler_type, *udt); + } + default: + llvm_unreachable("not a forward clang type decl!"); + } +} + bool PDBASTParser::AddEnumValue(CompilerType enum_type, const PDBSymbolData &enum_value) const { Declaration decl; @@ -513,3 +702,187 @@ enum_type.GetOpaqueQualType(), underlying_type, decl, name.c_str(), raw_value, byte_size * 8); } + +bool PDBASTParser::CompleteTypeFromUDT( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &compiler_type, + llvm::pdb::PDBSymbolTypeUDT &udt) { + ClangASTImporter::LayoutInfo layout_info; + layout_info.bit_size = udt.getLength() * 8; + + auto nested_enums = udt.findAllChildren(); + if (nested_enums) + while (auto nested = nested_enums->getNext()) + symbol_file.ResolveTypeUID(nested->getSymIndexId()); + + auto bases_enum = udt.findAllChildren(); + if (bases_enum) + AddRecordBases(symbol_file, compiler_type, + TranslateUdtKind(udt.getUdtKind()), *bases_enum, + layout_info); + + auto members_enum = udt.findAllChildren(); + if (members_enum) + AddRecordMembers(symbol_file, compiler_type, *members_enum, layout_info); + + auto methods_enum = udt.findAllChildren(); + if (methods_enum) + AddRecordMethods(symbol_file, compiler_type, *methods_enum); + + m_ast.AddMethodOverridesForCXXRecordType(compiler_type.GetOpaqueQualType()); + ClangASTContext::BuildIndirectFields(compiler_type); + ClangASTContext::CompleteTagDeclarationDefinition(compiler_type); + + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(compiler_type.GetOpaqueQualType()); + if (!record_decl) + return static_cast(compiler_type); + + GetClangASTImporter().InsertRecordDecl(record_decl, layout_info); + + return static_cast(compiler_type); +} + +void PDBASTParser::AddRecordMembers( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + PDBDataSymbolEnumerator &members_enum, + lldb_private::ClangASTImporter::LayoutInfo &layout_info) const { + while (auto member = members_enum.getNext()) { + if (member->isCompilerGenerated()) + continue; + + auto member_name = member->getName(); + + auto member_type = symbol_file.ResolveTypeUID(member->getTypeId()); + if (!member_type) + continue; + + auto member_comp_type = member_type->GetLayoutCompilerType(); + if (!member_comp_type.GetCompleteType()) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + ":: Class '%s' has a member '%s' of type '%s' " + "which does not have a complete definition.", + record_type.GetTypeName().GetCString(), member_name.c_str(), + member_comp_type.GetTypeName().GetCString()); + if (ClangASTContext::StartTagDeclarationDefinition(member_comp_type)) + ClangASTContext::CompleteTagDeclarationDefinition(member_comp_type); + } + + auto access = TranslateMemberAccess(member->getAccess()); + + switch (member->getDataKind()) { + case PDB_DataKind::Member: { + auto location_type = member->getLocationType(); + + auto bit_size = member->getLength(); + if (location_type == PDB_LocType::ThisRel) + bit_size *= 8; + + auto decl = ClangASTContext::AddFieldToRecordType( + record_type, member_name.c_str(), member_comp_type, access, bit_size); + if (!decl) + continue; + + auto offset = member->getOffset() * 8; + if (location_type == PDB_LocType::BitField) + offset += member->getBitPosition(); + + layout_info.field_offsets.insert(std::make_pair(decl, offset)); + + break; + } + case PDB_DataKind::StaticMember: + ClangASTContext::AddVariableToRecordType(record_type, member_name.c_str(), + member_comp_type, access); + break; + default: + llvm_unreachable("unsupported PDB data kind"); + } + } +} + +void PDBASTParser::AddRecordBases( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, int record_kind, + PDBBaseClassSymbolEnumerator &bases_enum, + lldb_private::ClangASTImporter::LayoutInfo &layout_info) const { + std::vector base_classes; + while (auto base = bases_enum.getNext()) { + auto base_type = symbol_file.ResolveTypeUID(base->getTypeId()); + if (!base_type) + continue; + + auto base_comp_type = base_type->GetFullCompilerType(); + if (!base_comp_type.GetCompleteType()) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + ":: Class '%s' has a base class '%s' " + "which does not have a complete definition.", + record_type.GetTypeName().GetCString(), + base_comp_type.GetTypeName().GetCString()); + if (ClangASTContext::StartTagDeclarationDefinition(base_comp_type)) + ClangASTContext::CompleteTagDeclarationDefinition(base_comp_type); + } + + auto access = TranslateMemberAccess(base->getAccess()); + + auto is_virtual = base->isVirtualBaseClass(); + + auto base_class_spec = m_ast.CreateBaseClassSpecifier( + base_comp_type.GetOpaqueQualType(), access, is_virtual, + record_kind == clang::TTK_Class); + if (!base_class_spec) + continue; + + base_classes.push_back(base_class_spec); + + if (is_virtual) + continue; + + auto decl = m_ast.GetAsCXXRecordDecl(base_comp_type.GetOpaqueQualType()); + if (!decl) + continue; + + auto offset = clang::CharUnits::fromQuantity(base->getOffset()); + layout_info.base_offsets.insert(std::make_pair(decl, offset)); + } + if (!base_classes.empty()) { + m_ast.SetBaseClassesForClassType(record_type.GetOpaqueQualType(), + &base_classes.front(), + base_classes.size()); + ClangASTContext::DeleteBaseClassSpecifiers(&base_classes.front(), + base_classes.size()); + } +} + +void PDBASTParser::AddRecordMethods( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + PDBFuncSymbolEnumerator &methods_enum) const { + while (auto method = methods_enum.getNext()) { + auto method_type = symbol_file.ResolveTypeUID(method->getSymIndexId()); + // MSVC specific __vecDelDtor. + if (!method_type) + break; + + auto method_comp_type = method_type->GetFullCompilerType(); + if (!method_comp_type.GetCompleteType()) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + ":: Class '%s' has a method '%s' whose type cannot be completed.", + record_type.GetTypeName().GetCString(), + method_comp_type.GetTypeName().GetCString()); + if (ClangASTContext::StartTagDeclarationDefinition(method_comp_type)) + ClangASTContext::CompleteTagDeclarationDefinition(method_comp_type); + } + + // TODO: get mangled name for the method. + m_ast.AddMethodToCXXRecordType( + record_type.GetOpaqueQualType(), method->getName().c_str(), + /*mangled_name*/ nullptr, method_comp_type, + TranslateMemberAccess(method->getAccess()), method->isVirtual(), + method->isStatic(), method->hasInlineAttribute(), + /*is_explicit*/ false, // FIXME: Need this field in CodeView. + /*is_attr_used*/ false, + /*is_artificial*/ method->isCompilerGenerated()); + } +} Index: source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -45,7 +45,7 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" -#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" // For IsCPPMangledName #include "Plugins/SymbolFile/PDB/PDBASTParser.h" #include "Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h" @@ -459,10 +459,13 @@ // This should cause the type to get cached and stored in the `m_types` // lookup. - if (!ResolveTypeUID(symbol->getSymIndexId())) - continue; - - ++num_added; + if (auto type = ResolveTypeUID(symbol->getSymIndexId())) { + // Resolve the type completely to avoid a completion + // (and so a list change, which causes an iterators invalidation) + // during a TypeList dumping + type->GetFullCompilerType(); + ++num_added; + } } } }; @@ -568,8 +571,20 @@ } bool SymbolFilePDB::CompleteType(lldb_private::CompilerType &compiler_type) { - // TODO: Implement this - return false; + std::lock_guard guard( + GetObjectFile()->GetModule()->GetMutex()); + + ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null( + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus)); + if (!clang_ast_ctx) + return false; + + PDBASTParser *pdb = + llvm::dyn_cast(clang_ast_ctx->GetPDBParser()); + if (!pdb) + return false; + + return pdb->CompleteTypeFromPDB(compiler_type); } lldb_private::CompilerDecl SymbolFilePDB::GetDeclForUID(lldb::user_id_t uid) { Index: source/Symbol/ClangASTContext.cpp =================================================================== --- source/Symbol/ClangASTContext.cpp +++ source/Symbol/ClangASTContext.cpp @@ -124,6 +124,84 @@ // Use Clang for D until there is a proper language plugin for it language == eLanguageTypeD; } + +// Checks whether m1 is an overload of m2 (as opposed to an override). This is +// called by addOverridesForMethod to distinguish overrides (which share a +// vtable entry) from overloads (which require distinct entries). +bool isOverload(clang::CXXMethodDecl *m1, clang::CXXMethodDecl *m2) { + // FIXME: This should detect covariant return types, but currently doesn't. + lldbassert(&m1->getASTContext() == &m2->getASTContext() && + "Methods should have the same AST context"); + clang::ASTContext &context = m1->getASTContext(); + + const auto *m1Type = llvm::cast( + context.getCanonicalType(m1->getType())); + + const auto *m2Type = llvm::cast( + context.getCanonicalType(m2->getType())); + + auto compareArgTypes = [&context](const clang::QualType &m1p, + const clang::QualType &m2p) { + return context.hasSameType(m1p.getUnqualifiedType(), + m2p.getUnqualifiedType()); + }; + + // FIXME: In C++14 and later, we can just pass m2Type->param_type_end() + // as a fourth parameter to std::equal(). + return (m1->getNumParams() != m2->getNumParams()) || + !std::equal(m1Type->param_type_begin(), m1Type->param_type_end(), + m2Type->param_type_begin(), compareArgTypes); +} + +// If decl is a virtual method, walk the base classes looking for methods that +// decl overrides. This table of overridden methods is used by IRGen to +// determine the vtable layout for decl's parent class. +void addOverridesForMethod(clang::CXXMethodDecl *decl) { + if (!decl->isVirtual()) + return; + + clang::CXXBasePaths paths; + + auto find_overridden_methods = + [decl](const clang::CXXBaseSpecifier *specifier, + clang::CXXBasePath &path) { + if (auto *base_record = llvm::dyn_cast( + specifier->getType()->getAs()->getDecl())) { + + clang::DeclarationName name = decl->getDeclName(); + + // If this is a destructor, check whether the base class destructor is + // virtual. + if (name.getNameKind() == clang::DeclarationName::CXXDestructorName) + if (auto *baseDtorDecl = base_record->getDestructor()) { + if (baseDtorDecl->isVirtual()) { + path.Decls = baseDtorDecl; + return true; + } else + return false; + } + + // Otherwise, search for name in the base class. + for (path.Decls = base_record->lookup(name); !path.Decls.empty(); + path.Decls = path.Decls.slice(1)) { + if (auto *method_decl = + llvm::dyn_cast(path.Decls.front())) + if (method_decl->isVirtual() && !isOverload(decl, method_decl)) { + path.Decls = method_decl; + return true; + } + } + } + + return false; + }; + + if (decl->getParent()->lookupInBases(find_overridden_methods, paths)) { + for (auto *overridden_decl : paths.found_decls()) + decl->addOverriddenMethod( + llvm::cast(overridden_decl)); + } +} } typedef lldb_private::ThreadSafeDenseMap @@ -6373,7 +6451,7 @@ language_flags = 0; const bool idx_is_valid = idx < GetNumChildren(type, omit_empty_base_classes); - uint32_t bit_offset; + int32_t bit_offset; switch (parent_type_class) { case clang::Type::Builtin: if (idx_is_valid) { @@ -6465,8 +6543,8 @@ cxx_record_decl, base_class_decl); const lldb::addr_t base_offset_addr = vbtable_ptr + vbtable_index * 4; - const uint32_t base_offset = - process->ReadUnsignedIntegerFromMemory( + const int32_t base_offset = + process->ReadSignedIntegerFromMemory( base_offset_addr, 4, UINT32_MAX, err); if (base_offset != UINT32_MAX) { handled = true; @@ -6490,8 +6568,8 @@ vtable_ptr + base_offset_offset.getQuantity(); const uint32_t base_offset_size = process->GetAddressByteSize(); - const uint64_t base_offset = - process->ReadUnsignedIntegerFromMemory( + const int64_t base_offset = + process->ReadSignedIntegerFromMemory( base_offset_addr, base_offset_size, UINT32_MAX, err); if (base_offset < UINT32_MAX) { @@ -8129,6 +8207,13 @@ return cxx_method_decl; } +void ClangASTContext::AddMethodOverridesForCXXRecordType( + lldb::opaque_compiler_type_t type) { + if (auto *record = GetAsCXXRecordDecl(type)) + for (auto *method : record->methods()) + addOverridesForMethod(method); +} + #pragma mark C++ Base Classes clang::CXXBaseSpecifier * @@ -9668,11 +9753,16 @@ llvm::DenseMap &vbase_offsets) { ClangASTContext *ast = (ClangASTContext *)baton; - DWARFASTParserClang *dwarf_ast_parser = - (DWARFASTParserClang *)ast->GetDWARFParser(); - return dwarf_ast_parser->GetClangASTImporter().LayoutRecordType( - record_decl, bit_size, alignment, field_offsets, base_offsets, - vbase_offsets); + lldb_private::ClangASTImporter *importer = nullptr; + if (ast->m_dwarf_ast_parser_ap) + importer = &ast->m_dwarf_ast_parser_ap->GetClangASTImporter(); + if (!importer && ast->m_pdb_ast_parser_ap) + importer = &ast->m_pdb_ast_parser_ap->GetClangASTImporter(); + if (!importer) + return false; + + return importer->LayoutRecordType(record_decl, bit_size, alignment, + field_offsets, base_offsets, vbase_offsets); } //----------------------------------------------------------------------