[NativePDB] Add basic support for tag types to the native pdb plugin.
This adds support to LLDB for named types (class, struct, union, and
enum). This is true cross platform support, and hits the PDB file
directly without a dependency on Windows. Tests are added which
compile a program with certain interesting types and then use
load the target in LLDB and use "type lookup -- <TypeName>" to
dump the layout of the type in LLDB without a running process.
Currently only fields are parsed -- we do not parse methods. Also
we don't deal with bitfields or virtual bases correctly. Those
will make good followups.
Differential Revision: https://reviews.llvm.org/D53511
llvm-svn: 345047
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 748c0de..9aa5ecb 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -9,8 +9,16 @@
#include "SymbolFileNativePDB.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/ObjectFile.h"
@@ -18,15 +26,19 @@
#include "lldb/Symbol/SymbolVendor.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
#include "llvm/DebugInfo/CodeView/RecordName.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Allocator.h"
@@ -36,10 +48,11 @@
#include "PdbSymUid.h"
#include "PdbUtil.h"
+#include "UdtRecordCompleter.h"
using namespace lldb;
using namespace lldb_private;
-using namespace lldb_private::npdb;
+using namespace npdb;
using namespace llvm::codeview;
using namespace llvm::pdb;
@@ -139,6 +152,265 @@
return false;
}
+static clang::MSInheritanceAttr::Spelling
+GetMSInheritance(LazyRandomTypeCollection &tpi, const ClassRecord &record) {
+ if (record.DerivationList == TypeIndex::None())
+ return clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance;
+
+ CVType bases = tpi.getType(record.DerivationList);
+ ArgListRecord base_list;
+ cantFail(TypeDeserializer::deserializeAs<ArgListRecord>(bases, base_list));
+ if (base_list.ArgIndices.empty())
+ return clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance;
+
+ int base_count = 0;
+ for (TypeIndex ti : base_list.ArgIndices) {
+ CVType base = tpi.getType(ti);
+ if (base.kind() == LF_VBCLASS || base.kind() == LF_IVBCLASS)
+ return clang::MSInheritanceAttr::Spelling::Keyword_virtual_inheritance;
+ ++base_count;
+ }
+
+ if (base_count > 1)
+ return clang::MSInheritanceAttr::Keyword_multiple_inheritance;
+ return clang::MSInheritanceAttr::Keyword_single_inheritance;
+}
+
+static lldb::BasicType GetCompilerTypeForSimpleKind(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Boolean8:
+ return lldb::eBasicTypeBool;
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ return lldb::eBasicTypeUnsignedChar;
+ case SimpleTypeKind::NarrowCharacter:
+ return lldb::eBasicTypeChar;
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return lldb::eBasicTypeSignedChar;
+ case SimpleTypeKind::Character16:
+ return lldb::eBasicTypeChar16;
+ case SimpleTypeKind::Character32:
+ return lldb::eBasicTypeChar32;
+ case SimpleTypeKind::Complex80:
+ return lldb::eBasicTypeLongDoubleComplex;
+ case SimpleTypeKind::Complex64:
+ return lldb::eBasicTypeDoubleComplex;
+ case SimpleTypeKind::Complex32:
+ return lldb::eBasicTypeFloatComplex;
+ case SimpleTypeKind::Float128:
+ case SimpleTypeKind::Float80:
+ return lldb::eBasicTypeLongDouble;
+ case SimpleTypeKind::Float64:
+ return lldb::eBasicTypeDouble;
+ case SimpleTypeKind::Float32:
+ return lldb::eBasicTypeFloat;
+ case SimpleTypeKind::Float16:
+ return lldb::eBasicTypeHalf;
+ case SimpleTypeKind::Int128:
+ return lldb::eBasicTypeInt128;
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return lldb::eBasicTypeLongLong;
+ case SimpleTypeKind::Int32:
+ return lldb::eBasicTypeInt;
+ case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
+ return lldb::eBasicTypeShort;
+ case SimpleTypeKind::UInt128:
+ return lldb::eBasicTypeUnsignedInt128;
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ return lldb::eBasicTypeUnsignedLongLong;
+ case SimpleTypeKind::HResult:
+ case SimpleTypeKind::UInt32:
+ return lldb::eBasicTypeUnsignedInt;
+ case SimpleTypeKind::UInt16:
+ case SimpleTypeKind::UInt16Short:
+ return lldb::eBasicTypeUnsignedShort;
+ case SimpleTypeKind::Int32Long:
+ return lldb::eBasicTypeLong;
+ case SimpleTypeKind::UInt32Long:
+ return lldb::eBasicTypeUnsignedLong;
+ case SimpleTypeKind::Void:
+ return lldb::eBasicTypeVoid;
+ case SimpleTypeKind::WideCharacter:
+ return lldb::eBasicTypeWChar;
+ default:
+ return lldb::eBasicTypeInvalid;
+ }
+}
+
+static size_t GetTypeSizeForSimpleKind(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Int128:
+ case SimpleTypeKind::UInt128:
+ case SimpleTypeKind::Float128:
+ return 16;
+ case SimpleTypeKind::Complex80:
+ case SimpleTypeKind::Float80:
+ return 10;
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Complex64:
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ case SimpleTypeKind::Float64:
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return 8;
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Character32:
+ case SimpleTypeKind::Complex32:
+ case SimpleTypeKind::Float32:
+ case SimpleTypeKind::Int32:
+ case SimpleTypeKind::Int32Long:
+ case SimpleTypeKind::UInt32Long:
+ case SimpleTypeKind::HResult:
+ case SimpleTypeKind::UInt32:
+ return 4;
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Character16:
+ case SimpleTypeKind::Float16:
+ case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
+ case SimpleTypeKind::UInt16:
+ case SimpleTypeKind::UInt16Short:
+ case SimpleTypeKind::WideCharacter:
+ return 2;
+ case SimpleTypeKind::Boolean8:
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ case SimpleTypeKind::NarrowCharacter:
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return 1;
+ case SimpleTypeKind::Void:
+ default:
+ return 0;
+ }
+}
+
+static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Boolean8:
+ return "bool";
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ return "unsigned char";
+ case SimpleTypeKind::NarrowCharacter:
+ return "char";
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return "signed chr";
+ case SimpleTypeKind::Character16:
+ return "char16_t";
+ case SimpleTypeKind::Character32:
+ return "char32_t";
+ case SimpleTypeKind::Complex80:
+ case SimpleTypeKind::Complex64:
+ case SimpleTypeKind::Complex32:
+ return "complex";
+ case SimpleTypeKind::Float128:
+ case SimpleTypeKind::Float80:
+ return "long double";
+ case SimpleTypeKind::Float64:
+ return "double";
+ case SimpleTypeKind::Float32:
+ return "float";
+ case SimpleTypeKind::Float16:
+ return "single";
+ case SimpleTypeKind::Int128:
+ return "__int128";
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return "__int64";
+ case SimpleTypeKind::Int32:
+ return "int";
+ case SimpleTypeKind::Int16:
+ return "short";
+ case SimpleTypeKind::UInt128:
+ return "unsigned __int128";
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ return "unsigned __int64";
+ case SimpleTypeKind::HResult:
+ return "HRESULT";
+ case SimpleTypeKind::UInt32:
+ return "unsigned";
+ case SimpleTypeKind::UInt16:
+ case SimpleTypeKind::UInt16Short:
+ return "unsigned short";
+ case SimpleTypeKind::Int32Long:
+ return "long";
+ case SimpleTypeKind::UInt32Long:
+ return "unsigned long";
+ case SimpleTypeKind::Void:
+ return "void";
+ case SimpleTypeKind::WideCharacter:
+ return "wchar_t";
+ default:
+ return "";
+ }
+}
+
+static bool IsClassRecord(TypeLeafKind kind) {
+ switch (kind) {
+ case LF_STRUCTURE:
+ case LF_CLASS:
+ case LF_INTERFACE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static PDB_SymType GetPdbSymType(TpiStream &tpi, TypeIndex ti) {
+ if (ti.isSimple()) {
+ if (ti.getSimpleMode() == SimpleTypeMode::Direct)
+ return PDB_SymType::BuiltinType;
+ return PDB_SymType::PointerType;
+ }
+
+ CVType cvt = tpi.getType(ti);
+ TypeLeafKind kind = cvt.kind();
+ if (kind != LF_MODIFIER)
+ return CVTypeToPDBType(kind);
+
+ // If this is an LF_MODIFIER, look through it to get the kind that it
+ // modifies. Note that it's not possible to have an LF_MODIFIER that
+ // modifies another LF_MODIFIER, although this would handle that anyway.
+ ModifierRecord mr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(cvt, mr));
+ return GetPdbSymType(tpi, mr.ModifiedType);
+}
+
+static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) {
+ switch (cr.Kind) {
+ case TypeRecordKind::Class:
+ return clang::TTK_Class;
+ case TypeRecordKind::Struct:
+ return clang::TTK_Struct;
+ case TypeRecordKind::Union:
+ return clang::TTK_Union;
+ case TypeRecordKind::Interface:
+ return clang::TTK_Interface;
+ case TypeRecordKind::Enum:
+ return clang::TTK_Enum;
+ default:
+ lldbassert(false && "Invalid tag record kind!");
+ return clang::TTK_Struct;
+ }
+}
+
void SymbolFileNativePDB::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance,
@@ -216,6 +488,11 @@
m_obj_load_address = m_obj_file->GetFileOffset();
m_index->SetLoadAddress(m_obj_load_address);
m_index->ParseSectionContribs();
+
+ TypeSystem *ts = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus);
+ m_clang = llvm::dyn_cast_or_null<ClangASTContext>(ts);
+ m_importer = llvm::make_unique<ClangASTImporter>();
+ lldbassert(m_clang);
}
uint32_t SymbolFileNativePDB::GetNumCompileUnits() {
@@ -295,6 +572,329 @@
return cu_sp;
}
+lldb::TypeSP SymbolFileNativePDB::CreateModifierType(PdbSymUid type_uid,
+ const ModifierRecord &mr) {
+ TpiStream &stream = m_index->tpi();
+
+ TypeSP t = GetOrCreateType(mr.ModifiedType);
+ CompilerType ct = t->GetForwardCompilerType();
+ if ((mr.Modifiers & ModifierOptions::Const) != ModifierOptions::None)
+ ct = ct.AddConstModifier();
+ if ((mr.Modifiers & ModifierOptions::Volatile) != ModifierOptions::None)
+ ct = ct.AddVolatileModifier();
+ std::string name;
+ if (mr.ModifiedType.isSimple())
+ name = GetSimpleTypeName(mr.ModifiedType.getSimpleKind());
+ else
+ name = computeTypeName(stream.typeCollection(), mr.ModifiedType);
+ Declaration decl;
+ return std::make_shared<Type>(type_uid.toOpaqueId(), m_clang->GetSymbolFile(),
+ ConstString(name), t->GetByteSize(), nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
+ ct, Type::eResolveStateFull);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreatePointerType(
+ PdbSymUid type_uid, const llvm::codeview::PointerRecord &pr) {
+ TypeSP pointee = GetOrCreateType(pr.ReferentType);
+ CompilerType pointee_ct = pointee->GetForwardCompilerType();
+ lldbassert(pointee_ct);
+ Declaration decl;
+
+ if (pr.isPointerToMember()) {
+ MemberPointerInfo mpi = pr.getMemberInfo();
+ TypeSP class_type = GetOrCreateType(mpi.ContainingType);
+
+ CompilerType ct = ClangASTContext::CreateMemberPointerType(
+ class_type->GetLayoutCompilerType(), pointee_ct);
+
+ return std::make_shared<Type>(
+ type_uid.toOpaqueId(), m_clang->GetSymbolFile(), ConstString(),
+ pr.getSize(), nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct,
+ Type::eResolveStateFull);
+ }
+
+ CompilerType pointer_ct = pointee_ct;
+ if (pr.getMode() == PointerMode::LValueReference)
+ pointer_ct = pointer_ct.GetLValueReferenceType();
+ else if (pr.getMode() == PointerMode::RValueReference)
+ pointer_ct = pointer_ct.GetRValueReferenceType();
+ else
+ pointer_ct = pointer_ct.GetPointerType();
+
+ if ((pr.getOptions() & PointerOptions::Const) != PointerOptions::None)
+ pointer_ct = pointer_ct.AddConstModifier();
+
+ if ((pr.getOptions() & PointerOptions::Volatile) != PointerOptions::None)
+ pointer_ct = pointer_ct.AddVolatileModifier();
+
+ if ((pr.getOptions() & PointerOptions::Restrict) != PointerOptions::None)
+ pointer_ct = pointer_ct.AddRestrictModifier();
+
+ return std::make_shared<Type>(type_uid.toOpaqueId(), m_clang->GetSymbolFile(),
+ ConstString(), pr.getSize(), nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
+ pointer_ct, Type::eResolveStateFull);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti) {
+ if (ti.getSimpleMode() != SimpleTypeMode::Direct) {
+ PdbSymUid uid =
+ PdbSymUid::makeTypeSymId(PDB_SymType::PointerType, ti, false);
+ TypeSP direct_sp = GetOrCreateType(ti.makeDirect());
+ CompilerType ct = direct_sp->GetFullCompilerType();
+ ct = ct.GetPointerType();
+ uint32_t pointer_size = 4;
+ switch (ti.getSimpleMode()) {
+ case SimpleTypeMode::FarPointer32:
+ case SimpleTypeMode::NearPointer32:
+ pointer_size = 4;
+ break;
+ case SimpleTypeMode::NearPointer64:
+ pointer_size = 8;
+ break;
+ default:
+ // 128-bit and 16-bit pointers unsupported.
+ return nullptr;
+ }
+ Declaration decl;
+ return std::make_shared<Type>(uid.toOpaqueId(), m_clang->GetSymbolFile(),
+ ConstString(), pointer_size, nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
+ ct, Type::eResolveStateFull);
+ }
+
+ PdbSymUid uid = PdbSymUid::makeTypeSymId(PDB_SymType::BuiltinType, ti, false);
+ if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated)
+ return nullptr;
+
+ lldb::BasicType bt = GetCompilerTypeForSimpleKind(ti.getSimpleKind());
+ lldbassert(bt != lldb::eBasicTypeInvalid);
+ CompilerType ct = m_clang->GetBasicType(bt);
+ size_t size = GetTypeSizeForSimpleKind(ti.getSimpleKind());
+
+ llvm::StringRef type_name = GetSimpleTypeName(ti.getSimpleKind());
+
+ Declaration decl;
+ return std::make_shared<Type>(uid.toOpaqueId(), m_clang->GetSymbolFile(),
+ ConstString(type_name), size, nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
+ ct, Type::eResolveStateFull);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateClassStructUnion(
+ PdbSymUid type_uid, llvm::StringRef name, size_t size,
+ clang::TagTypeKind ttk, clang::MSInheritanceAttr::Spelling inheritance) {
+
+ // Some UDT with trival ctor has zero length. Just ignore.
+ if (size == 0)
+ return nullptr;
+
+ // Ignore unnamed-tag UDTs.
+ name = DropNameScope(name);
+ if (name.empty())
+ return nullptr;
+
+ clang::DeclContext *decl_context = m_clang->GetTranslationUnitDecl();
+
+ lldb::AccessType access =
+ (ttk == clang::TTK_Class) ? lldb::eAccessPrivate : lldb::eAccessPublic;
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(type_uid.toOpaqueId());
+ metadata.SetIsDynamicCXXType(false);
+
+ CompilerType ct =
+ m_clang->CreateRecordType(decl_context, access, name.str().c_str(), ttk,
+ lldb::eLanguageTypeC_plus_plus, &metadata);
+ lldbassert(ct.IsValid());
+
+ clang::CXXRecordDecl *record_decl =
+ m_clang->GetAsCXXRecordDecl(ct.GetOpaqueQualType());
+ lldbassert(record_decl);
+
+ clang::MSInheritanceAttr *attr = clang::MSInheritanceAttr::CreateImplicit(
+ *m_clang->getASTContext(), inheritance);
+ record_decl->addAttr(attr);
+
+ ClangASTContext::StartTagDeclarationDefinition(ct);
+
+ // Even if it's possible, don't complete it at this point. Just mark it
+ // forward resolved, and if/when LLDB needs the full definition, it can
+ // ask us.
+ ClangASTContext::SetHasExternalStorage(ct.GetOpaqueQualType(), true);
+
+ // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE.
+ Declaration decl;
+ return std::make_shared<Type>(type_uid.toOpaqueId(), m_clang->GetSymbolFile(),
+ ConstString(name), size, nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
+ ct, Type::eResolveStateForward);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbSymUid type_uid,
+ const ClassRecord &cr) {
+ clang::TagTypeKind ttk = TranslateUdtKind(cr);
+
+ clang::MSInheritanceAttr::Spelling inheritance =
+ GetMSInheritance(m_index->tpi().typeCollection(), cr);
+ return CreateClassStructUnion(type_uid, cr.getName(), cr.getSize(), ttk,
+ inheritance);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbSymUid type_uid,
+ const UnionRecord &ur) {
+ return CreateClassStructUnion(
+ type_uid, ur.getName(), ur.getSize(), clang::TTK_Union,
+ clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbSymUid type_uid,
+ const EnumRecord &er) {
+ llvm::StringRef name = DropNameScope(er.getName());
+
+ clang::DeclContext *decl_context = m_clang->GetTranslationUnitDecl();
+
+ Declaration decl;
+ TypeSP underlying_type = GetOrCreateType(er.UnderlyingType);
+ CompilerType enum_ct = m_clang->CreateEnumerationType(
+ name.str().c_str(), decl_context, decl,
+ underlying_type->GetFullCompilerType(), er.isScoped());
+
+ ClangASTContext::StartTagDeclarationDefinition(enum_ct);
+
+ // We're just going to forward resolve this for now. We'll complete
+ // it only if the user requests.
+ return std::make_shared<lldb_private::Type>(
+ type_uid.toOpaqueId(), m_clang->GetSymbolFile(), ConstString(name),
+ underlying_type->GetByteSize(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, enum_ct,
+ lldb_private::Type::eResolveStateForward);
+}
+
+TypeSP SymbolFileNativePDB::CreateType(PdbSymUid type_uid) {
+ const PdbTypeSymId &tsid = type_uid.asTypeSym();
+ TypeIndex index(tsid.index);
+
+ if (index.getIndex() < TypeIndex::FirstNonSimpleIndex)
+ return CreateSimpleType(index);
+
+ TpiStream &stream = tsid.is_ipi ? m_index->ipi() : m_index->tpi();
+ CVType cvt = stream.getType(index);
+
+ if (cvt.kind() == LF_MODIFIER) {
+ ModifierRecord modifier;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier));
+ return CreateModifierType(type_uid, modifier);
+ }
+
+ if (cvt.kind() == LF_POINTER) {
+ PointerRecord pointer;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer));
+ return CreatePointerType(type_uid, pointer);
+ }
+
+ if (IsClassRecord(cvt.kind())) {
+ ClassRecord cr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
+ return CreateTagType(type_uid, cr);
+ }
+
+ if (cvt.kind() == LF_ENUM) {
+ EnumRecord er;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return CreateTagType(type_uid, er);
+ }
+
+ if (cvt.kind() == LF_UNION) {
+ UnionRecord ur;
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
+ return CreateTagType(type_uid, ur);
+ }
+
+ return nullptr;
+}
+
+TypeSP SymbolFileNativePDB::CreateAndCacheType(PdbSymUid type_uid) {
+ // If they search for a UDT which is a forward ref, try and resolve the full
+ // decl and just map the forward ref uid to the full decl record.
+ llvm::Optional<PdbSymUid> full_decl_uid;
+ if (type_uid.tag() == PDB_SymType::UDT ||
+ type_uid.tag() == PDB_SymType::Enum) {
+ const PdbTypeSymId &type_id = type_uid.asTypeSym();
+ TypeIndex ti(type_id.index);
+ lldbassert(!ti.isSimple());
+ CVType cvt = m_index->tpi().getType(ti);
+
+ if (IsForwardRefUdt(cvt)) {
+ auto expected_full_ti = m_index->tpi().findFullDeclForForwardRef(ti);
+ if (!expected_full_ti)
+ llvm::consumeError(expected_full_ti.takeError());
+ else {
+ full_decl_uid = PdbSymUid::makeTypeSymId(
+ type_uid.tag(), *expected_full_ti, type_id.is_ipi);
+
+ // It's possible that a lookup would occur for the full decl causing it
+ // to be cached, then a second lookup would occur for the forward decl.
+ // We don't want to create a second full decl, so make sure the full
+ // decl hasn't already been cached.
+ auto full_iter = m_types.find(full_decl_uid->toOpaqueId());
+ if (full_iter != m_types.end()) {
+ TypeSP result = full_iter->second;
+ // Map the forward decl to the TypeSP for the full decl so we can take
+ // the fast path next time.
+ m_types[type_uid.toOpaqueId()] = result;
+ return result;
+ }
+ }
+ }
+ }
+
+ PdbSymUid best_uid = full_decl_uid ? *full_decl_uid : type_uid;
+ TypeSP result = CreateType(best_uid);
+ m_types[best_uid.toOpaqueId()] = result;
+ // If we had both a forward decl and a full decl, make both point to the new
+ // type.
+ if (full_decl_uid)
+ m_types[type_uid.toOpaqueId()] = result;
+
+ const PdbTypeSymId &type_id = best_uid.asTypeSym();
+ if (best_uid.tag() == PDB_SymType::UDT ||
+ best_uid.tag() == PDB_SymType::Enum) {
+ clang::TagDecl *record_decl =
+ m_clang->GetAsTagDecl(result->GetForwardCompilerType());
+ lldbassert(record_decl);
+
+ TypeIndex ti(type_id.index);
+ CVType cvt = m_index->tpi().getType(ti);
+ m_uid_to_decl[best_uid.toOpaqueId()] = record_decl;
+ m_decl_to_status[record_decl] =
+ DeclStatus(best_uid.toOpaqueId(), Type::eResolveStateForward);
+ }
+ return result;
+}
+
+TypeSP SymbolFileNativePDB::GetOrCreateType(PdbSymUid type_uid) {
+ lldbassert(PdbSymUid::isTypeSym(type_uid.tag()));
+ // We can't use try_emplace / overwrite here because the process of creating
+ // a type could create nested types, which could invalidate iterators. So
+ // we have to do a 2-phase lookup / insert.
+ auto iter = m_types.find(type_uid.toOpaqueId());
+ if (iter != m_types.end())
+ return iter->second;
+
+ return CreateAndCacheType(type_uid);
+}
+
+lldb::TypeSP
+SymbolFileNativePDB::GetOrCreateType(llvm::codeview::TypeIndex ti) {
+ PDB_SymType pdbst = GetPdbSymType(m_index->tpi(), ti);
+ PdbSymUid tuid = PdbSymUid::makeTypeSymId(pdbst, ti, false);
+ return GetOrCreateType(tuid);
+}
+
FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbSymUid func_uid,
const SymbolContext &sc) {
lldbassert(func_uid.tag() == PDB_SymType::Function);
@@ -595,7 +1195,18 @@
const CompilerDeclContext *parent_decl_ctx, bool append,
uint32_t max_matches, llvm::DenseSet<SymbolFile *> &searched_symbol_files,
TypeMap &types) {
- return 0;
+ if (!append)
+ types.Clear();
+ if (!name)
+ return 0;
+
+ searched_symbol_files.clear();
+ searched_symbol_files.insert(this);
+
+ // There is an assumption 'name' is not a regex
+ size_t match_count = FindTypesByName(name.GetStringRef(), max_matches, types);
+
+ return match_count;
}
size_t
@@ -604,13 +1215,96 @@
return 0;
}
+size_t SymbolFileNativePDB::FindTypesByName(llvm::StringRef name,
+ uint32_t max_matches,
+ TypeMap &types) {
+
+ size_t match_count = 0;
+ std::vector<TypeIndex> matches = m_index->tpi().findRecordsByName(name);
+ if (max_matches > 0 && max_matches < matches.size())
+ matches.resize(max_matches);
+
+ for (TypeIndex ti : matches) {
+ TypeSP type = GetOrCreateType(ti);
+ if (!type)
+ continue;
+
+ types.Insert(type);
+ ++match_count;
+ }
+ return match_count;
+}
+
size_t SymbolFileNativePDB::ParseTypes(const SymbolContext &sc) { return 0; }
Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) {
- return nullptr;
+ auto iter = m_types.find(type_uid);
+ // lldb should not be passing us non-sensical type uids. the only way it
+ // could have a type uid in the first place is if we handed it out, in which
+ // case we should know about the type. So this is not a get-or-create type
+ // operation, it is strictly a get, and the type is guaranteed to exist.
+ //
+ // However, since the implementation is not yet complete, we don't currently
+ // support all possible use cases. For example, we currently create all
+ // functions with indices of 0 for the signature type simply because this is
+ // not yet implemented. At the time the function object is created we should
+ // be creating an lldb::TypeSP for this, adding it to the m_types, and
+ // returning a valid Type object for it and putting it in this map. Once all
+ // cases like this are handled, we can promote this to an assert.
+ if (iter == m_types.end())
+ return nullptr;
+ return &*iter->second;
}
bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) {
+ // If this is not in our map, it's an error.
+ clang::TagDecl *tag_decl = m_clang->GetAsTagDecl(compiler_type);
+ lldbassert(tag_decl);
+ auto status_iter = m_decl_to_status.find(tag_decl);
+ lldbassert(status_iter != m_decl_to_status.end());
+
+ // If it's already complete, just return.
+ DeclStatus &status = status_iter->second;
+ if (status.status == Type::eResolveStateFull)
+ return true;
+
+ PdbSymUid uid = PdbSymUid::fromOpaqueId(status.uid);
+ lldbassert(uid.tag() == PDB_SymType::UDT || uid.tag() == PDB_SymType::Enum);
+
+ const PdbTypeSymId &type_id = uid.asTypeSym();
+
+ ClangASTContext::SetHasExternalStorage(compiler_type.GetOpaqueQualType(),
+ false);
+
+ // In CreateAndCacheType, we already go out of our way to resolve forward
+ // ref UDTs to full decls, and the uids we vend out always refer to full
+ // decls if a full decl exists in the debug info. So if we don't have a full
+ // decl here, it means one doesn't exist in the debug info, and we can't
+ // complete the type.
+ CVType cvt = m_index->tpi().getType(TypeIndex(type_id.index));
+ if (IsForwardRefUdt(cvt))
+ return false;
+
+ auto types_iter = m_types.find(uid.toOpaqueId());
+ lldbassert(types_iter != m_types.end());
+
+ TypeIndex field_list_ti = GetFieldListIndex(cvt);
+ CVType field_list_cvt = m_index->tpi().getType(field_list_ti);
+ if (field_list_cvt.kind() != LF_FIELDLIST)
+ return false;
+
+ // Visit all members of this class, then perform any finalization necessary
+ // to complete the class.
+ UdtRecordCompleter completer(uid, compiler_type, *tag_decl, *this);
+ auto error =
+ llvm::codeview::visitMemberRecordStream(field_list_cvt.data(), completer);
+ completer.complete();
+
+ status.status = Type::eResolveStateFull;
+ if (!error)
+ return true;
+
+ llvm::consumeError(std::move(error));
return false;
}