Resubmit "[pdb] Change type visitor pattern to be dynamic."

There was a regression introduced during type stream merging when
visiting a field list record.  This has been fixed in this patch.

llvm-svn: 272929
This commit is contained in:
Zachary Turner 2016-06-16 18:22:27 +00:00
parent 5a07687a8e
commit 01ee3dae04
14 changed files with 485 additions and 389 deletions

View File

@ -11,151 +11,31 @@
#define LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H #define LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H
#include "llvm/DebugInfo/CodeView/CVRecord.h" #include "llvm/DebugInfo/CodeView/CVRecord.h"
#include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/Support/ErrorOr.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
#include "llvm/Support/Error.h"
namespace llvm { namespace llvm {
namespace codeview { namespace codeview {
template <typename Derived>
class CVTypeVisitor { class CVTypeVisitor {
public: public:
CVTypeVisitor() {} explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks);
bool hadError() const { return HadError; } Error visitTypeRecord(const CVRecord<TypeLeafKind> &Record);
template <typename T>
bool consumeObject(ArrayRef<uint8_t> &Data, const T *&Res) {
if (Data.size() < sizeof(*Res)) {
HadError = true;
return false;
}
Res = reinterpret_cast<const T *>(Data.data());
Data = Data.drop_front(sizeof(*Res));
return true;
}
/// Actions to take on known types. By default, they do nothing. Visit methods
/// for member records take the FieldData by non-const reference and are
/// expected to consume the trailing bytes used by the field.
/// FIXME: Make the visitor interpret the trailing bytes so that clients don't
/// need to.
#define TYPE_RECORD(EnumName, EnumVal, Name) \
void visit##Name(Name##Record &Record) {}
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
void visit##Name(Name##Record &Record) {}
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "TypeRecords.def"
void visitTypeRecord(const CVRecord<TypeLeafKind> &Record) {
ArrayRef<uint8_t> LeafData = Record.Data;
auto *DerivedThis = static_cast<Derived *>(this);
DerivedThis->visitTypeBegin(Record);
switch (Record.Type) {
default:
DerivedThis->visitUnknownType(Record);
break;
case LF_FIELDLIST:
DerivedThis->visitFieldList(Record.Type, LeafData);
break;
#define TYPE_RECORD(EnumName, EnumVal, Name) \
case EnumName: { \
TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \
auto Result = Name##Record::deserialize(RK, LeafData); \
if (Result.getError()) \
return parseError(); \
DerivedThis->visit##Name(*Result); \
break; \
}
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
TYPE_RECORD(EnumVal, EnumVal, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name)
#include "TypeRecords.def"
}
DerivedThis->visitTypeEnd(Record);
}
/// Visits the type records in Data. Sets the error flag on parse failures. /// Visits the type records in Data. Sets the error flag on parse failures.
void visitTypeStream(const CVTypeArray &Types) { Error visitTypeStream(const CVTypeArray &Types);
for (const auto &I : Types) {
visitTypeRecord(I);
if (hadError())
break;
}
}
/// Action to take on unknown types. By default, they are ignored. Error skipPadding(ArrayRef<uint8_t> &Data);
void visitUnknownType(const CVRecord<TypeLeafKind> &Record) {}
/// Paired begin/end actions for all types. Receives all record data,
/// including the fixed-length record prefix.
void visitTypeBegin(const CVRecord<TypeLeafKind> &Record) {}
void visitTypeEnd(const CVRecord<TypeLeafKind> &Record) {}
ArrayRef<uint8_t> skipPadding(ArrayRef<uint8_t> Data) {
if (Data.empty())
return Data;
uint8_t Leaf = Data.front();
if (Leaf < LF_PAD0)
return Data;
// Leaf is greater than 0xf0. We should advance by the number of bytes in
// the low 4 bits.
unsigned BytesToAdvance = Leaf & 0x0F;
if (Data.size() < BytesToAdvance) {
parseError();
return None;
}
return Data.drop_front(BytesToAdvance);
}
/// Visits individual member records of a field list record. Member records do /// Visits individual member records of a field list record. Member records do
/// not describe their own length, and need special handling. /// not describe their own length, and need special handling.
void visitFieldList(TypeLeafKind Leaf, ArrayRef<uint8_t> FieldData) { Error visitFieldList(const CVRecord<TypeLeafKind> &Record);
auto *DerivedThis = static_cast<Derived *>(this);
while (!FieldData.empty()) {
const ulittle16_t *LeafPtr;
if (!CVTypeVisitor::consumeObject(FieldData, LeafPtr))
return;
TypeLeafKind Leaf = TypeLeafKind(unsigned(*LeafPtr));
switch (Leaf) {
default:
// Field list records do not describe their own length, so we cannot
// continue parsing past an unknown member type.
DerivedThis->visitUnknownMember(Leaf);
return parseError();
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
case EnumName: { \
TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \
auto Result = Name##Record::deserialize(RK, FieldData); \
if (Result.getError()) \
return parseError(); \
DerivedThis->visit##Name(*Result); \
break; \
}
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
MEMBER_RECORD(EnumVal, EnumVal, AliasName)
#include "TypeRecords.def"
}
FieldData = skipPadding(FieldData);
if (hadError())
break;
}
}
/// Action to take on unknown members. By default, they are ignored. Member
/// record parsing cannot recover from an unknown member record, so this
/// method is only called at most once per field list record.
void visitUnknownMember(TypeLeafKind Leaf) {}
/// Helper for returning from a void function when the stream is corrupted.
void parseError() { HadError = true; }
private: private:
/// Whether a type stream parsing error was encountered. /// The interface to the class that gets notified of each visitation.
bool HadError = false; TypeVisitorCallbacks &Callbacks;
}; };
} // end namespace codeview } // end namespace codeview

View File

@ -14,6 +14,7 @@
#include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSet.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
namespace llvm { namespace llvm {
class ScopedPrinter; class ScopedPrinter;
@ -21,7 +22,7 @@ class ScopedPrinter;
namespace codeview { namespace codeview {
/// Dumper for CodeView type streams found in COFF object files and PDB files. /// Dumper for CodeView type streams found in COFF object files and PDB files.
class CVTypeDumper { class CVTypeDumper : public TypeVisitorCallbacks {
public: public:
CVTypeDumper(ScopedPrinter *W, bool PrintRecordBytes) CVTypeDumper(ScopedPrinter *W, bool PrintRecordBytes)
: W(W), PrintRecordBytes(PrintRecordBytes) {} : W(W), PrintRecordBytes(PrintRecordBytes) {}
@ -33,17 +34,17 @@ public:
/// and true otherwise. This should be called in order, since the dumper /// and true otherwise. This should be called in order, since the dumper
/// maintains state about previous records which are necessary for cross /// maintains state about previous records which are necessary for cross
/// type references. /// type references.
bool dump(const CVRecord<TypeLeafKind> &Record); Error dump(const CVRecord<TypeLeafKind> &Record);
/// Dumps the type records in Types. Returns false if there was a type stream /// Dumps the type records in Types. Returns false if there was a type stream
/// parse error, and true otherwise. /// parse error, and true otherwise.
bool dump(const CVTypeArray &Types); Error dump(const CVTypeArray &Types);
/// Dumps the type records in Data. Returns false if there was a type stream /// Dumps the type records in Data. Returns false if there was a type stream
/// parse error, and true otherwise. Use this method instead of the /// parse error, and true otherwise. Use this method instead of the
/// CVTypeArray overload when type records are laid out contiguously in /// CVTypeArray overload when type records are laid out contiguously in
/// memory. /// memory.
bool dump(ArrayRef<uint8_t> Data); Error dump(ArrayRef<uint8_t> Data);
/// Gets the type index for the next type record. /// Gets the type index for the next type record.
unsigned getNextTypeIndex() const { unsigned getNextTypeIndex() const {
@ -61,11 +62,35 @@ public:
void setPrinter(ScopedPrinter *P); void setPrinter(ScopedPrinter *P);
ScopedPrinter *getPrinter() { return W; } ScopedPrinter *getPrinter() { return W; }
/// Action to take on unknown types. By default, they are ignored.
Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) override;
Error visitUnknownMember(const CVRecord<TypeLeafKind> &Record) override;
/// Paired begin/end actions for all types. Receives all record data,
/// including the fixed-length record prefix.
Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) override;
Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override;
#define TYPE_RECORD(EnumName, EnumVal, Name) \
Error visit##Name(Name##Record &Record) override;
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
TYPE_RECORD(EnumName, EnumVal, Name)
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "TypeRecords.def"
private: private:
void printMemberAttributes(MemberAttributes Attrs);
void printMemberAttributes(MemberAccess Access, MethodKind Kind,
MethodOptions Options);
ScopedPrinter *W; ScopedPrinter *W;
bool PrintRecordBytes = false; bool PrintRecordBytes = false;
/// Name of the current type. Only valid before visitTypeEnd.
StringRef Name;
/// All user defined type records in .debug$T live in here. Type indices /// All user defined type records in .debug$T live in here. Type indices
/// greater than 0x1000 are user defined. Subtract 0x1000 from the index to /// greater than 0x1000 are user defined. Subtract 0x1000 from the index to
/// index into this vector. /// index into this vector.

View File

@ -83,6 +83,7 @@ TYPE_RECORD(LF_STRING_ID, 0x1605, StringId)
TYPE_RECORD(LF_UDT_SRC_LINE, 0x1606, UdtSourceLine) TYPE_RECORD(LF_UDT_SRC_LINE, 0x1606, UdtSourceLine)
TYPE_RECORD(LF_UDT_MOD_SRC_LINE, 0x1607, UdtModSourceLine) TYPE_RECORD(LF_UDT_MOD_SRC_LINE, 0x1607, UdtModSourceLine)
TYPE_RECORD(LF_METHODLIST, 0x1206, MethodOverloadList) TYPE_RECORD(LF_METHODLIST, 0x1206, MethodOverloadList)
@ -195,6 +196,8 @@ CV_TYPE(LF_MODIFIER_EX, 0x1518)
CV_TYPE(LF_VECTOR, 0x151b) CV_TYPE(LF_VECTOR, 0x151b)
CV_TYPE(LF_MATRIX, 0x151c) CV_TYPE(LF_MATRIX, 0x151c)
// ID leaf records. Subsequent leaf types may be referenced from .debug$S.
// Numeric leaf types. These are generally contained in other records, and not // Numeric leaf types. These are generally contained in other records, and not
// encountered in the main type stream. // encountered in the main type stream.

View File

@ -0,0 +1,61 @@
//===- TypeVisitorCallbacks.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_TYPEVISITORCALLBACKS_H
#define LLVM_DEBUGINFO_CODEVIEW_TYPEVISITORCALLBACKS_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/Support/Error.h"
namespace llvm {
namespace codeview {
class TypeVisitorCallbacks {
friend class CVTypeVisitor;
public:
virtual ~TypeVisitorCallbacks() {}
/// Action to take on unknown types. By default, they are ignored.
virtual Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) {
return Error::success();
}
virtual Error visitUnknownMember(const CVRecord<TypeLeafKind> &Record) {
return Error::success();
}
/// Paired begin/end actions for all types. Receives all record data,
/// including the fixed-length record prefix.
virtual Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) {
return Error::success();
}
virtual Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) {
return Error::success();
}
virtual Error visitFieldListBegin(const CVRecord<TypeLeafKind> &Record) {
return Error::success();
}
virtual Error visitFieldListEnd(const CVRecord<TypeLeafKind> &Record) {
return Error::success();
}
#define TYPE_RECORD(EnumName, EnumVal, Name) \
virtual Error visit##Name(Name##Record &Record) { return Error::success(); }
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
TYPE_RECORD(EnumName, EnumVal, Name)
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "TypeRecords.def"
};
}
}
#endif

View File

@ -25,6 +25,7 @@ enum class raw_error_code {
index_out_of_bounds, index_out_of_bounds,
invalid_block_address, invalid_block_address,
not_writable, not_writable,
invalid_tpi_hash,
}; };
/// Base class for errors originating when parsing raw PDB files /// Base class for errors originating when parsing raw PDB files

View File

@ -322,10 +322,9 @@ void CodeViewDebug::emitTypeInformation() {
ScopedPrinter SP(CommentOS); ScopedPrinter SP(CommentOS);
SP.setPrefix(CommentPrefix); SP.setPrefix(CommentPrefix);
CVTD.setPrinter(&SP); CVTD.setPrinter(&SP);
bool DumpSuccess = Error EC = CVTD.dump({Record.bytes_begin(), Record.bytes_end()});
CVTD.dump({Record.bytes_begin(), Record.bytes_end()}); assert(!EC && "produced malformed type record");
(void)DumpSuccess; consumeError(std::move(EC));
assert(DumpSuccess && "produced malformed type record");
// emitRawComment will insert its own tab and comment string before // emitRawComment will insert its own tab and comment string before
// the first line, so strip off our first one. It also prints its own // the first line, so strip off our first one. It also prints its own
// newline. // newline.

View File

@ -1,6 +1,7 @@
add_llvm_library(LLVMDebugInfoCodeView add_llvm_library(LLVMDebugInfoCodeView
ByteStream.cpp ByteStream.cpp
CodeViewError.cpp CodeViewError.cpp
CVTypeVisitor.cpp
EnumTables.cpp EnumTables.cpp
FieldListRecordBuilder.cpp FieldListRecordBuilder.cpp
Line.cpp Line.cpp

View File

@ -0,0 +1,123 @@
//===- CVTypeVisitor.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/CVTypeVisitor.h"
using namespace llvm;
using namespace llvm::codeview;
template <typename T>
static Error takeObject(ArrayRef<uint8_t> &Data, const T *&Res) {
if (Data.size() < sizeof(*Res))
return llvm::make_error<CodeViewError>(cv_error_code::insufficient_buffer);
Res = reinterpret_cast<const T *>(Data.data());
Data = Data.drop_front(sizeof(*Res));
return Error::success();
}
CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
: Callbacks(Callbacks) {}
Error CVTypeVisitor::visitTypeRecord(const CVRecord<TypeLeafKind> &Record) {
ArrayRef<uint8_t> LeafData = Record.Data;
if (auto EC = Callbacks.visitTypeBegin(Record))
return EC;
switch (Record.Type) {
default:
if (auto EC = Callbacks.visitUnknownType(Record))
return EC;
break;
case LF_FIELDLIST:
if (auto EC = Callbacks.visitFieldListBegin(Record))
return EC;
if (auto EC = visitFieldList(Record))
return EC;
if (auto EC = Callbacks.visitFieldListEnd(Record))
return EC;
break;
#define TYPE_RECORD(EnumName, EnumVal, Name) \
case EnumName: { \
TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \
auto Result = Name##Record::deserialize(RK, LeafData); \
if (Result.getError()) \
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); \
if (auto EC = Callbacks.visit##Name(*Result)) \
return EC; \
break; \
}
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
TYPE_RECORD(EnumVal, EnumVal, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name)
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
}
if (auto EC = Callbacks.visitTypeEnd(Record))
return EC;
return Error::success();
}
/// Visits the type records in Data. Sets the error flag on parse failures.
Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) {
for (const auto &I : Types) {
if (auto EC = visitTypeRecord(I))
return EC;
}
return Error::success();
}
Error CVTypeVisitor::skipPadding(ArrayRef<uint8_t> &Data) {
if (Data.empty())
return Error::success();
uint8_t Leaf = Data.front();
if (Leaf < LF_PAD0)
return Error::success();
// Leaf is greater than 0xf0. We should advance by the number of bytes in
// the low 4 bits.
unsigned BytesToAdvance = Leaf & 0x0F;
if (Data.size() < BytesToAdvance) {
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record,
"Invalid padding bytes!");
}
Data = Data.drop_front(BytesToAdvance);
return Error::success();
}
/// Visits individual member records of a field list record. Member records do
/// not describe their own length, and need special handling.
Error CVTypeVisitor::visitFieldList(const CVRecord<TypeLeafKind> &Record) {
ArrayRef<uint8_t> RecordData = Record.Data;
while (!RecordData.empty()) {
const ulittle16_t *LeafPtr;
if (auto EC = takeObject(RecordData, LeafPtr))
return EC;
TypeLeafKind Leaf = TypeLeafKind(unsigned(*LeafPtr));
switch (Leaf) {
default:
// Field list records do not describe their own length, so we cannot
// continue parsing past an unknown member type.
if (auto EC = Callbacks.visitUnknownMember(Record))
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record);
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
case EnumName: { \
TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \
auto Result = Name##Record::deserialize(RK, RecordData); \
if (Result.getError()) \
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); \
if (auto EC = Callbacks.visit##Name(*Result)) \
return EC; \
break; \
}
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
MEMBER_RECORD(EnumVal, EnumVal, AliasName)
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
}
if (auto EC = skipPadding(RecordData))
return EC;
}
return Error::success();
}

View File

@ -189,55 +189,6 @@ static const EnumEntry<uint8_t> FunctionOptionEnum[] = {
#undef ENUM_ENTRY #undef ENUM_ENTRY
namespace {
/// Use this private dumper implementation to keep implementation details about
/// the visitor out of TypeDumper.h.
class CVTypeDumperImpl : public CVTypeVisitor<CVTypeDumperImpl> {
public:
CVTypeDumperImpl(CVTypeDumper &CVTD, ScopedPrinter &W, bool PrintRecordBytes)
: CVTD(CVTD), W(W), PrintRecordBytes(PrintRecordBytes) {}
/// CVTypeVisitor overrides.
#define TYPE_RECORD(EnumName, EnumVal, Name) \
void visit##Name(Name##Record &Record);
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
void visit##Name(Name##Record &Record);
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
void visitUnknownMember(TypeLeafKind Leaf);
void visitUnknownType(const CVRecord<TypeLeafKind> &Record);
void visitTypeBegin(const CVRecord<TypeLeafKind> &Record);
void visitTypeEnd(const CVRecord<TypeLeafKind> &Record);
void printMemberAttributes(MemberAttributes Attrs);
void printMemberAttributes(MemberAccess Access, MethodKind Kind,
MethodOptions Options);
private:
/// Forwards to the dumper, which holds the persistent state from visitation.
StringRef getTypeName(TypeIndex TI) {
return CVTD.getTypeName(TI);
}
void printTypeIndex(StringRef FieldName, TypeIndex TI) {
CVTD.printTypeIndex(FieldName, TI);
}
CVTypeDumper &CVTD;
ScopedPrinter &W;
bool PrintRecordBytes = false;
/// Name of the current type. Only valid before visitTypeEnd.
StringRef Name;
};
} // end anonymous namespace
static StringRef getLeafTypeName(TypeLeafKind LT) { static StringRef getLeafTypeName(TypeLeafKind LT) {
switch (LT) { switch (LT) {
#define TYPE_RECORD(ename, value, name) \ #define TYPE_RECORD(ename, value, name) \
@ -250,39 +201,44 @@ static StringRef getLeafTypeName(TypeLeafKind LT) {
return "UnknownLeaf"; return "UnknownLeaf";
} }
void CVTypeDumperImpl::visitTypeBegin(const CVRecord<TypeLeafKind> &Rec) { Error CVTypeDumper::visitTypeBegin(const CVRecord<TypeLeafKind> &Record) {
// Reset Name to the empty string. If the visitor sets it, we know it. // Reset Name to the empty string. If the visitor sets it, we know it.
Name = ""; Name = "";
W.startLine() << getLeafTypeName(Rec.Type) << " (" W->startLine() << getLeafTypeName(Record.Type) << " ("
<< HexNumber(CVTD.getNextTypeIndex()) << ") {\n"; << HexNumber(getNextTypeIndex()) << ") {\n";
W.indent(); W->indent();
W.printEnum("TypeLeafKind", unsigned(Rec.Type), makeArrayRef(LeafTypeNames)); W->printEnum("TypeLeafKind", unsigned(Record.Type),
makeArrayRef(LeafTypeNames));
return Error::success();
} }
void CVTypeDumperImpl::visitTypeEnd(const CVRecord<TypeLeafKind> &Rec) { Error CVTypeDumper::visitTypeEnd(const CVRecord<TypeLeafKind> &Record) {
// Always record some name for every type, even if Name is empty. CVUDTNames // Always record some name for every type, even if Name is empty. CVUDTNames
// is indexed by type index, and must have one entry for every type. // is indexed by type index, and must have one entry for every type.
CVTD.recordType(Name); recordType(Name);
if (PrintRecordBytes)
W.printBinaryBlock("LeafData", getBytesAsCharacters(Rec.Data));
W.unindent(); if (PrintRecordBytes)
W.startLine() << "}\n"; W->printBinaryBlock("LeafData", getBytesAsCharacters(Record.Data));
W->unindent();
W->startLine() << "}\n";
return Error::success();
} }
void CVTypeDumperImpl::visitStringId(StringIdRecord &String) { Error CVTypeDumper::visitStringId(StringIdRecord &String) {
printTypeIndex("Id", String.getId()); printTypeIndex("Id", String.getId());
W.printString("StringData", String.getString()); W->printString("StringData", String.getString());
// Put this in CVUDTNames so it gets printed with LF_UDT_SRC_LINE. // Put this in CVUDTNames so it gets printed with LF_UDT_SRC_LINE.
Name = String.getString(); Name = String.getString();
return Error::success();
} }
void CVTypeDumperImpl::visitArgList(ArgListRecord &Args) { Error CVTypeDumper::visitArgList(ArgListRecord &Args) {
auto Indices = Args.getIndices(); auto Indices = Args.getIndices();
uint32_t Size = Indices.size(); uint32_t Size = Indices.size();
W.printNumber("NumArgs", Size); W->printNumber("NumArgs", Size);
ListScope Arguments(W, "Arguments"); ListScope Arguments(*W, "Arguments");
SmallString<256> TypeName("("); SmallString<256> TypeName("(");
for (uint32_t I = 0; I < Size; ++I) { for (uint32_t I = 0; I < Size; ++I) {
printTypeIndex("ArgType", Indices[I]); printTypeIndex("ArgType", Indices[I]);
@ -292,77 +248,84 @@ void CVTypeDumperImpl::visitArgList(ArgListRecord &Args) {
TypeName.append(", "); TypeName.append(", ");
} }
TypeName.push_back(')'); TypeName.push_back(')');
Name = CVTD.saveName(TypeName); Name = saveName(TypeName);
return Error::success();
} }
void CVTypeDumperImpl::visitClass(ClassRecord &Class) { Error CVTypeDumper::visitClass(ClassRecord &Class) {
uint16_t Props = static_cast<uint16_t>(Class.getOptions()); uint16_t Props = static_cast<uint16_t>(Class.getOptions());
W.printNumber("MemberCount", Class.getMemberCount()); W->printNumber("MemberCount", Class.getMemberCount());
W.printFlags("Properties", Props, makeArrayRef(ClassOptionNames)); W->printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
printTypeIndex("FieldList", Class.getFieldList()); printTypeIndex("FieldList", Class.getFieldList());
printTypeIndex("DerivedFrom", Class.getDerivationList()); printTypeIndex("DerivedFrom", Class.getDerivationList());
printTypeIndex("VShape", Class.getVTableShape()); printTypeIndex("VShape", Class.getVTableShape());
W.printNumber("SizeOf", Class.getSize()); W->printNumber("SizeOf", Class.getSize());
W.printString("Name", Class.getName()); W->printString("Name", Class.getName());
if (Props & uint16_t(ClassOptions::HasUniqueName)) if (Props & uint16_t(ClassOptions::HasUniqueName))
W.printString("LinkageName", Class.getUniqueName()); W->printString("LinkageName", Class.getUniqueName());
Name = Class.getName(); Name = Class.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitUnion(UnionRecord &Union) { Error CVTypeDumper::visitUnion(UnionRecord &Union) {
uint16_t Props = static_cast<uint16_t>(Union.getOptions()); uint16_t Props = static_cast<uint16_t>(Union.getOptions());
W.printNumber("MemberCount", Union.getMemberCount()); W->printNumber("MemberCount", Union.getMemberCount());
W.printFlags("Properties", Props, makeArrayRef(ClassOptionNames)); W->printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
printTypeIndex("FieldList", Union.getFieldList()); printTypeIndex("FieldList", Union.getFieldList());
W.printNumber("SizeOf", Union.getSize()); W->printNumber("SizeOf", Union.getSize());
W.printString("Name", Union.getName()); W->printString("Name", Union.getName());
if (Props & uint16_t(ClassOptions::HasUniqueName)) if (Props & uint16_t(ClassOptions::HasUniqueName))
W.printString("LinkageName", Union.getUniqueName()); W->printString("LinkageName", Union.getUniqueName());
Name = Union.getName(); Name = Union.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitEnum(EnumRecord &Enum) { Error CVTypeDumper::visitEnum(EnumRecord &Enum) {
W.printNumber("NumEnumerators", Enum.getMemberCount()); W->printNumber("NumEnumerators", Enum.getMemberCount());
W.printFlags("Properties", uint16_t(Enum.getOptions()), W->printFlags("Properties", uint16_t(Enum.getOptions()),
makeArrayRef(ClassOptionNames)); makeArrayRef(ClassOptionNames));
printTypeIndex("UnderlyingType", Enum.getUnderlyingType()); printTypeIndex("UnderlyingType", Enum.getUnderlyingType());
printTypeIndex("FieldListType", Enum.getFieldList()); printTypeIndex("FieldListType", Enum.getFieldList());
W.printString("Name", Enum.getName()); W->printString("Name", Enum.getName());
Name = Enum.getName(); Name = Enum.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitArray(ArrayRecord &AT) { Error CVTypeDumper::visitArray(ArrayRecord &AT) {
printTypeIndex("ElementType", AT.getElementType()); printTypeIndex("ElementType", AT.getElementType());
printTypeIndex("IndexType", AT.getIndexType()); printTypeIndex("IndexType", AT.getIndexType());
W.printNumber("SizeOf", AT.getSize()); W->printNumber("SizeOf", AT.getSize());
W.printString("Name", AT.getName()); W->printString("Name", AT.getName());
Name = AT.getName(); Name = AT.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitVFTable(VFTableRecord &VFT) { Error CVTypeDumper::visitVFTable(VFTableRecord &VFT) {
printTypeIndex("CompleteClass", VFT.getCompleteClass()); printTypeIndex("CompleteClass", VFT.getCompleteClass());
printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable()); printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable());
W.printHex("VFPtrOffset", VFT.getVFPtrOffset()); W->printHex("VFPtrOffset", VFT.getVFPtrOffset());
W.printString("VFTableName", VFT.getName()); W->printString("VFTableName", VFT.getName());
for (auto N : VFT.getMethodNames()) for (auto N : VFT.getMethodNames())
W.printString("MethodName", N); W->printString("MethodName", N);
Name = VFT.getName(); Name = VFT.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitMemberFuncId(MemberFuncIdRecord &Id) { Error CVTypeDumper::visitMemberFuncId(MemberFuncIdRecord &Id) {
printTypeIndex("ClassType", Id.getClassType()); printTypeIndex("ClassType", Id.getClassType());
printTypeIndex("FunctionType", Id.getFunctionType()); printTypeIndex("FunctionType", Id.getFunctionType());
W.printString("Name", Id.getName()); W->printString("Name", Id.getName());
Name = Id.getName(); Name = Id.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitProcedure(ProcedureRecord &Proc) { Error CVTypeDumper::visitProcedure(ProcedureRecord &Proc) {
printTypeIndex("ReturnType", Proc.getReturnType()); printTypeIndex("ReturnType", Proc.getReturnType());
W.printEnum("CallingConvention", uint8_t(Proc.getCallConv()), W->printEnum("CallingConvention", uint8_t(Proc.getCallConv()),
makeArrayRef(CallingConventions)); makeArrayRef(CallingConventions));
W.printFlags("FunctionOptions", uint8_t(Proc.getOptions()), W->printFlags("FunctionOptions", uint8_t(Proc.getOptions()),
makeArrayRef(FunctionOptionEnum)); makeArrayRef(FunctionOptionEnum));
W.printNumber("NumParameters", Proc.getParameterCount()); W->printNumber("NumParameters", Proc.getParameterCount());
printTypeIndex("ArgListType", Proc.getArgumentList()); printTypeIndex("ArgListType", Proc.getArgumentList());
StringRef ReturnTypeName = getTypeName(Proc.getReturnType()); StringRef ReturnTypeName = getTypeName(Proc.getReturnType());
@ -370,20 +333,21 @@ void CVTypeDumperImpl::visitProcedure(ProcedureRecord &Proc) {
SmallString<256> TypeName(ReturnTypeName); SmallString<256> TypeName(ReturnTypeName);
TypeName.push_back(' '); TypeName.push_back(' ');
TypeName.append(ArgListTypeName); TypeName.append(ArgListTypeName);
Name = CVTD.saveName(TypeName); Name = saveName(TypeName);
return Error::success();
} }
void CVTypeDumperImpl::visitMemberFunction(MemberFunctionRecord &MF) { Error CVTypeDumper::visitMemberFunction(MemberFunctionRecord &MF) {
printTypeIndex("ReturnType", MF.getReturnType()); printTypeIndex("ReturnType", MF.getReturnType());
printTypeIndex("ClassType", MF.getClassType()); printTypeIndex("ClassType", MF.getClassType());
printTypeIndex("ThisType", MF.getThisType()); printTypeIndex("ThisType", MF.getThisType());
W.printEnum("CallingConvention", uint8_t(MF.getCallConv()), W->printEnum("CallingConvention", uint8_t(MF.getCallConv()),
makeArrayRef(CallingConventions)); makeArrayRef(CallingConventions));
W.printFlags("FunctionOptions", uint8_t(MF.getOptions()), W->printFlags("FunctionOptions", uint8_t(MF.getOptions()),
makeArrayRef(FunctionOptionEnum)); makeArrayRef(FunctionOptionEnum));
W.printNumber("NumParameters", MF.getParameterCount()); W->printNumber("NumParameters", MF.getParameterCount());
printTypeIndex("ArgListType", MF.getArgumentList()); printTypeIndex("ArgListType", MF.getArgumentList());
W.printNumber("ThisAdjustment", MF.getThisPointerAdjustment()); W->printNumber("ThisAdjustment", MF.getThisPointerAdjustment());
StringRef ReturnTypeName = getTypeName(MF.getReturnType()); StringRef ReturnTypeName = getTypeName(MF.getReturnType());
StringRef ClassTypeName = getTypeName(MF.getClassType()); StringRef ClassTypeName = getTypeName(MF.getClassType());
@ -393,52 +357,56 @@ void CVTypeDumperImpl::visitMemberFunction(MemberFunctionRecord &MF) {
TypeName.append(ClassTypeName); TypeName.append(ClassTypeName);
TypeName.append("::"); TypeName.append("::");
TypeName.append(ArgListTypeName); TypeName.append(ArgListTypeName);
Name = CVTD.saveName(TypeName); Name = saveName(TypeName);
return Error::success();
} }
void CVTypeDumperImpl::visitMethodOverloadList( Error CVTypeDumper::visitMethodOverloadList(
MethodOverloadListRecord &MethodList) { MethodOverloadListRecord &MethodList) {
for (auto &M : MethodList.getMethods()) { for (auto &M : MethodList.getMethods()) {
ListScope S(W, "Method"); ListScope S(*W, "Method");
printMemberAttributes(M.getAccess(), M.getKind(), M.getOptions()); printMemberAttributes(M.getAccess(), M.getKind(), M.getOptions());
printTypeIndex("Type", M.getType()); printTypeIndex("Type", M.getType());
if (M.isIntroducingVirtual()) if (M.isIntroducingVirtual())
W.printHex("VFTableOffset", M.getVFTableOffset()); W->printHex("VFTableOffset", M.getVFTableOffset());
} }
return Error::success();
} }
void CVTypeDumperImpl::visitFuncId(FuncIdRecord &Func) { Error CVTypeDumper::visitFuncId(FuncIdRecord &Func) {
printTypeIndex("ParentScope", Func.getParentScope()); printTypeIndex("ParentScope", Func.getParentScope());
printTypeIndex("FunctionType", Func.getFunctionType()); printTypeIndex("FunctionType", Func.getFunctionType());
W.printString("Name", Func.getName()); W->printString("Name", Func.getName());
Name = Func.getName(); Name = Func.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitTypeServer2(TypeServer2Record &TS) { Error CVTypeDumper::visitTypeServer2(TypeServer2Record &TS) {
W.printBinary("Signature", TS.getGuid()); W->printBinary("Signature", TS.getGuid());
W.printNumber("Age", TS.getAge()); W->printNumber("Age", TS.getAge());
W.printString("Name", TS.getName()); W->printString("Name", TS.getName());
Name = TS.getName(); Name = TS.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitPointer(PointerRecord &Ptr) { Error CVTypeDumper::visitPointer(PointerRecord &Ptr) {
printTypeIndex("PointeeType", Ptr.getReferentType()); printTypeIndex("PointeeType", Ptr.getReferentType());
W.printHex("PointerAttributes", uint32_t(Ptr.getOptions())); W->printHex("PointerAttributes", uint32_t(Ptr.getOptions()));
W.printEnum("PtrType", unsigned(Ptr.getPointerKind()), W->printEnum("PtrType", unsigned(Ptr.getPointerKind()),
makeArrayRef(PtrKindNames)); makeArrayRef(PtrKindNames));
W.printEnum("PtrMode", unsigned(Ptr.getMode()), makeArrayRef(PtrModeNames)); W->printEnum("PtrMode", unsigned(Ptr.getMode()), makeArrayRef(PtrModeNames));
W.printNumber("IsFlat", Ptr.isFlat()); W->printNumber("IsFlat", Ptr.isFlat());
W.printNumber("IsConst", Ptr.isConst()); W->printNumber("IsConst", Ptr.isConst());
W.printNumber("IsVolatile", Ptr.isVolatile()); W->printNumber("IsVolatile", Ptr.isVolatile());
W.printNumber("IsUnaligned", Ptr.isUnaligned()); W->printNumber("IsUnaligned", Ptr.isUnaligned());
if (Ptr.isPointerToMember()) { if (Ptr.isPointerToMember()) {
const MemberPointerInfo &MI = Ptr.getMemberInfo(); const MemberPointerInfo &MI = Ptr.getMemberInfo();
printTypeIndex("ClassType", MI.getContainingType()); printTypeIndex("ClassType", MI.getContainingType());
W.printEnum("Representation", uint16_t(MI.getRepresentation()), W->printEnum("Representation", uint16_t(MI.getRepresentation()),
makeArrayRef(PtrMemberRepNames)); makeArrayRef(PtrMemberRepNames));
StringRef PointeeName = getTypeName(Ptr.getReferentType()); StringRef PointeeName = getTypeName(Ptr.getReferentType());
StringRef ClassName = getTypeName(MI.getContainingType()); StringRef ClassName = getTypeName(MI.getContainingType());
@ -446,7 +414,7 @@ void CVTypeDumperImpl::visitPointer(PointerRecord &Ptr) {
TypeName.push_back(' '); TypeName.push_back(' ');
TypeName.append(ClassName); TypeName.append(ClassName);
TypeName.append("::*"); TypeName.append("::*");
Name = CVTD.saveName(TypeName); Name = saveName(TypeName);
} else { } else {
SmallString<256> TypeName; SmallString<256> TypeName;
if (Ptr.isConst()) if (Ptr.isConst())
@ -466,14 +434,15 @@ void CVTypeDumperImpl::visitPointer(PointerRecord &Ptr) {
TypeName.append("*"); TypeName.append("*");
if (!TypeName.empty()) if (!TypeName.empty())
Name = CVTD.saveName(TypeName); Name = saveName(TypeName);
} }
return Error::success();
} }
void CVTypeDumperImpl::visitModifier(ModifierRecord &Mod) { Error CVTypeDumper::visitModifier(ModifierRecord &Mod) {
uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers()); uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers());
printTypeIndex("ModifiedType", Mod.getModifiedType()); printTypeIndex("ModifiedType", Mod.getModifiedType());
W.printFlags("Modifiers", Mods, makeArrayRef(TypeModifierNames)); W->printFlags("Modifiers", Mods, makeArrayRef(TypeModifierNames));
StringRef ModifiedName = getTypeName(Mod.getModifiedType()); StringRef ModifiedName = getTypeName(Mod.getModifiedType());
SmallString<256> TypeName; SmallString<256> TypeName;
@ -484,146 +453,162 @@ void CVTypeDumperImpl::visitModifier(ModifierRecord &Mod) {
if (Mods & uint16_t(ModifierOptions::Unaligned)) if (Mods & uint16_t(ModifierOptions::Unaligned))
TypeName.append("__unaligned "); TypeName.append("__unaligned ");
TypeName.append(ModifiedName); TypeName.append(ModifiedName);
Name = CVTD.saveName(TypeName); Name = saveName(TypeName);
return Error::success();
} }
void CVTypeDumperImpl::visitBitField(BitFieldRecord &BitField) { Error CVTypeDumper::visitBitField(BitFieldRecord &BitField) {
printTypeIndex("Type", BitField.getType()); printTypeIndex("Type", BitField.getType());
W.printNumber("BitSize", BitField.getBitSize()); W->printNumber("BitSize", BitField.getBitSize());
W.printNumber("BitOffset", BitField.getBitOffset()); W->printNumber("BitOffset", BitField.getBitOffset());
return Error::success();
} }
void CVTypeDumperImpl::visitVFTableShape(VFTableShapeRecord &Shape) { Error CVTypeDumper::visitVFTableShape(VFTableShapeRecord &Shape) {
W.printNumber("VFEntryCount", Shape.getEntryCount()); W->printNumber("VFEntryCount", Shape.getEntryCount());
return Error::success();
} }
void CVTypeDumperImpl::visitUdtSourceLine(UdtSourceLineRecord &Line) { Error CVTypeDumper::visitUdtSourceLine(UdtSourceLineRecord &Line) {
printTypeIndex("UDT", Line.getUDT()); printTypeIndex("UDT", Line.getUDT());
printTypeIndex("SourceFile", Line.getSourceFile()); printTypeIndex("SourceFile", Line.getSourceFile());
W.printNumber("LineNumber", Line.getLineNumber()); W->printNumber("LineNumber", Line.getLineNumber());
return Error::success();
} }
void CVTypeDumperImpl::visitUdtModSourceLine(UdtModSourceLineRecord &Line) { Error CVTypeDumper::visitUdtModSourceLine(UdtModSourceLineRecord &Line) {
printTypeIndex("UDT", Line.getUDT()); printTypeIndex("UDT", Line.getUDT());
printTypeIndex("SourceFile", Line.getSourceFile()); printTypeIndex("SourceFile", Line.getSourceFile());
W.printNumber("LineNumber", Line.getLineNumber()); W->printNumber("LineNumber", Line.getLineNumber());
W.printNumber("Module", Line.getModule()); W->printNumber("Module", Line.getModule());
return Error::success();
} }
void CVTypeDumperImpl::visitBuildInfo(BuildInfoRecord &Args) { Error CVTypeDumper::visitBuildInfo(BuildInfoRecord &Args) {
W.printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size())); W->printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size()));
ListScope Arguments(W, "Arguments"); ListScope Arguments(*W, "Arguments");
for (auto Arg : Args.getArgs()) { for (auto Arg : Args.getArgs()) {
printTypeIndex("ArgType", Arg); printTypeIndex("ArgType", Arg);
} }
return Error::success();
} }
void CVTypeDumperImpl::printMemberAttributes(MemberAttributes Attrs) { void CVTypeDumper::printMemberAttributes(MemberAttributes Attrs) {
return printMemberAttributes(Attrs.getAccess(), Attrs.getMethodKind(), return printMemberAttributes(Attrs.getAccess(), Attrs.getMethodKind(),
Attrs.getFlags()); Attrs.getFlags());
} }
void CVTypeDumperImpl::printMemberAttributes(MemberAccess Access, void CVTypeDumper::printMemberAttributes(MemberAccess Access, MethodKind Kind,
MethodKind Kind, MethodOptions Options) {
MethodOptions Options) { W->printEnum("AccessSpecifier", uint8_t(Access),
W.printEnum("AccessSpecifier", uint8_t(Access), makeArrayRef(MemberAccessNames));
makeArrayRef(MemberAccessNames));
// Data members will be vanilla. Don't try to print a method kind for them. // Data members will be vanilla. Don't try to print a method kind for them.
if (Kind != MethodKind::Vanilla) if (Kind != MethodKind::Vanilla)
W.printEnum("MethodKind", unsigned(Kind), makeArrayRef(MemberKindNames)); W->printEnum("MethodKind", unsigned(Kind), makeArrayRef(MemberKindNames));
if (Options != MethodOptions::None) { if (Options != MethodOptions::None) {
W.printFlags("MethodOptions", unsigned(Options), W->printFlags("MethodOptions", unsigned(Options),
makeArrayRef(MethodOptionNames)); makeArrayRef(MethodOptionNames));
} }
} }
void CVTypeDumperImpl::visitUnknownMember(TypeLeafKind Leaf) { Error CVTypeDumper::visitUnknownMember(const CVRecord<TypeLeafKind> &Record) {
W.printHex("UnknownMember", unsigned(Leaf)); W->printHex("UnknownMember", unsigned(Record.Type));
return Error::success();
} }
void CVTypeDumperImpl::visitUnknownType(const CVRecord<TypeLeafKind> &Rec) { Error CVTypeDumper::visitUnknownType(const CVRecord<TypeLeafKind> &Record) {
DictScope S(W, "UnknownType"); DictScope S(*W, "UnknownType");
W.printEnum("Kind", uint16_t(Rec.Type), makeArrayRef(LeafTypeNames)); W->printEnum("Kind", uint16_t(Record.Type), makeArrayRef(LeafTypeNames));
W.printNumber("Length", uint32_t(Rec.Data.size())); W->printNumber("Length", uint32_t(Record.Data.size()));
return Error::success();
} }
void CVTypeDumperImpl::visitNestedType(NestedTypeRecord &Nested) { Error CVTypeDumper::visitNestedType(NestedTypeRecord &Nested) {
DictScope S(W, "NestedType"); DictScope S(*W, "NestedType");
printTypeIndex("Type", Nested.getNestedType()); printTypeIndex("Type", Nested.getNestedType());
W.printString("Name", Nested.getName()); W->printString("Name", Nested.getName());
Name = Nested.getName(); Name = Nested.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitOneMethod(OneMethodRecord &Method) { Error CVTypeDumper::visitOneMethod(OneMethodRecord &Method) {
DictScope S(W, "OneMethod"); DictScope S(*W, "OneMethod");
MethodKind K = Method.getKind(); MethodKind K = Method.getKind();
printMemberAttributes(Method.getAccess(), K, Method.getOptions()); printMemberAttributes(Method.getAccess(), K, Method.getOptions());
printTypeIndex("Type", Method.getType()); printTypeIndex("Type", Method.getType());
// If virtual, then read the vftable offset. // If virtual, then read the vftable offset.
if (Method.isIntroducingVirtual()) if (Method.isIntroducingVirtual())
W.printHex("VFTableOffset", Method.getVFTableOffset()); W->printHex("VFTableOffset", Method.getVFTableOffset());
W.printString("Name", Method.getName()); W->printString("Name", Method.getName());
Name = Method.getName(); Name = Method.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitOverloadedMethod(OverloadedMethodRecord &Method) { Error CVTypeDumper::visitOverloadedMethod(OverloadedMethodRecord &Method) {
DictScope S(W, "OverloadedMethod"); DictScope S(*W, "OverloadedMethod");
W.printHex("MethodCount", Method.getNumOverloads()); W->printHex("MethodCount", Method.getNumOverloads());
printTypeIndex("MethodListIndex", Method.getMethodList()); printTypeIndex("MethodListIndex", Method.getMethodList());
W.printString("Name", Method.getName()); W->printString("Name", Method.getName());
Name = Method.getName(); Name = Method.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitDataMember(DataMemberRecord &Field) { Error CVTypeDumper::visitDataMember(DataMemberRecord &Field) {
DictScope S(W, "DataMember"); DictScope S(*W, "DataMember");
printMemberAttributes(Field.getAccess(), MethodKind::Vanilla, printMemberAttributes(Field.getAccess(), MethodKind::Vanilla,
MethodOptions::None); MethodOptions::None);
printTypeIndex("Type", Field.getType()); printTypeIndex("Type", Field.getType());
W.printHex("FieldOffset", Field.getFieldOffset()); W->printHex("FieldOffset", Field.getFieldOffset());
W.printString("Name", Field.getName()); W->printString("Name", Field.getName());
Name = Field.getName(); Name = Field.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitStaticDataMember(StaticDataMemberRecord &Field) { Error CVTypeDumper::visitStaticDataMember(StaticDataMemberRecord &Field) {
DictScope S(W, "StaticDataMember"); DictScope S(*W, "StaticDataMember");
printMemberAttributes(Field.getAccess(), MethodKind::Vanilla, printMemberAttributes(Field.getAccess(), MethodKind::Vanilla,
MethodOptions::None); MethodOptions::None);
printTypeIndex("Type", Field.getType()); printTypeIndex("Type", Field.getType());
W.printString("Name", Field.getName()); W->printString("Name", Field.getName());
Name = Field.getName(); Name = Field.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitVFPtr(VFPtrRecord &VFTable) { Error CVTypeDumper::visitVFPtr(VFPtrRecord &VFTable) {
DictScope S(W, "VFPtr"); DictScope S(*W, "VFPtr");
printTypeIndex("Type", VFTable.getType()); printTypeIndex("Type", VFTable.getType());
return Error::success();
} }
void CVTypeDumperImpl::visitEnumerator(EnumeratorRecord &Enum) { Error CVTypeDumper::visitEnumerator(EnumeratorRecord &Enum) {
DictScope S(W, "Enumerator"); DictScope S(*W, "Enumerator");
printMemberAttributes(Enum.getAccess(), MethodKind::Vanilla, printMemberAttributes(Enum.getAccess(), MethodKind::Vanilla,
MethodOptions::None); MethodOptions::None);
W.printNumber("EnumValue", Enum.getValue()); W->printNumber("EnumValue", Enum.getValue());
W.printString("Name", Enum.getName()); W->printString("Name", Enum.getName());
Name = Enum.getName(); Name = Enum.getName();
return Error::success();
} }
void CVTypeDumperImpl::visitBaseClass(BaseClassRecord &Base) { Error CVTypeDumper::visitBaseClass(BaseClassRecord &Base) {
DictScope S(W, "BaseClass"); DictScope S(*W, "BaseClass");
printMemberAttributes(Base.getAccess(), MethodKind::Vanilla, printMemberAttributes(Base.getAccess(), MethodKind::Vanilla,
MethodOptions::None); MethodOptions::None);
printTypeIndex("BaseType", Base.getBaseType()); printTypeIndex("BaseType", Base.getBaseType());
W.printHex("BaseOffset", Base.getBaseOffset()); W->printHex("BaseOffset", Base.getBaseOffset());
return Error::success();
} }
void CVTypeDumperImpl::visitVirtualBaseClass(VirtualBaseClassRecord &Base) { Error CVTypeDumper::visitVirtualBaseClass(VirtualBaseClassRecord &Base) {
DictScope S(W, "VirtualBaseClass"); DictScope S(*W, "VirtualBaseClass");
printMemberAttributes(Base.getAccess(), MethodKind::Vanilla, printMemberAttributes(Base.getAccess(), MethodKind::Vanilla,
MethodOptions::None); MethodOptions::None);
printTypeIndex("BaseType", Base.getBaseType()); printTypeIndex("BaseType", Base.getBaseType());
printTypeIndex("VBPtrType", Base.getVBPtrType()); printTypeIndex("VBPtrType", Base.getVBPtrType());
W.printHex("VBPtrOffset", Base.getVBPtrOffset()); W->printHex("VBPtrOffset", Base.getVBPtrOffset());
W.printHex("VBTableIndex", Base.getVTableIndex()); W->printHex("VBTableIndex", Base.getVTableIndex());
return Error::success();
} }
StringRef CVTypeDumper::getTypeName(TypeIndex TI) { StringRef CVTypeDumper::getTypeName(TypeIndex TI) {
@ -663,28 +648,29 @@ void CVTypeDumper::printTypeIndex(StringRef FieldName, TypeIndex TI) {
W->printHex(FieldName, TI.getIndex()); W->printHex(FieldName, TI.getIndex());
} }
bool CVTypeDumper::dump(const CVRecord<TypeLeafKind> &Record) { Error CVTypeDumper::dump(const CVRecord<TypeLeafKind> &Record) {
assert(W && "printer should not be null"); assert(W && "printer should not be null");
CVTypeDumperImpl Dumper(*this, *W, PrintRecordBytes); CVTypeVisitor Visitor(*this);
Dumper.visitTypeRecord(Record);
return !Dumper.hadError(); if (auto EC = Visitor.visitTypeRecord(Record))
return EC;
return Error::success();
} }
bool CVTypeDumper::dump(const CVTypeArray &Types) { Error CVTypeDumper::dump(const CVTypeArray &Types) {
assert(W && "printer should not be null"); assert(W && "printer should not be null");
CVTypeDumperImpl Dumper(*this, *W, PrintRecordBytes); CVTypeVisitor Visitor(*this);
Dumper.visitTypeStream(Types); if (auto EC = Visitor.visitTypeStream(Types))
return !Dumper.hadError(); return EC;
return Error::success();
} }
bool CVTypeDumper::dump(ArrayRef<uint8_t> Data) { Error CVTypeDumper::dump(ArrayRef<uint8_t> Data) {
ByteStream<> Stream(Data); ByteStream<> Stream(Data);
CVTypeArray Types; CVTypeArray Types;
StreamReader Reader(Stream); StreamReader Reader(Stream);
if (auto EC = Reader.readArray(Types, Reader.getLength())) { if (auto EC = Reader.readArray(Types, Reader.getLength()))
consumeError(std::move(EC)); return EC;
return false;
}
return dump(Types); return dump(Types);
} }

View File

@ -15,6 +15,8 @@
#include "llvm/DebugInfo/CodeView/StreamRef.h" #include "llvm/DebugInfo/CodeView/StreamRef.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/ScopedPrinter.h"
using namespace llvm; using namespace llvm;
@ -49,32 +51,32 @@ namespace {
/// - If the type record already exists in the destination stream, discard it /// - If the type record already exists in the destination stream, discard it
/// and update the type index map to forward the source type index to the /// and update the type index map to forward the source type index to the
/// existing destination type index. /// existing destination type index.
class TypeStreamMerger : public CVTypeVisitor<TypeStreamMerger> { class TypeStreamMerger : public TypeVisitorCallbacks {
public: public:
TypeStreamMerger(TypeTableBuilder &DestStream) : DestStream(DestStream) { TypeStreamMerger(TypeTableBuilder &DestStream) : DestStream(DestStream) {
assert(!hadError()); assert(!hadError());
} }
/// CVTypeVisitor overrides. /// TypeVisitorCallbacks overrides.
#define TYPE_RECORD(EnumName, EnumVal, Name) \ #define TYPE_RECORD(EnumName, EnumVal, Name) \
void visit##Name(Name##Record &Record); Error visit##Name(Name##Record &Record) override;
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name) \ #define MEMBER_RECORD(EnumName, EnumVal, Name) \
void visit##Name(Name##Record &Record); TYPE_RECORD(EnumName, EnumVal, Name)
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/TypeRecords.def" #include "llvm/DebugInfo/CodeView/TypeRecords.def"
void visitUnknownType(const CVRecord<TypeLeafKind> &Record); Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) override;
void visitTypeBegin(const CVRecord<TypeLeafKind> &Record); Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) override;
void visitTypeEnd(const CVRecord<TypeLeafKind> &Record); Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override;
void visitFieldList(TypeLeafKind Leaf, ArrayRef<uint8_t> FieldData); Error visitFieldListEnd(const CVRecord<TypeLeafKind> &Record) override;
bool mergeStream(const CVTypeArray &Types); bool mergeStream(const CVTypeArray &Types);
private: private:
bool hadError() { return FoundBadTypeIndex || CVTypeVisitor::hadError(); } bool hadError() { return FoundBadTypeIndex; }
bool FoundBadTypeIndex = false; bool FoundBadTypeIndex = false;
@ -91,45 +93,52 @@ private:
} // end anonymous namespace } // end anonymous namespace
void TypeStreamMerger::visitTypeBegin(const CVRecord<TypeLeafKind> &Rec) { Error TypeStreamMerger::visitTypeBegin(const CVRecord<TypeLeafKind> &Rec) {
BeginIndexMapSize = IndexMap.size(); BeginIndexMapSize = IndexMap.size();
return Error::success();
} }
void TypeStreamMerger::visitTypeEnd(const CVRecord<TypeLeafKind> &Rec) { Error TypeStreamMerger::visitTypeEnd(const CVRecord<TypeLeafKind> &Rec) {
assert(IndexMap.size() == BeginIndexMapSize + 1); assert(IndexMap.size() == BeginIndexMapSize + 1);
return Error::success();
} }
void TypeStreamMerger::visitFieldList(TypeLeafKind Leaf, Error TypeStreamMerger::visitFieldListEnd(const CVRecord<TypeLeafKind> &Rec) {
ArrayRef<uint8_t> FieldData) {
CVTypeVisitor::visitFieldList(Leaf, FieldData);
IndexMap.push_back(DestStream.writeFieldList(FieldBuilder)); IndexMap.push_back(DestStream.writeFieldList(FieldBuilder));
FieldBuilder.reset(); FieldBuilder.reset();
return Error::success();
} }
#define TYPE_RECORD(EnumName, EnumVal, Name) \ #define TYPE_RECORD(EnumName, EnumVal, Name) \
void TypeStreamMerger::visit##Name(Name##Record &Record) { \ Error TypeStreamMerger::visit##Name(Name##Record &Record) { \
FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \ FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \
IndexMap.push_back(DestStream.write##Name(Record)); \ IndexMap.push_back(DestStream.write##Name(Record)); \
return Error::success(); \
} }
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name) \ #define MEMBER_RECORD(EnumName, EnumVal, Name) \
void TypeStreamMerger::visit##Name(Name##Record &Record) { \ Error TypeStreamMerger::visit##Name(Name##Record &Record) { \
FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \ FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \
FieldBuilder.write##Name(Record); \ FieldBuilder.write##Name(Record); \
return Error::success(); \
} }
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/TypeRecords.def" #include "llvm/DebugInfo/CodeView/TypeRecords.def"
void TypeStreamMerger::visitUnknownType(const CVRecord<TypeLeafKind> &Rec) { Error TypeStreamMerger::visitUnknownType(const CVRecord<TypeLeafKind> &Rec) {
// We failed to translate a type. Translate this index as "not translated". // We failed to translate a type. Translate this index as "not translated".
IndexMap.push_back( IndexMap.push_back(
TypeIndex(SimpleTypeKind::NotTranslated, SimpleTypeMode::Direct)); TypeIndex(SimpleTypeKind::NotTranslated, SimpleTypeMode::Direct));
parseError(); return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record);
} }
bool TypeStreamMerger::mergeStream(const CVTypeArray &Types) { bool TypeStreamMerger::mergeStream(const CVTypeArray &Types) {
assert(IndexMap.empty()); assert(IndexMap.empty());
visitTypeStream(Types); CVTypeVisitor Visitor(*this);
if (auto EC = Visitor.visitTypeStream(Types)) {
consumeError(std::move(EC));
return false;
}
IndexMap.clear(); IndexMap.clear();
return !hadError(); return !hadError();
} }

View File

@ -32,6 +32,8 @@ public:
return "The specified block address is not valid."; return "The specified block address is not valid.";
case raw_error_code::not_writable: case raw_error_code::not_writable:
return "The PDB does not support writing."; return "The PDB does not support writing.";
case raw_error_code::invalid_tpi_hash:
return "The Type record has an invalid hash value.";
} }
llvm_unreachable("Unrecognized raw_error_code"); llvm_unreachable("Unrecognized raw_error_code");
} }

View File

@ -84,39 +84,44 @@ template <typename T> static uint32_t getTpiHash(T &Rec) {
} }
namespace { namespace {
class TpiHashVerifier : public CVTypeVisitor<TpiHashVerifier> { class TpiHashVerifier : public TypeVisitorCallbacks {
public: public:
TpiHashVerifier(FixedStreamArray<support::ulittle32_t> &HashValues, TpiHashVerifier(FixedStreamArray<support::ulittle32_t> &HashValues,
uint32_t NumHashBuckets) uint32_t NumHashBuckets)
: HashValues(HashValues), NumHashBuckets(NumHashBuckets) {} : HashValues(HashValues), NumHashBuckets(NumHashBuckets) {}
void visitUdtSourceLine(UdtSourceLineRecord &Rec) { verifySourceLine(Rec); } Error visitUdtSourceLine(UdtSourceLineRecord &Rec) override {
return verifySourceLine(Rec);
void visitUdtModSourceLine(UdtModSourceLineRecord &Rec) {
verifySourceLine(Rec);
} }
void visitClass(ClassRecord &Rec) { verify(Rec); } Error visitUdtModSourceLine(UdtModSourceLineRecord &Rec) override {
void visitEnum(EnumRecord &Rec) { verify(Rec); } return verifySourceLine(Rec);
void visitInterface(ClassRecord &Rec) { verify(Rec); } }
void visitStruct(ClassRecord &Rec) { verify(Rec); }
void visitUnion(UnionRecord &Rec) { verify(Rec); }
void visitTypeEnd(const CVRecord<TypeLeafKind> &Record) { ++Index; } Error visitClass(ClassRecord &Rec) override { return verify(Rec); }
Error visitEnum(EnumRecord &Rec) override { return verify(Rec); }
Error visitUnion(UnionRecord &Rec) override { return verify(Rec); }
Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override {
++Index;
return Error::success();
}
private: private:
template <typename T> void verify(T &Rec) { template <typename T> Error verify(T &Rec) {
uint32_t Hash = getTpiHash(Rec); uint32_t Hash = getTpiHash(Rec);
if (Hash && Hash % NumHashBuckets != HashValues[Index]) if (Hash && Hash % NumHashBuckets != HashValues[Index])
parseError(); return make_error<RawError>(raw_error_code::invalid_tpi_hash);
return Error::success();
} }
template <typename T> void verifySourceLine(T &Rec) { template <typename T> Error verifySourceLine(T &Rec) {
char Buf[4]; char Buf[4];
support::endian::write32le(Buf, Rec.getUDT().getIndex()); support::endian::write32le(Buf, Rec.getUDT().getIndex());
uint32_t Hash = hashStringV1(StringRef(Buf, 4)); uint32_t Hash = hashStringV1(StringRef(Buf, 4));
if (Hash % NumHashBuckets != HashValues[Index]) if (Hash % NumHashBuckets != HashValues[Index])
parseError(); return make_error<RawError>(raw_error_code::invalid_tpi_hash);
return Error::success();
} }
FixedStreamArray<support::ulittle32_t> HashValues; FixedStreamArray<support::ulittle32_t> HashValues;
@ -129,11 +134,8 @@ private:
// Currently we only verify SRC_LINE records. // Currently we only verify SRC_LINE records.
Error TpiStream::verifyHashValues() { Error TpiStream::verifyHashValues() {
TpiHashVerifier Verifier(HashValues, Header->NumHashBuckets); TpiHashVerifier Verifier(HashValues, Header->NumHashBuckets);
Verifier.visitTypeStream(TypeRecords); CVTypeVisitor Visitor(Verifier);
if (Verifier.hadError()) return Visitor.visitTypeStream(TypeRecords);
return make_error<RawError>(raw_error_code::corrupt_file,
"Corrupt TPI hash table.");
return Error::success();
} }
Error TpiStream::reload() { Error TpiStream::reload() {

View File

@ -328,8 +328,10 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
for (auto &Type : Tpi->types(&HadError)) { for (auto &Type : Tpi->types(&HadError)) {
DictScope DD(P, ""); DictScope DD(P, "");
if (DumpRecords) if (DumpRecords) {
TD.dump(Type); if (auto EC = TD.dump(Type))
return EC;
}
if (DumpRecordBytes) if (DumpRecordBytes)
P.printBinaryBlock("Bytes", Type.Data); P.printBinaryBlock("Bytes", Type.Data);
@ -347,8 +349,10 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
TD.setPrinter(nullptr); TD.setPrinter(nullptr);
bool HadError = false; bool HadError = false;
for (auto &Type : Tpi->types(&HadError)) for (auto &Type : Tpi->types(&HadError)) {
TD.dump(Type); if (auto EC = TD.dump(Type))
return EC;
}
TD.setPrinter(OldP); TD.setPrinter(OldP);
dumpTpiHash(P, *Tpi); dumpTpiHash(P, *Tpi);

View File

@ -1108,9 +1108,9 @@ void COFFDumper::printCodeViewTypeSection(StringRef SectionName,
if (Magic != COFF::DEBUG_SECTION_MAGIC) if (Magic != COFF::DEBUG_SECTION_MAGIC)
return error(object_error::parse_failed); return error(object_error::parse_failed);
if (!CVTD.dump({Data.bytes_begin(), Data.bytes_end()})) { if (auto EC = CVTD.dump({Data.bytes_begin(), Data.bytes_end()})) {
W.flush(); W.flush();
error(object_error::parse_failed); error(llvm::errorToErrorCode(std::move(EC)));
} }
} }
@ -1555,8 +1555,8 @@ void llvm::dumpCodeViewMergedTypes(
Buf.append(Record.begin(), Record.end()); Buf.append(Record.begin(), Record.end());
}); });
CVTypeDumper CVTD(&Writer, opts::CodeViewSubsectionBytes); CVTypeDumper CVTD(&Writer, opts::CodeViewSubsectionBytes);
if (!CVTD.dump({Buf.str().bytes_begin(), Buf.str().bytes_end()})) { if (auto EC = CVTD.dump({Buf.str().bytes_begin(), Buf.str().bytes_end()})) {
Writer.flush(); Writer.flush();
error(object_error::parse_failed); error(llvm::errorToErrorCode(std::move(EC)));
} }
} }