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::(const CVTypeArray &Types) { |
144 | for (auto I : Types) { |
145 | if (auto EC = visitTypeRecord(Record&: 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(Record&: 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(Index: *I); |
163 | if (auto EC = visitTypeRecord(Record&: Type, Index: *I)) |
164 | return EC; |
165 | I = Types.getNext(Prev: *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(Dest&: 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::endianness::little), Reader(Stream), |
189 | Deserializer(Reader), |
190 | Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) { |
191 | if (Source == VDS_BytesPresent) { |
192 | Pipeline.addCallbackToPipeline(Callbacks&: 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(Callbacks&: 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::(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::(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(Record: R, Callbacks, Source: 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(Reader&: V.Reader); |
275 | } |
276 | |