CVTypeVisitor.cpp revision 360784
1//===- CVTypeVisitor.cpp ----------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
10
11#include "llvm/DebugInfo/CodeView/CodeViewError.h"
12#include "llvm/DebugInfo/CodeView/TypeCollection.h"
13#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
14#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
15#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
16#include "llvm/Support/BinaryByteStream.h"
17#include "llvm/Support/BinaryStreamReader.h"
18
19using namespace llvm;
20using namespace llvm::codeview;
21
22
23template <typename T>
24static Error visitKnownRecord(CVType &Record, TypeVisitorCallbacks &Callbacks) {
25  TypeRecordKind RK = static_cast<TypeRecordKind>(Record.kind());
26  T KnownRecord(RK);
27  if (auto EC = Callbacks.visitKnownRecord(Record, KnownRecord))
28    return EC;
29  return Error::success();
30}
31
32template <typename T>
33static Error visitKnownMember(CVMemberRecord &Record,
34                              TypeVisitorCallbacks &Callbacks) {
35  TypeRecordKind RK = static_cast<TypeRecordKind>(Record.Kind);
36  T KnownRecord(RK);
37  if (auto EC = Callbacks.visitKnownMember(Record, KnownRecord))
38    return EC;
39  return Error::success();
40}
41
42static Error visitMemberRecord(CVMemberRecord &Record,
43                               TypeVisitorCallbacks &Callbacks) {
44  if (auto EC = Callbacks.visitMemberBegin(Record))
45    return EC;
46
47  switch (Record.Kind) {
48  default:
49    if (auto EC = Callbacks.visitUnknownMember(Record))
50      return EC;
51    break;
52#define MEMBER_RECORD(EnumName, EnumVal, Name)                                 \
53  case EnumName: {                                                             \
54    if (auto EC = visitKnownMember<Name##Record>(Record, Callbacks))           \
55      return EC;                                                               \
56    break;                                                                     \
57  }
58#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)                \
59  MEMBER_RECORD(EnumVal, EnumVal, AliasName)
60#define TYPE_RECORD(EnumName, EnumVal, Name)
61#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
62#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
63  }
64
65  if (auto EC = Callbacks.visitMemberEnd(Record))
66    return EC;
67
68  return Error::success();
69}
70
71namespace {
72
73class CVTypeVisitor {
74public:
75  explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks);
76
77  Error visitTypeRecord(CVType &Record, TypeIndex Index);
78  Error visitTypeRecord(CVType &Record);
79
80  /// Visits the type records in Data. Sets the error flag on parse failures.
81  Error visitTypeStream(const CVTypeArray &Types);
82  Error visitTypeStream(CVTypeRange Types);
83  Error visitTypeStream(TypeCollection &Types);
84
85  Error visitMemberRecord(CVMemberRecord Record);
86  Error visitFieldListMemberStream(BinaryStreamReader &Stream);
87
88private:
89  Error finishVisitation(CVType &Record);
90
91  /// The interface to the class that gets notified of each visitation.
92  TypeVisitorCallbacks &Callbacks;
93};
94
95CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
96    : Callbacks(Callbacks) {}
97
98Error CVTypeVisitor::finishVisitation(CVType &Record) {
99  switch (Record.kind()) {
100  default:
101    if (auto EC = Callbacks.visitUnknownType(Record))
102      return EC;
103    break;
104#define TYPE_RECORD(EnumName, EnumVal, Name)                                   \
105  case EnumName: {                                                             \
106    if (auto EC = visitKnownRecord<Name##Record>(Record, Callbacks))           \
107      return EC;                                                               \
108    break;                                                                     \
109  }
110#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)                  \
111  TYPE_RECORD(EnumVal, EnumVal, AliasName)
112#define MEMBER_RECORD(EnumName, EnumVal, Name)
113#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
114#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
115  }
116
117  if (auto EC = Callbacks.visitTypeEnd(Record))
118    return EC;
119
120  return Error::success();
121}
122
123Error CVTypeVisitor::visitTypeRecord(CVType &Record, TypeIndex Index) {
124  if (auto EC = Callbacks.visitTypeBegin(Record, Index))
125    return EC;
126
127  return finishVisitation(Record);
128}
129
130Error CVTypeVisitor::visitTypeRecord(CVType &Record) {
131  if (auto EC = Callbacks.visitTypeBegin(Record))
132    return EC;
133
134  return finishVisitation(Record);
135}
136
137Error CVTypeVisitor::visitMemberRecord(CVMemberRecord Record) {
138  return ::visitMemberRecord(Record, Callbacks);
139}
140
141/// Visits the type records in Data. Sets the error flag on parse failures.
142Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) {
143  for (auto I : Types) {
144    if (auto EC = visitTypeRecord(I))
145      return EC;
146  }
147  return Error::success();
148}
149
150Error CVTypeVisitor::visitTypeStream(CVTypeRange Types) {
151  for (auto I : Types) {
152    if (auto EC = visitTypeRecord(I))
153      return EC;
154  }
155  return Error::success();
156}
157
158Error CVTypeVisitor::visitTypeStream(TypeCollection &Types) {
159  Optional<TypeIndex> I = Types.getFirst();
160  while (I) {
161    CVType Type = Types.getType(*I);
162    if (auto EC = visitTypeRecord(Type, *I))
163      return EC;
164    I = Types.getNext(*I);
165  }
166  return Error::success();
167}
168
169Error CVTypeVisitor::visitFieldListMemberStream(BinaryStreamReader &Reader) {
170  TypeLeafKind Leaf;
171  while (!Reader.empty()) {
172    if (auto EC = Reader.readEnum(Leaf))
173      return EC;
174
175    CVMemberRecord Record;
176    Record.Kind = Leaf;
177    if (auto EC = ::visitMemberRecord(Record, Callbacks))
178      return EC;
179  }
180
181  return Error::success();
182}
183
184struct FieldListVisitHelper {
185  FieldListVisitHelper(TypeVisitorCallbacks &Callbacks, ArrayRef<uint8_t> Data,
186                       VisitorDataSource Source)
187      : Stream(Data, llvm::support::little), Reader(Stream),
188        Deserializer(Reader),
189        Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) {
190    if (Source == VDS_BytesPresent) {
191      Pipeline.addCallbackToPipeline(Deserializer);
192      Pipeline.addCallbackToPipeline(Callbacks);
193    }
194  }
195
196  BinaryByteStream Stream;
197  BinaryStreamReader Reader;
198  FieldListDeserializer Deserializer;
199  TypeVisitorCallbackPipeline Pipeline;
200  CVTypeVisitor Visitor;
201};
202
203struct VisitHelper {
204  VisitHelper(TypeVisitorCallbacks &Callbacks, VisitorDataSource Source)
205      : Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) {
206    if (Source == VDS_BytesPresent) {
207      Pipeline.addCallbackToPipeline(Deserializer);
208      Pipeline.addCallbackToPipeline(Callbacks);
209    }
210  }
211
212  TypeDeserializer Deserializer;
213  TypeVisitorCallbackPipeline Pipeline;
214  CVTypeVisitor Visitor;
215};
216}
217
218Error llvm::codeview::visitTypeRecord(CVType &Record, TypeIndex Index,
219                                      TypeVisitorCallbacks &Callbacks,
220                                      VisitorDataSource Source) {
221  VisitHelper V(Callbacks, Source);
222  return V.Visitor.visitTypeRecord(Record, Index);
223}
224
225Error llvm::codeview::visitTypeRecord(CVType &Record,
226                                      TypeVisitorCallbacks &Callbacks,
227                                      VisitorDataSource Source) {
228  VisitHelper V(Callbacks, Source);
229  return V.Visitor.visitTypeRecord(Record);
230}
231
232Error llvm::codeview::visitTypeStream(const CVTypeArray &Types,
233                                      TypeVisitorCallbacks &Callbacks,
234                                      VisitorDataSource Source) {
235  VisitHelper V(Callbacks, Source);
236  return V.Visitor.visitTypeStream(Types);
237}
238
239Error llvm::codeview::visitTypeStream(CVTypeRange Types,
240                                      TypeVisitorCallbacks &Callbacks) {
241  VisitHelper V(Callbacks, VDS_BytesPresent);
242  return V.Visitor.visitTypeStream(Types);
243}
244
245Error llvm::codeview::visitTypeStream(TypeCollection &Types,
246                                      TypeVisitorCallbacks &Callbacks) {
247  // When the internal visitor calls Types.getType(Index) the interface is
248  // required to return a CVType with the bytes filled out.  So we can assume
249  // that the bytes will be present when individual records are visited.
250  VisitHelper V(Callbacks, VDS_BytesPresent);
251  return V.Visitor.visitTypeStream(Types);
252}
253
254Error llvm::codeview::visitMemberRecord(CVMemberRecord Record,
255                                        TypeVisitorCallbacks &Callbacks,
256                                        VisitorDataSource Source) {
257  FieldListVisitHelper V(Callbacks, Record.Data, Source);
258  return V.Visitor.visitMemberRecord(Record);
259}
260
261Error llvm::codeview::visitMemberRecord(TypeLeafKind Kind,
262                                        ArrayRef<uint8_t> Record,
263                                        TypeVisitorCallbacks &Callbacks) {
264  CVMemberRecord R;
265  R.Data = Record;
266  R.Kind = Kind;
267  return visitMemberRecord(R, Callbacks, VDS_BytesPresent);
268}
269
270Error llvm::codeview::visitMemberRecordStream(ArrayRef<uint8_t> FieldList,
271                                              TypeVisitorCallbacks &Callbacks) {
272  FieldListVisitHelper V(Callbacks, FieldList, VDS_BytesPresent);
273  return V.Visitor.visitFieldListMemberStream(V.Reader);
274}
275