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 
20 using namespace llvm;
21 using namespace llvm::codeview;
22 
23 
24 template <typename T>
25 static 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 
33 template <typename T>
34 static 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 
43 static 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 
72 namespace {
73 
74 class CVTypeVisitor {
75 public:
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 
89 private:
90   Error finishVisitation(CVType &Record);
91 
92   /// The interface to the class that gets notified of each visitation.
93   TypeVisitorCallbacks &Callbacks;
94 };
95 
96 CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
97     : Callbacks(Callbacks) {}
98 
99 Error 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 
124 Error CVTypeVisitor::visitTypeRecord(CVType &Record, TypeIndex Index) {
125   if (auto EC = Callbacks.visitTypeBegin(Record, Index))
126     return EC;
127 
128   return finishVisitation(Record);
129 }
130 
131 Error CVTypeVisitor::visitTypeRecord(CVType &Record) {
132   if (auto EC = Callbacks.visitTypeBegin(Record))
133     return EC;
134 
135   return finishVisitation(Record);
136 }
137 
138 Error 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.
143 Error 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 
151 Error 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 
159 Error 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 
170 Error 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 
185 struct FieldListVisitHelper {
186   FieldListVisitHelper(TypeVisitorCallbacks &Callbacks, ArrayRef<uint8_t> Data,
187                        VisitorDataSource Source)
188       : Stream(Data, llvm::support::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 
204 struct 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 
219 Error 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 
226 Error llvm::codeview::visitTypeRecord(CVType &Record,
227                                       TypeVisitorCallbacks &Callbacks,
228                                       VisitorDataSource Source) {
229   VisitHelper V(Callbacks, Source);
230   return V.Visitor.visitTypeRecord(Record);
231 }
232 
233 Error 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 
240 Error llvm::codeview::visitTypeStream(CVTypeRange Types,
241                                       TypeVisitorCallbacks &Callbacks) {
242   VisitHelper V(Callbacks, VDS_BytesPresent);
243   return V.Visitor.visitTypeStream(Types);
244 }
245 
246 Error 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 
255 Error 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 
262 Error 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 
271 Error 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