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