blob: dd6f75f97a4ac7433e6a08bd257a52af2437d8a9 [file] [log] [blame]
//===- CVTypeVisitor.cpp ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/CodeViewError.h"
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/BinaryStreamReader.h"
using namespace llvm;
using namespace llvm::codeview;
template <typename T>
static Error visitKnownRecord(CVType &Record, TypeVisitorCallbacks &Callbacks) {
TypeRecordKind RK = static_cast<TypeRecordKind>(Record.kind());
T KnownRecord(RK);
if (auto EC = Callbacks.visitKnownRecord(Record, KnownRecord))
return EC;
return Error::success();
}
template <typename T>
static Error visitKnownMember(CVMemberRecord &Record,
TypeVisitorCallbacks &Callbacks) {
TypeRecordKind RK = static_cast<TypeRecordKind>(Record.Kind);
T KnownRecord(RK);
if (auto EC = Callbacks.visitKnownMember(Record, KnownRecord))
return EC;
return Error::success();
}
static Error visitMemberRecord(CVMemberRecord &Record,
TypeVisitorCallbacks &Callbacks) {
if (auto EC = Callbacks.visitMemberBegin(Record))
return EC;
switch (Record.Kind) {
default:
if (auto EC = Callbacks.visitUnknownMember(Record))
return EC;
break;
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
case EnumName: { \
if (auto EC = visitKnownMember<Name##Record>(Record, Callbacks)) \
return EC; \
break; \
}
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
MEMBER_RECORD(EnumVal, EnumVal, AliasName)
#define TYPE_RECORD(EnumName, EnumVal, Name)
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
}
if (auto EC = Callbacks.visitMemberEnd(Record))
return EC;
return Error::success();
}
namespace {
class CVTypeVisitor {
public:
explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks);
Error visitTypeRecord(CVType &Record, TypeIndex Index);
Error visitTypeRecord(CVType &Record);
/// Visits the type records in Data. Sets the error flag on parse failures.
Error visitTypeStream(const CVTypeArray &Types);
Error visitTypeStream(CVTypeRange Types);
Error visitTypeStream(TypeCollection &Types);
Error visitMemberRecord(CVMemberRecord Record);
Error visitFieldListMemberStream(BinaryStreamReader &Stream);
private:
Error finishVisitation(CVType &Record);
/// The interface to the class that gets notified of each visitation.
TypeVisitorCallbacks &Callbacks;
};
CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
: Callbacks(Callbacks) {}
Error CVTypeVisitor::finishVisitation(CVType &Record) {
switch (Record.kind()) {
default:
if (auto EC = Callbacks.visitUnknownType(Record))
return EC;
break;
#define TYPE_RECORD(EnumName, EnumVal, Name) \
case EnumName: { \
if (auto EC = visitKnownRecord<Name##Record>(Record, Callbacks)) \
return EC; \
break; \
}
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
TYPE_RECORD(EnumVal, EnumVal, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name)
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
}
if (auto EC = Callbacks.visitTypeEnd(Record))
return EC;
return Error::success();
}
Error CVTypeVisitor::visitTypeRecord(CVType &Record, TypeIndex Index) {
if (auto EC = Callbacks.visitTypeBegin(Record, Index))
return EC;
return finishVisitation(Record);
}
Error CVTypeVisitor::visitTypeRecord(CVType &Record) {
if (auto EC = Callbacks.visitTypeBegin(Record))
return EC;
return finishVisitation(Record);
}
Error CVTypeVisitor::visitMemberRecord(CVMemberRecord Record) {
return ::visitMemberRecord(Record, Callbacks);
}
/// Visits the type records in Data. Sets the error flag on parse failures.
Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) {
for (auto I : Types) {
if (auto EC = visitTypeRecord(I))
return EC;
}
return Error::success();
}
Error CVTypeVisitor::visitTypeStream(CVTypeRange Types) {
for (auto I : Types) {
if (auto EC = visitTypeRecord(I))
return EC;
}
return Error::success();
}
Error CVTypeVisitor::visitTypeStream(TypeCollection &Types) {
Optional<TypeIndex> I = Types.getFirst();
while (I) {
CVType Type = Types.getType(*I);
if (auto EC = visitTypeRecord(Type, *I))
return EC;
I = Types.getNext(*I);
}
return Error::success();
}
Error CVTypeVisitor::visitFieldListMemberStream(BinaryStreamReader &Reader) {
TypeLeafKind Leaf;
while (!Reader.empty()) {
if (auto EC = Reader.readEnum(Leaf))
return EC;
CVMemberRecord Record;
Record.Kind = Leaf;
if (auto EC = ::visitMemberRecord(Record, Callbacks))
return EC;
}
return Error::success();
}
struct FieldListVisitHelper {
FieldListVisitHelper(TypeVisitorCallbacks &Callbacks, ArrayRef<uint8_t> Data,
VisitorDataSource Source)
: Stream(Data, llvm::support::little), Reader(Stream),
Deserializer(Reader),
Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) {
if (Source == VDS_BytesPresent) {
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Callbacks);
}
}
BinaryByteStream Stream;
BinaryStreamReader Reader;
FieldListDeserializer Deserializer;
TypeVisitorCallbackPipeline Pipeline;
CVTypeVisitor Visitor;
};
struct VisitHelper {
VisitHelper(TypeVisitorCallbacks &Callbacks, VisitorDataSource Source)
: Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) {
if (Source == VDS_BytesPresent) {
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Callbacks);
}
}
TypeDeserializer Deserializer;
TypeVisitorCallbackPipeline Pipeline;
CVTypeVisitor Visitor;
};
}
Error llvm::codeview::visitTypeRecord(CVType &Record, TypeIndex Index,
TypeVisitorCallbacks &Callbacks,
VisitorDataSource Source) {
VisitHelper V(Callbacks, Source);
return V.Visitor.visitTypeRecord(Record, Index);
}
Error llvm::codeview::visitTypeRecord(CVType &Record,
TypeVisitorCallbacks &Callbacks,
VisitorDataSource Source) {
VisitHelper V(Callbacks, Source);
return V.Visitor.visitTypeRecord(Record);
}
Error llvm::codeview::visitTypeStream(const CVTypeArray &Types,
TypeVisitorCallbacks &Callbacks,
VisitorDataSource Source) {
VisitHelper V(Callbacks, Source);
return V.Visitor.visitTypeStream(Types);
}
Error llvm::codeview::visitTypeStream(CVTypeRange Types,
TypeVisitorCallbacks &Callbacks) {
VisitHelper V(Callbacks, VDS_BytesPresent);
return V.Visitor.visitTypeStream(Types);
}
Error llvm::codeview::visitTypeStream(TypeCollection &Types,
TypeVisitorCallbacks &Callbacks) {
// When the internal visitor calls Types.getType(Index) the interface is
// required to return a CVType with the bytes filled out. So we can assume
// that the bytes will be present when individual records are visited.
VisitHelper V(Callbacks, VDS_BytesPresent);
return V.Visitor.visitTypeStream(Types);
}
Error llvm::codeview::visitMemberRecord(CVMemberRecord Record,
TypeVisitorCallbacks &Callbacks,
VisitorDataSource Source) {
FieldListVisitHelper V(Callbacks, Record.Data, Source);
return V.Visitor.visitMemberRecord(Record);
}
Error llvm::codeview::visitMemberRecord(TypeLeafKind Kind,
ArrayRef<uint8_t> Record,
TypeVisitorCallbacks &Callbacks) {
CVMemberRecord R;
R.Data = Record;
R.Kind = Kind;
return visitMemberRecord(R, Callbacks, VDS_BytesPresent);
}
Error llvm::codeview::visitMemberRecordStream(ArrayRef<uint8_t> FieldList,
TypeVisitorCallbacks &Callbacks) {
FieldListVisitHelper V(Callbacks, FieldList, VDS_BytesPresent);
return V.Visitor.visitFieldListMemberStream(V.Reader);
}