Index: llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/CodeView/TypeIndexDiscovery.h @@ -0,0 +1,33 @@ +//===- TypeIndexDiscovery.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPEINDEXDISCOVERY_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPEINDEXDISCOVERY_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { +enum class TiRefKind { TypeRef, IndexRef }; +struct TiReference { + TiRefKind Kind; + uint32_t Offset; + uint32_t Count; +}; + +void discoverTypeIndices(ArrayRef RecordData, + SmallVectorImpl &Refs); +void discoverTypeIndices(const CVType &Type, + SmallVectorImpl &Refs); +} +} + +#endif Index: llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h @@ -279,15 +279,9 @@ Attrs(calcAttrs(PK, PM, PO, Size)) {} PointerRecord(TypeIndex ReferentType, PointerKind PK, PointerMode PM, - PointerOptions PO, uint8_t Size, - const MemberPointerInfo &Member) + PointerOptions PO, uint8_t Size, const MemberPointerInfo &MPI) : TypeRecord(TypeRecordKind::Pointer), ReferentType(ReferentType), - Attrs(calcAttrs(PK, PM, PO, Size)), MemberInfo(Member) {} - - PointerRecord(TypeIndex ReferentType, uint32_t Attrs, - const MemberPointerInfo &Member) - : TypeRecord(TypeRecordKind::Pointer), ReferentType(ReferentType), - Attrs(Attrs), MemberInfo(Member) {} + Attrs(calcAttrs(PK, PM, PO, Size)), MemberInfo(MPI) {} TypeIndex getReferentType() const { return ReferentType; } Index: llvm/lib/DebugInfo/CodeView/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/CodeView/CMakeLists.txt +++ llvm/lib/DebugInfo/CodeView/CMakeLists.txt @@ -22,6 +22,7 @@ TypeDatabaseVisitor.cpp TypeDumpVisitor.cpp TypeIndex.cpp + TypeIndexDiscovery.cpp TypeRecordMapping.cpp TypeSerializer.cpp TypeStreamMerger.cpp Index: llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -0,0 +1,366 @@ +//===- TypeIndexDiscovery.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::codeview; + +static inline MethodKind getMethodKind(uint16_t Attrs) { + Attrs &= uint16_t(MethodOptions::MethodKindMask); + Attrs >>= 2; + return MethodKind(Attrs); +} + +static inline bool isIntroVirtual(uint16_t Attrs) { + MethodKind MK = getMethodKind(Attrs); + return MK == MethodKind::IntroducingVirtual || + MK == MethodKind::PureIntroducingVirtual; +} + +static inline PointerMode getPointerMode(uint32_t Attrs) { + return static_cast((Attrs >> PointerRecord::PointerModeShift) & + PointerRecord::PointerModeMask); +} + +static inline uint32_t getEncodedIntegerLength(ArrayRef Data) { + uint16_t N = support::endian::read16le(Data.data()); + if (N < LF_NUMERIC) + return 2; + + assert(N <= LF_UQUADWORD); + + constexpr uint32_t Sizes[] = { + 1, // LF_CHAR + 2, // LF_SHORT + 2, // LF_USHORT + 4, // LF_LONG + 4, // LF_ULONG + 4, // LF_REAL32 + 8, // LF_REAL64 + 10, // LF_REAL80 + 16, // LF_REAL128 + 8, // LF_QUADWORD + 8, // LF_UQUADWORD + }; + + return Sizes[N - LF_NUMERIC]; +} + +static inline uint32_t getCStringLength(ArrayRef Data) { + const char *S = reinterpret_cast(Data.data()); + return strlen(S) + 1; +} + +static void handleMethodOverloadList(const CVType &Type, + SmallVectorImpl &Refs) { + ArrayRef Data = Type.content(); + uint32_t Offset = 0; + + while (!Data.empty()) { + // Array of: + // 0: Attrs + // 2: Padding + // 4: TypeIndex + // if (isIntroVirtual()) + // 8: VFTableOffset + + // At least 8 bytes are guaranteed. 4 extra bytes come iff function is an + // intro virtual. + uint32_t Len = 8; + + uint16_t Attrs = support::endian::read16le(Data.data()); + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + + if (LLVM_UNLIKELY(isIntroVirtual(Attrs))) + Len += 4; + Offset += Len; + Data = Data.drop_front(Len); + } +} + +static uint32_t handleBaseClass(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Encoded Integer + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getEncodedIntegerLength(Data.drop_front(8)); +} + +static uint32_t handleEnumerator(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: Encoded Integer + // : Name + uint32_t Size = 4 + getEncodedIntegerLength(Data.drop_front(4)); + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleDataMember(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Encoded Integer + // : Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + uint32_t Size = 8 + getEncodedIntegerLength(Data.drop_front(8)); + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleOverloadedMethod(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleOneMethod(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Attributes + // 4: Type + // if (isIntroVirtual) + // 8: VFTableOffset + // : Name + uint32_t Size = 8; + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + + uint16_t Attrs = support::endian::read16le(Data.drop_front(2).data()); + if (LLVM_UNLIKELY(isIntroVirtual(Attrs))) + Size += 4; + + return Size + getCStringLength(Data.drop_front(Size)); +} + +static uint32_t handleNestedType(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleStaticDataMember(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + // 8: Name + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8 + getCStringLength(Data.drop_front(8)); +} + +static uint32_t handleVirtualBaseClass(ArrayRef Data, uint32_t Offset, + bool IsIndirect, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Attrs + // 4: TypeIndex + // 8: TypeIndex + // 12: Encoded Integer + // : Encoded Integer + uint32_t Size = 12; + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 2}); + Size += getEncodedIntegerLength(Data.drop_front(Size)); + Size += getEncodedIntegerLength(Data.drop_front(Size)); + return Size; +} + +static uint32_t handleVFPtr(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8; +} + +static uint32_t handleListContinuation(ArrayRef Data, uint32_t Offset, + SmallVectorImpl &Refs) { + // 0: Kind + // 2: Padding + // 4: TypeIndex + Refs.push_back({TiRefKind::TypeRef, Offset + 4, 1}); + return 8; +} + +static void handleFieldList(const CVType &Type, + SmallVectorImpl &Refs) { + ArrayRef Data = Type.content(); + + uint32_t Offset = 0; + uint32_t ThisLen = 0; + while (!Data.empty()) { + TypeLeafKind Kind = + static_cast(support::endian::read16le(Data.data())); + switch (Kind) { + case LF_BCLASS: + ThisLen = handleBaseClass(Data, Offset, Refs); + break; + case LF_ENUMERATE: + ThisLen = handleEnumerator(Data, Offset, Refs); + break; + case LF_MEMBER: + ThisLen = handleDataMember(Data, Offset, Refs); + break; + case LF_METHOD: + ThisLen = handleOverloadedMethod(Data, Offset, Refs); + break; + case LF_ONEMETHOD: + ThisLen = handleOneMethod(Data, Offset, Refs); + break; + case LF_NESTTYPE: + ThisLen = handleNestedType(Data, Offset, Refs); + break; + case LF_STMEMBER: + ThisLen = handleStaticDataMember(Data, Offset, Refs); + break; + case LF_VBCLASS: + case LF_IVBCLASS: + ThisLen = handleVirtualBaseClass(Data, Offset, Kind == LF_VBCLASS, Refs); + break; + case LF_VFUNCTAB: + ThisLen = handleVFPtr(Data, Offset, Refs); + break; + case LF_INDEX: + ThisLen = handleListContinuation(Data, Offset, Refs); + break; + default: + return; + } + Data = Data.drop_front(ThisLen); + Offset += ThisLen; + if (!Data.empty()) { + uint8_t Pad = Data.front(); + if (Pad >= LF_PAD0) { + uint32_t Skip = Pad & 0x0F; + Data = Data.drop_front(Skip); + Offset += Skip; + } + } + } +} + +static void handlePointer(const CVType &Type, + SmallVectorImpl &Refs) { + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + + uint32_t Attrs = + support::endian::read32le(Type.content().drop_front(4).data()); + PointerMode Mode = + static_cast((Attrs >> PointerRecord::PointerModeShift) & + PointerRecord::PointerModeMask); + if (Mode == PointerMode::PointerToDataMember || + Mode == PointerMode::PointerToDataMember) + Refs.push_back({TiRefKind::TypeRef, 8, 1}); +} + +void llvm::codeview::discoverTypeIndices(const CVType &Type, + SmallVectorImpl &Refs) { + uint32_t Count; + // FIXME: In the future it would be nice if we could avoid hardcoding these + // values. One idea is to define some structures representing these types + // that would allow the use of offsetof(). + switch (Type.kind()) { + case TypeLeafKind::LF_FUNC_ID: + Refs.push_back({TiRefKind::IndexRef, 0, 1}); + Refs.push_back({TiRefKind::TypeRef, 4, 1}); + break; + case TypeLeafKind::LF_MFUNC_ID: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_STRING_ID: + Refs.push_back({TiRefKind::IndexRef, 0, 1}); + break; + case TypeLeafKind::LF_SUBSTR_LIST: + Count = support::endian::read32le(Type.content().data()); + if (Count > 0) + Refs.push_back({TiRefKind::IndexRef, 4, Count}); + break; + case TypeLeafKind::LF_BUILDINFO: + Count = support::endian::read16le(Type.content().data()); + if (Count > 0) + Refs.push_back({TiRefKind::IndexRef, 2, Count}); + break; + case TypeLeafKind::LF_UDT_SRC_LINE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + Refs.push_back({TiRefKind::IndexRef, 4, 1}); + break; + case TypeLeafKind::LF_UDT_MOD_SRC_LINE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_MODIFIER: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_PROCEDURE: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + Refs.push_back({TiRefKind::TypeRef, 8, 1}); + break; + case TypeLeafKind::LF_MFUNCTION: + Refs.push_back({TiRefKind::TypeRef, 0, 3}); + Refs.push_back({TiRefKind::TypeRef, 16, 1}); + break; + case TypeLeafKind::LF_ARGLIST: + Count = support::endian::read32le(Type.content().data()); + if (Count > 0) + Refs.push_back({TiRefKind::TypeRef, 4, Count}); + break; + case TypeLeafKind::LF_ARRAY: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_CLASS: + Refs.push_back({TiRefKind::TypeRef, 4, 3}); + break; + case TypeLeafKind::LF_UNION: + Refs.push_back({TiRefKind::TypeRef, 4, 1}); + break; + case TypeLeafKind::LF_ENUM: + Refs.push_back({TiRefKind::TypeRef, 4, 2}); + break; + case TypeLeafKind::LF_BITFIELD: + Refs.push_back({TiRefKind::TypeRef, 0, 1}); + break; + case TypeLeafKind::LF_VFTABLE: + Refs.push_back({TiRefKind::TypeRef, 0, 2}); + break; + case TypeLeafKind::LF_VTSHAPE: + break; + case TypeLeafKind::LF_METHODLIST: + handleMethodOverloadList(Type, Refs); + break; + case TypeLeafKind::LF_FIELDLIST: + handleFieldList(Type, Refs); + break; + case TypeLeafKind::LF_POINTER: + handlePointer(Type, Refs); + break; + default: + break; + } +} + +void llvm::codeview::discoverTypeIndices(ArrayRef RecordData, + SmallVectorImpl &Refs) { + const RecordPrefix *P = + reinterpret_cast(RecordData.data()); + TypeLeafKind K = static_cast(uint16_t(P->RecordKind)); + CVType CVT(K, RecordData); + discoverTypeIndices(CVT, Refs); +} Index: llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" @@ -66,20 +67,8 @@ static const TypeIndex Untranslated; -/// TypeVisitorCallbacks overrides. -#define TYPE_RECORD(EnumName, EnumVal, Name) \ - Error visitKnownRecord(CVType &CVR, Name##Record &Record) override; -#define MEMBER_RECORD(EnumName, EnumVal, Name) \ - Error visitKnownMember(CVMemberRecord &CVR, Name##Record &Record) override; -#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) -#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) -#include "llvm/DebugInfo/CodeView/TypeRecords.def" - - Error visitUnknownType(CVType &Record) override; - Error visitTypeBegin(CVType &Record) override; Error visitTypeEnd(CVType &Record) override; - Error visitMemberEnd(CVMemberRecord &Record) override; Error mergeTypesAndIds(TypeTableBuilder &DestIds, TypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes); @@ -96,29 +85,25 @@ bool remapTypeIndex(TypeIndex &Idx); bool remapItemIndex(TypeIndex &Idx); - bool remapIndices(RemappedType &Record, ArrayRef TidOffs, - ArrayRef IidOffs) { + bool remapIndices(RemappedType &Record, ArrayRef Refs) { auto OriginalData = Record.OriginalRecord.content(); bool Success = true; - for (auto Off : TidOffs) { - ArrayRef Bytes = OriginalData.slice(Off, sizeof(TypeIndex)); - TypeIndex OldTI( - *reinterpret_cast(Bytes.data())); - TypeIndex NewTI = OldTI; - bool ThisSuccess = remapTypeIndex(NewTI); - if (ThisSuccess && NewTI != OldTI) - Record.Mappings.emplace_back(Off, NewTI); - Success &= ThisSuccess; - } - for (auto Off : IidOffs) { - ArrayRef Bytes = OriginalData.slice(Off, sizeof(TypeIndex)); - TypeIndex OldTI( - *reinterpret_cast(Bytes.data())); - TypeIndex NewTI = OldTI; - bool ThisSuccess = remapItemIndex(NewTI); - if (ThisSuccess && NewTI != OldTI) - Record.Mappings.emplace_back(Off, NewTI); - Success &= ThisSuccess; + for (auto &Ref : Refs) { + uint32_t Offset = Ref.Offset; + ArrayRef Bytes = + OriginalData.slice(Ref.Offset, sizeof(TypeIndex)); + ArrayRef TIs(reinterpret_cast(Bytes.data()), + Ref.Count); + for (auto TI : TIs) { + TypeIndex NewTI = TI; + bool ThisSuccess = (Ref.Kind == TiRefKind::IndexRef) + ? remapItemIndex(NewTI) + : remapTypeIndex(NewTI); + if (ThisSuccess && NewTI != TI) + Record.Mappings.emplace_back(Offset, NewTI); + Offset += sizeof(TypeIndex); + Success &= ThisSuccess; + } } return Success; } @@ -134,26 +119,6 @@ return llvm::make_error(cv_error_code::corrupt_record); } - template - Error writeKnownRecord(TypeTableBuilder &Dest, RecordType &R, - bool RemapSuccess) { - TypeIndex DestIdx = Untranslated; - if (RemapSuccess) - DestIdx = Dest.writeKnownType(R); - addMapping(DestIdx); - return Error::success(); - } - - template - Error writeKnownTypeRecord(RecordType &R, bool RemapSuccess) { - return writeKnownRecord(*DestTypeStream, R, RemapSuccess); - } - - template - Error writeKnownIdRecord(RecordType &R, bool RemapSuccess) { - return writeKnownRecord(*DestIdStream, R, RemapSuccess); - } - Error writeRecord(TypeTableBuilder &Dest, const RemappedType &Record, bool RemapSuccess) { TypeIndex DestIdx = Untranslated; @@ -178,15 +143,6 @@ return writeRecord(*DestIdStream, Record, RemapSuccess); } - template - Error writeMember(RecordType &R, bool RemapSuccess) { - if (RemapSuccess) - FieldListBuilder->writeMemberType(R); - else - HadUntranslatedMember = true; - return Error::success(); - } - Optional LastError; bool IsSecondPass = false; @@ -217,7 +173,26 @@ const TypeIndex TypeStreamMerger::Untranslated(SimpleTypeKind::NotTranslated); -Error TypeStreamMerger::visitTypeBegin(CVType &Rec) { return Error::success(); } +Error TypeStreamMerger::visitTypeBegin(CVType &Rec) { + RemappedType R(Rec); + SmallVector Refs; + discoverTypeIndices(Rec.RecordData, Refs); + + bool Success = remapIndices(R, Refs); + switch (Rec.kind()) { + case TypeLeafKind::LF_FUNC_ID: + case TypeLeafKind::LF_MFUNC_ID: + case TypeLeafKind::LF_STRING_ID: + case TypeLeafKind::LF_SUBSTR_LIST: + case TypeLeafKind::LF_BUILDINFO: + case TypeLeafKind::LF_UDT_SRC_LINE: + case TypeLeafKind::LF_UDT_MOD_SRC_LINE: + return writeIdRecord(R, Success); + default: + return writeTypeRecord(R, Success); + } + return Error::success(); +} Error TypeStreamMerger::visitTypeEnd(CVType &Rec) { ++CurIndex; @@ -227,10 +202,6 @@ return Error::success(); } -Error TypeStreamMerger::visitMemberEnd(CVMemberRecord &Rec) { - return Error::success(); -} - void TypeStreamMerger::addMapping(TypeIndex Idx) { if (!IsSecondPass) { assert(IndexMap.size() == slotForIndex(CurIndex) && @@ -290,283 +261,6 @@ return remapIndex(Idx, IndexMap); } -//----------------------------------------------------------------------------// -// Item records -//----------------------------------------------------------------------------// - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, FuncIdRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - return writeIdRecord(RR, remapIndices(RR, {4}, {0})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - return writeIdRecord(RR, remapIndices(RR, {0, 4}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, StringIdRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - return writeIdRecord(RR, remapIndices(RR, {}, {0})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, StringListRecord &R) { - assert(DestIdStream); - - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - bool Success = true; - - for (TypeIndex &Id : R.StringIndices) - Success &= remapItemIndex(Id); - return writeKnownIdRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, BuildInfoRecord &R) { - assert(DestIdStream); - - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - - bool Success = true; - for (TypeIndex &Str : R.ArgIndices) - Success &= remapItemIndex(Str); - return writeKnownIdRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, UdtSourceLineRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - - // FIXME: Translate UdtSourceLineRecord into UdtModSourceLineRecords in the - // IPI stream. - return writeIdRecord(RR, remapIndices(RR, {0}, {4})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, - UdtModSourceLineRecord &R) { - assert(DestIdStream); - - RemappedType RR(CVR); - - // UdtModSourceLine Source File Ids are offsets into the global string table, - // not type indices. - // FIXME: We need to merge string table records for this to be valid. - return writeIdRecord(RR, remapIndices(RR, {0}, {})); -} - -//----------------------------------------------------------------------------// -// Type records -//----------------------------------------------------------------------------// - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ModifierRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ProcedureRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0, 8}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, MemberFunctionRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0, 4, 8, 16}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ArgListRecord &R) { - assert(DestTypeStream); - - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - - bool Success = true; - for (TypeIndex &Arg : R.ArgIndices) - Success &= remapTypeIndex(Arg); - - return writeKnownTypeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, PointerRecord &R) { - assert(DestTypeStream); - - // Pointer records have a different number of TypeIndex mappings depending - // on whether or not it is a pointer to member. - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - - bool Success = remapTypeIndex(R.ReferentType); - if (R.isPointerToMember()) - Success &= remapTypeIndex(R.MemberInfo->ContainingType); - return writeKnownTypeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ArrayRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0, 4}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, ClassRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {4, 8, 12}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, UnionRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {4}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, EnumRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {4, 8}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, BitFieldRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, VFTableShapeRecord &R) { - assert(DestTypeStream); - - return writeTypeRecord(CVR); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, TypeServer2Record &R) { - assert(DestTypeStream); - - return writeTypeRecord(CVR); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, LabelRecord &R) { - assert(DestTypeStream); - - return writeTypeRecord(CVR); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, VFTableRecord &R) { - assert(DestTypeStream); - - RemappedType RR(CVR); - return writeTypeRecord(RR, remapIndices(RR, {0, 4}, {})); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, - MethodOverloadListRecord &R) { - assert(DestTypeStream); - - if (auto EC = TypeDeserializer::deserializeAs(CVR, R)) - return EC; - - bool Success = true; - for (OneMethodRecord &Meth : R.Methods) - Success &= remapTypeIndex(Meth.Type); - return writeKnownTypeRecord(R, Success); -} - -Error TypeStreamMerger::visitKnownRecord(CVType &CVR, FieldListRecord &R) { - assert(DestTypeStream); - // Visit the members inside the field list. - HadUntranslatedMember = false; - if (!FieldListBuilder) - FieldListBuilder = - llvm::make_unique(*DestTypeStream); - - FieldListBuilder->begin(); - if (auto EC = codeview::visitMemberRecordStream(CVR.content(), *this)) - return EC; - - // Write the record if we translated all field list members. - TypeIndex DestIdx = FieldListBuilder->end(!HadUntranslatedMember); - addMapping(HadUntranslatedMember ? Untranslated : DestIdx); - - return Error::success(); -} - -//----------------------------------------------------------------------------// -// Member records -//----------------------------------------------------------------------------// - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - NestedTypeRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, OneMethodRecord &R) { - bool Success = true; - Success &= remapTypeIndex(R.Type); - return writeMember(R, Success); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - OverloadedMethodRecord &R) { - return writeMember(R, remapTypeIndex(R.MethodList)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - DataMemberRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - StaticDataMemberRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - EnumeratorRecord &R) { - return writeMember(R, true); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, VFPtrRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, BaseClassRecord &R) { - return writeMember(R, remapTypeIndex(R.Type)); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - VirtualBaseClassRecord &R) { - bool Success = true; - Success &= remapTypeIndex(R.BaseType); - Success &= remapTypeIndex(R.VBPtrType); - return writeMember(R, Success); -} - -Error TypeStreamMerger::visitKnownMember(CVMemberRecord &, - ListContinuationRecord &R) { - return writeMember(R, remapTypeIndex(R.ContinuationIndex)); -} - -Error TypeStreamMerger::visitUnknownType(CVType &Rec) { - // We failed to translate a type. Translate this index as "not translated". - addMapping(TypeIndex(SimpleTypeKind::NotTranslated)); - return errorCorruptRecord(); -} - Error TypeStreamMerger::mergeTypeRecords(TypeTableBuilder &Dest, const CVTypeArray &Types) { DestTypeStream = &Dest; @@ -598,6 +292,10 @@ // We don't want to deserialize records. I guess this flag is poorly named, // but it really means "Don't deserialize records before switching on the // concrete type. + // FIXME: We can probably get even more speed here if we don't use the visitor + // pipeline here, but instead write the switch ourselves. I don't think it + // would buy us much since it's already pretty fast, but it's probably worth + // a few cycles. if (auto EC = codeview::visitTypeStream(Types, *this, VDS_BytesExternal, Handler)) return EC; Index: llvm/unittests/DebugInfo/CodeView/CMakeLists.txt =================================================================== --- llvm/unittests/DebugInfo/CodeView/CMakeLists.txt +++ llvm/unittests/DebugInfo/CodeView/CMakeLists.txt @@ -4,6 +4,7 @@ set(DebugInfoCodeViewSources RandomAccessVisitorTest.cpp + TypeIndexDiscoveryTest.cpp ) add_llvm_unittest(DebugInfoCodeViewTests Index: llvm/unittests/DebugInfo/CodeView/ErrorChecking.h =================================================================== --- llvm/unittests/DebugInfo/CodeView/ErrorChecking.h +++ llvm/unittests/DebugInfo/CodeView/ErrorChecking.h @@ -26,6 +26,15 @@ consumeError(std::move(E)); \ } +#define ASSERT_EXPECTED(Exp) \ + { \ + auto E = Exp.takeError(); \ + bool Success = static_cast(E); \ + if (!Success) \ + consumeError(std::move(E)); \ + ASSERT_FALSE(Success); \ + } + #define EXPECT_EXPECTED(Exp) \ { \ auto E = Exp.takeError(); \ Index: llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp @@ -0,0 +1,486 @@ +//===- llvm/unittest/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" + +#include "ErrorChecking.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/Support/Allocator.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::codeview; + +class TypeIndexIteratorTest : public testing::Test { +public: + TypeIndexIteratorTest() {} + + void SetUp() override { + Refs.clear(); + TTB = make_unique(Storage); + FLRB = make_unique(*TTB); + } + + void TearDown() override { + FLRB.reset(); + TTB.reset(); + } + +protected: + template + bool checkTypeReferences(uint32_t RecordIndex, Indices &&... TIs) const { + EXPECT_EQ(sizeof...(Indices), countRefs(RecordIndex)); + return checkTypeReferencesImpl(RecordIndex, std::forward(TIs)...); + } + + template void writeFieldList(T &&... MemberRecords) { + FLRB->begin(); + writeFieldListImpl(std::forward(MemberRecords)...); + FLRB->end(true); + ASSERT_EQ(1u, TTB->records().size()); + discoverAllTypeIndices(); + } + + template void writeTypeRecords(T &&... Records) { + writeTypeRecordsImpl(std::forward(Records)...); + ASSERT_EQ(sizeof...(T), TTB->records().size()); + discoverAllTypeIndices(); + } + + std::unique_ptr TTB; + +private: + uint32_t countRefs(uint32_t RecordIndex) const { + auto &R = Refs[RecordIndex]; + uint32_t Count = 0; + for (auto &Ref : R) { + Count += Ref.Count; + } + return Count; + } + + bool checkOneTypeReference(uint32_t RecordIndex, ArrayRef RecordData, + TypeIndex TI) const { + RecordData = RecordData.drop_front(sizeof(RecordPrefix)); + auto &RefList = Refs[RecordIndex]; + for (auto &Ref : RefList) { + uint32_t Offset = Ref.Offset; + ArrayRef Loc = RecordData.drop_front(Offset); + ArrayRef Indices( + reinterpret_cast(Loc.data()), Ref.Count); + if (llvm::any_of(Indices, + [TI](const TypeIndex &Other) { return Other == TI; })) + return true; + } + return false; + } + + template + bool checkTypeReferencesImpl(uint32_t RecordIndex) const { + return true; + } + + template + bool checkTypeReferencesImpl(uint32_t RecordIndex, TypeIndex TI, + Indices &&... Rest) const { + ArrayRef Record = TTB->records()[RecordIndex]; + bool Success = checkOneTypeReference(RecordIndex, Record, TI); + EXPECT_TRUE(Success); + return Success & + checkTypeReferencesImpl(RecordIndex, std::forward(Rest)...); + } + + void discoverAllTypeIndices() { + Refs.resize(TTB->records().size()); + for (uint32_t I = 0; I < TTB->records().size(); ++I) { + ArrayRef Data = TTB->records()[I]; + discoverTypeIndices(Data, Refs[I]); + } + } + + // Helper function to write out a field list record with the given list + // of member records. + void writeFieldListImpl() {} + + template + void writeFieldListImpl(RecType &&Record, Rest &&... Records) { + FLRB->writeMemberType(Record); + writeFieldListImpl(std::forward(Records)...); + } + + // Helper function to write out a list of type records. + void writeTypeRecordsImpl() {} + + template + void writeTypeRecordsImpl(RecType &&Record, Rest &&... Records) { + TTB->writeKnownType(Record); + writeTypeRecordsImpl(std::forward(Records)...); + } + + std::vector> Refs; + std::unique_ptr FLRB; + BumpPtrAllocator Storage; +}; + +namespace leafs { +static FuncIdRecord FuncId(TypeIndex(333), TypeIndex(17), "FuncId"); +static MemberFuncIdRecord MemFuncId(TypeIndex(333), TypeIndex(17), "FuncId"); +static StringIdRecord StringId(TypeIndex(712), "TheString"); +static struct { + std::vector Ids = {TypeIndex(10), TypeIndex(27), TypeIndex(9023)}; + StringListRecord Record{TypeRecordKind::StringList, Ids}; +} StringList; +static struct { + std::vector Ids = {TypeIndex(10), TypeIndex(27), TypeIndex(9023)}; + BuildInfoRecord Record{Ids}; +} BuildInfo; +static UdtSourceLineRecord UdtSourceLine(TypeIndex(9082), TypeIndex(89342), 0); +static UdtModSourceLineRecord UdtModSourceLine(TypeIndex(9082), + TypeIndex(89342), 0, 0); +static ModifierRecord Modifier(TypeIndex(1212), ModifierOptions::None); +static ProcedureRecord Procedure(TypeIndex(12), CallingConvention::PpcCall, + FunctionOptions::None, 0, TypeIndex(3456)); +static MemberFunctionRecord MemberFunction(TypeIndex(1234), TypeIndex(6543), + TypeIndex(2123), + CallingConvention::ThisCall, + FunctionOptions::None, 2, + TypeIndex(1213), 0); +static struct { + std::vector Ids = {TypeIndex(10), TypeIndex(27), TypeIndex(9023)}; + ArgListRecord Record{TypeRecordKind::ArgList, Ids}; +} ArgList; +static ArrayRecord Array(TypeIndex(1234), TypeIndex(6543), 10, "MyArray"); +static ClassRecord Class(TypeRecordKind::Class, 3, ClassOptions::None, + TypeIndex(1234), TypeIndex(6543), TypeIndex(98), 10, + "MyClass", "MyClassUniqueName"); +static UnionRecord Union(1, ClassOptions::None, TypeIndex(1234), 10, "MyUnion", + "MyUnionUniqueName"); +static EnumRecord Enum(1, ClassOptions::None, TypeIndex(1234), "MyEnum", + "EnumUniqueName", TypeIndex(2345)); +static BitFieldRecord BitField(TypeIndex(1234), 1, 0); +static VFTableRecord VFTable(TypeIndex(1234), TypeIndex(2345), 1, "VFT", {}); +static VFTableShapeRecord VTableShape({}); +static struct { + const TypeIndex T1{1234}; + const TypeIndex T2{2345}; + const TypeIndex T3{3456}; + const TypeIndex T4{4567}; + + std::vector Methods{ + {T1, MemberAccess::Public, MethodKind::IntroducingVirtual, + MethodOptions::None, 0, "Method1"}, + {T2, MemberAccess::Public, MethodKind::PureVirtual, MethodOptions::None, + 0, "Method1"}, + {T3, MemberAccess::Public, MethodKind::PureIntroducingVirtual, + MethodOptions::None, 0, "Method1"}, + {T4, MemberAccess::Public, MethodKind::Static, MethodOptions::None, 0, + "Method1"}}; + + MethodOverloadListRecord Record{Methods}; +} MethodOverloadList; +static PointerRecord Pointer(TypeIndex(1234), PointerKind::Near32, + PointerMode::Pointer, PointerOptions::Const, 3); +static PointerRecord MemberPointer( + TypeIndex(1234), PointerKind::Near32, PointerMode::PointerToDataMember, + PointerOptions::Const, 3, + MemberPointerInfo(TypeIndex(2345), + PointerToMemberRepresentation::GeneralData)); +} + +namespace members { +static BaseClassRecord BaseClass(MemberAccess::Public, TypeIndex(1), 0); +static EnumeratorRecord Enumerator(MemberAccess::Public, + APSInt(APInt(8, 3, false)), "Test"); +DataMemberRecord DataMember(MemberAccess::Public, TypeIndex(2), 0, "Test"); +OverloadedMethodRecord OverloadedMethod(3, TypeIndex(3), "MethodList"); +static struct { + const TypeIndex T1{4}; + const TypeIndex T2{5}; + const TypeIndex T3{6}; + const TypeIndex T4{7}; + OneMethodRecord R1{T1, + MemberAccess::Public, + MethodKind::IntroducingVirtual, + MethodOptions::None, + 0, + "M1"}; + OneMethodRecord R2{T2, + MemberAccess::Public, + MethodKind::PureVirtual, + MethodOptions::None, + 0, + "M2"}; + OneMethodRecord R3{T3, + MemberAccess::Public, + MethodKind::PureIntroducingVirtual, + MethodOptions::None, + 0, + "M3"}; + OneMethodRecord R4{T4, + MemberAccess::Protected, + MethodKind::Vanilla, + MethodOptions::CompilerGenerated, + 0, + "M4"}; +} OneMethod; +static NestedTypeRecord NestedType(TypeIndex(8), "MyClass"); +static StaticDataMemberRecord StaticDataMember(MemberAccess::Public, + TypeIndex(9), "Foo"); +static VirtualBaseClassRecord VirtualBaseClass(TypeRecordKind::VirtualBaseClass, + MemberAccess::Public, + TypeIndex(10), TypeIndex(11), 0, + 0); +static VFPtrRecord VFPtr(TypeIndex(12)); +static ListContinuationRecord Continuation(TypeIndex(13)); +} + +TEST_F(TypeIndexIteratorTest, FuncId) { + using namespace leafs; + writeTypeRecords(FuncId); + checkTypeReferences(0, FuncId.FunctionType, FuncId.ParentScope); +} + +TEST_F(TypeIndexIteratorTest, MemFuncId) { + using namespace leafs; + writeTypeRecords(MemFuncId); + checkTypeReferences(0, MemFuncId.ClassType, MemFuncId.FunctionType); +} + +TEST_F(TypeIndexIteratorTest, StringId) { + using namespace leafs; + writeTypeRecords(StringId); + checkTypeReferences(0, StringId.Id); +} + +TEST_F(TypeIndexIteratorTest, SubstrList) { + using namespace leafs; + writeTypeRecords(StringList.Record); + checkTypeReferences(0, StringList.Ids[0], StringList.Ids[1], + StringList.Ids[2]); +} + +TEST_F(TypeIndexIteratorTest, BuildInfo) { + using namespace leafs; + writeTypeRecords(BuildInfo.Record); + checkTypeReferences(0, BuildInfo.Ids[0], BuildInfo.Ids[1], BuildInfo.Ids[2]); +} + +TEST_F(TypeIndexIteratorTest, UdtSrcLine) { + using namespace leafs; + writeTypeRecords(UdtSourceLine); + checkTypeReferences(0, UdtSourceLine.UDT, UdtSourceLine.SourceFile); +} + +TEST_F(TypeIndexIteratorTest, UdtModSrcLine) { + using namespace leafs; + writeTypeRecords(UdtModSourceLine); + checkTypeReferences(0, UdtModSourceLine.UDT, UdtModSourceLine.SourceFile); +} + +TEST_F(TypeIndexIteratorTest, Modifier) { + using namespace leafs; + writeTypeRecords(Modifier); + checkTypeReferences(0, Modifier.ModifiedType); +} + +TEST_F(TypeIndexIteratorTest, Procedure) { + using namespace leafs; + writeTypeRecords(Procedure); + checkTypeReferences(0, Procedure.ReturnType, Procedure.ArgumentList); +} + +TEST_F(TypeIndexIteratorTest, MemFunc) { + using namespace leafs; + writeTypeRecords(MemberFunction); + checkTypeReferences(0, MemberFunction.ReturnType, MemberFunction.ClassType, + MemberFunction.ThisType, MemberFunction.ArgumentList); +} + +TEST_F(TypeIndexIteratorTest, ArgList) { + using namespace leafs; + writeTypeRecords(ArgList.Record); + checkTypeReferences(0, ArgList.Ids[0], ArgList.Ids[1], ArgList.Ids[2]); +} + +TEST_F(TypeIndexIteratorTest, Array) { + using namespace leafs; + writeTypeRecords(Array); + checkTypeReferences(0, Array.ElementType, Array.IndexType); +} + +TEST_F(TypeIndexIteratorTest, Class) { + using namespace leafs; + writeTypeRecords(Class); + checkTypeReferences(0, Class.FieldList, Class.DerivationList, + Class.VTableShape); +} + +TEST_F(TypeIndexIteratorTest, Union) { + using namespace leafs; + writeTypeRecords(Union); + checkTypeReferences(0, Union.FieldList); +} + +TEST_F(TypeIndexIteratorTest, Enum) { + using namespace leafs; + writeTypeRecords(Enum); + checkTypeReferences(0, Enum.FieldList, Enum.UnderlyingType); +} + +TEST_F(TypeIndexIteratorTest, Bitfield) { + using namespace leafs; + writeTypeRecords(BitField); + checkTypeReferences(0, BitField.Type); +} + +TEST_F(TypeIndexIteratorTest, VTable) { + using namespace leafs; + writeTypeRecords(VFTable); + checkTypeReferences(0, VFTable.CompleteClass, VFTable.OverriddenVFTable); +} + +TEST_F(TypeIndexIteratorTest, VTShape) { + using namespace leafs; + writeTypeRecords(VTableShape); + checkTypeReferences(0); +} + +TEST_F(TypeIndexIteratorTest, OverloadList) { + using namespace leafs; + writeTypeRecords(MethodOverloadList.Record); + checkTypeReferences(0, MethodOverloadList.T1, MethodOverloadList.T2, + MethodOverloadList.T3, MethodOverloadList.T4); +} + +TEST_F(TypeIndexIteratorTest, Pointer) { + using namespace leafs; + writeTypeRecords(Pointer); + checkTypeReferences(0, Pointer.ReferentType); +} + +TEST_F(TypeIndexIteratorTest, MemberPointer) { + using namespace leafs; + writeTypeRecords(MemberPointer); + checkTypeReferences(0, MemberPointer.ReferentType, + MemberPointer.MemberInfo->ContainingType); +} + +TEST_F(TypeIndexIteratorTest, ManyTypes) { + + using namespace leafs; + writeTypeRecords(FuncId, MemFuncId, StringId, StringList.Record, + BuildInfo.Record, UdtSourceLine, UdtModSourceLine, Modifier, + Procedure, MemberFunction, ArgList.Record, Array, Class, + Union, Enum, BitField, VFTable, VTableShape, + MethodOverloadList.Record, Pointer, MemberPointer); + + checkTypeReferences(0, FuncId.FunctionType, FuncId.ParentScope); + checkTypeReferences(1, MemFuncId.ClassType, MemFuncId.FunctionType); + checkTypeReferences(2, StringId.Id); + checkTypeReferences(3, StringList.Ids[0], StringList.Ids[1], + StringList.Ids[2]); + checkTypeReferences(4, BuildInfo.Ids[0], BuildInfo.Ids[1], BuildInfo.Ids[2]); + checkTypeReferences(5, UdtSourceLine.UDT, UdtSourceLine.SourceFile); + checkTypeReferences(6, UdtModSourceLine.UDT, UdtModSourceLine.SourceFile); + checkTypeReferences(7, Modifier.ModifiedType); + checkTypeReferences(8, Procedure.ReturnType, Procedure.ArgumentList); + checkTypeReferences(9, MemberFunction.ReturnType, MemberFunction.ClassType, + MemberFunction.ThisType, MemberFunction.ArgumentList); + checkTypeReferences(10, ArgList.Ids[0], ArgList.Ids[1], ArgList.Ids[2]); + checkTypeReferences(11, Array.ElementType, Array.IndexType); + checkTypeReferences(12, Class.FieldList, Class.DerivationList, + Class.VTableShape); + checkTypeReferences(13, Union.FieldList); + checkTypeReferences(14, Enum.FieldList, Enum.UnderlyingType); + checkTypeReferences(15, BitField.Type); + checkTypeReferences(16, VFTable.CompleteClass, VFTable.OverriddenVFTable); + checkTypeReferences(17); + checkTypeReferences(18, MethodOverloadList.T1, MethodOverloadList.T2, + MethodOverloadList.T3, MethodOverloadList.T4); + checkTypeReferences(19, Pointer.ReferentType); + checkTypeReferences(20, MemberPointer.ReferentType, + MemberPointer.MemberInfo->ContainingType); +} + +TEST_F(TypeIndexIteratorTest, FieldListBaseClass) { + using namespace members; + writeFieldList(BaseClass); + checkTypeReferences(0, BaseClass.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListEnumerator) { + using namespace members; + writeFieldList(Enumerator); + checkTypeReferences(0); +} + +TEST_F(TypeIndexIteratorTest, FieldListMember) { + using namespace members; + writeFieldList(DataMember); + checkTypeReferences(0, DataMember.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListMethod) { + using namespace members; + writeFieldList(OverloadedMethod); + checkTypeReferences(0, OverloadedMethod.MethodList); +} + +TEST_F(TypeIndexIteratorTest, FieldListOneMethod) { + using namespace members; + writeFieldList(OneMethod.R1, OneMethod.R2, OneMethod.R3, OneMethod.R4); + checkTypeReferences(0, OneMethod.T1, OneMethod.T2, OneMethod.T3, + OneMethod.T4); +} + +TEST_F(TypeIndexIteratorTest, FieldListNestedType) { + using namespace members; + writeFieldList(NestedType); + checkTypeReferences(0, NestedType.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListStaticMember) { + using namespace members; + writeFieldList(StaticDataMember); + checkTypeReferences(0, StaticDataMember.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListVirtualBase) { + using namespace members; + writeFieldList(VirtualBaseClass); + checkTypeReferences(0, VirtualBaseClass.BaseType, VirtualBaseClass.VBPtrType); +} + +TEST_F(TypeIndexIteratorTest, FieldListVFTable) { + using namespace members; + writeFieldList(VFPtr); + checkTypeReferences(0, VFPtr.Type); +} + +TEST_F(TypeIndexIteratorTest, FieldListContinuation) { + using namespace members; + writeFieldList(Continuation); + checkTypeReferences(0, Continuation.ContinuationIndex); +} + +TEST_F(TypeIndexIteratorTest, ManyMembers) { + using namespace members; + writeFieldList(BaseClass, Enumerator, DataMember, OverloadedMethod, + OneMethod.R1, OneMethod.R2, OneMethod.R3, OneMethod.R4, + NestedType, StaticDataMember, VirtualBaseClass, VFPtr, + Continuation); + + checkTypeReferences( + 0, BaseClass.Type, DataMember.Type, OverloadedMethod.MethodList, + OneMethod.T1, OneMethod.T2, OneMethod.T3, OneMethod.T4, NestedType.Type, + StaticDataMember.Type, VirtualBaseClass.BaseType, + VirtualBaseClass.VBPtrType, VFPtr.Type, Continuation.ContinuationIndex); +} \ No newline at end of file