1 | //===- MinimalTypeDumper.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 "MinimalTypeDumper.h" |
10 | |
11 | #include "TypeReferenceTracker.h" |
12 | |
13 | #include "llvm-pdbutil.h" |
14 | #include "llvm/ADT/StringExtras.h" |
15 | #include "llvm/DebugInfo/CodeView/CVRecord.h" |
16 | #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" |
17 | #include "llvm/DebugInfo/CodeView/CodeView.h" |
18 | #include "llvm/DebugInfo/CodeView/Formatters.h" |
19 | #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" |
20 | #include "llvm/DebugInfo/CodeView/TypeRecord.h" |
21 | #include "llvm/DebugInfo/PDB/Native/FormatUtil.h" |
22 | #include "llvm/DebugInfo/PDB/Native/LinePrinter.h" |
23 | #include "llvm/DebugInfo/PDB/Native/NativeSession.h" |
24 | #include "llvm/DebugInfo/PDB/Native/PDBFile.h" |
25 | #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" |
26 | #include "llvm/DebugInfo/PDB/Native/TpiStream.h" |
27 | #include "llvm/Object/COFF.h" |
28 | #include "llvm/Support/FormatVariadic.h" |
29 | #include "llvm/Support/MathExtras.h" |
30 | |
31 | using namespace llvm; |
32 | using namespace llvm::codeview; |
33 | using namespace llvm::pdb; |
34 | |
35 | static std::string formatClassOptions(uint32_t IndentLevel, |
36 | ClassOptions Options, TpiStream *Stream, |
37 | TypeIndex CurrentTypeIndex) { |
38 | std::vector<std::string> Opts; |
39 | |
40 | if (Stream && Stream->supportsTypeLookup() && |
41 | !opts::dump::DontResolveForwardRefs && |
42 | ((Options & ClassOptions::ForwardReference) != ClassOptions::None)) { |
43 | // If we're able to resolve forward references, do that. |
44 | Expected<TypeIndex> ETI = |
45 | Stream->findFullDeclForForwardRef(ForwardRefTI: CurrentTypeIndex); |
46 | if (!ETI) { |
47 | consumeError(Err: ETI.takeError()); |
48 | PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref (??\?)" ); |
49 | } else { |
50 | const char *Direction = (*ETI == CurrentTypeIndex) |
51 | ? "=" |
52 | : ((*ETI < CurrentTypeIndex) ? "<-" : "->" ); |
53 | std::string Formatted = |
54 | formatv(Fmt: "forward ref ({0} {1})" , Vals&: Direction, Vals&: *ETI).str(); |
55 | PUSH_FLAG(ClassOptions, ForwardReference, Options, std::move(Formatted)); |
56 | } |
57 | } else { |
58 | PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref" ); |
59 | } |
60 | |
61 | PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options, |
62 | "has ctor / dtor" ); |
63 | PUSH_FLAG(ClassOptions, ContainsNestedClass, Options, |
64 | "contains nested class" ); |
65 | PUSH_FLAG(ClassOptions, HasConversionOperator, Options, |
66 | "conversion operator" ); |
67 | PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name" ); |
68 | PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin" ); |
69 | PUSH_FLAG(ClassOptions, Nested, Options, "is nested" ); |
70 | PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options, |
71 | "overloaded operator" ); |
72 | PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options, |
73 | "overloaded operator=" ); |
74 | PUSH_FLAG(ClassOptions, Packed, Options, "packed" ); |
75 | PUSH_FLAG(ClassOptions, Scoped, Options, "scoped" ); |
76 | PUSH_FLAG(ClassOptions, Sealed, Options, "sealed" ); |
77 | |
78 | return typesetItemList(Opts, IndentLevel: 4, GroupSize: IndentLevel, Sep: " | " ); |
79 | } |
80 | |
81 | static std::string pointerOptions(PointerOptions Options) { |
82 | std::vector<std::string> Opts; |
83 | PUSH_FLAG(PointerOptions, Flat32, Options, "flat32" ); |
84 | PUSH_FLAG(PointerOptions, Volatile, Options, "volatile" ); |
85 | PUSH_FLAG(PointerOptions, Const, Options, "const" ); |
86 | PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned" ); |
87 | PUSH_FLAG(PointerOptions, Restrict, Options, "restrict" ); |
88 | PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt" ); |
89 | if (Opts.empty()) |
90 | return "None" ; |
91 | return join(R&: Opts, Separator: " | " ); |
92 | } |
93 | |
94 | static std::string modifierOptions(ModifierOptions Options) { |
95 | std::vector<std::string> Opts; |
96 | PUSH_FLAG(ModifierOptions, Const, Options, "const" ); |
97 | PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile" ); |
98 | PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned" ); |
99 | if (Opts.empty()) |
100 | return "None" ; |
101 | return join(R&: Opts, Separator: " | " ); |
102 | } |
103 | |
104 | static std::string formatCallingConvention(CallingConvention Convention) { |
105 | switch (Convention) { |
106 | RETURN_CASE(CallingConvention, AlphaCall, "alphacall" ); |
107 | RETURN_CASE(CallingConvention, AM33Call, "am33call" ); |
108 | RETURN_CASE(CallingConvention, ArmCall, "armcall" ); |
109 | RETURN_CASE(CallingConvention, ClrCall, "clrcall" ); |
110 | RETURN_CASE(CallingConvention, FarC, "far cdecl" ); |
111 | RETURN_CASE(CallingConvention, FarFast, "far fastcall" ); |
112 | RETURN_CASE(CallingConvention, FarPascal, "far pascal" ); |
113 | RETURN_CASE(CallingConvention, FarStdCall, "far stdcall" ); |
114 | RETURN_CASE(CallingConvention, FarSysCall, "far syscall" ); |
115 | RETURN_CASE(CallingConvention, Generic, "generic" ); |
116 | RETURN_CASE(CallingConvention, Inline, "inline" ); |
117 | RETURN_CASE(CallingConvention, M32RCall, "m32rcall" ); |
118 | RETURN_CASE(CallingConvention, MipsCall, "mipscall" ); |
119 | RETURN_CASE(CallingConvention, NearC, "cdecl" ); |
120 | RETURN_CASE(CallingConvention, NearFast, "fastcall" ); |
121 | RETURN_CASE(CallingConvention, NearPascal, "pascal" ); |
122 | RETURN_CASE(CallingConvention, NearStdCall, "stdcall" ); |
123 | RETURN_CASE(CallingConvention, NearSysCall, "near syscall" ); |
124 | RETURN_CASE(CallingConvention, NearVector, "vectorcall" ); |
125 | RETURN_CASE(CallingConvention, PpcCall, "ppccall" ); |
126 | RETURN_CASE(CallingConvention, SHCall, "shcall" ); |
127 | RETURN_CASE(CallingConvention, SH5Call, "sh5call" ); |
128 | RETURN_CASE(CallingConvention, Swift, "swift" ); |
129 | RETURN_CASE(CallingConvention, ThisCall, "thiscall" ); |
130 | RETURN_CASE(CallingConvention, TriCall, "tricall" ); |
131 | } |
132 | return formatUnknownEnum(Value: Convention); |
133 | } |
134 | |
135 | static std::string formatPointerMode(PointerMode Mode) { |
136 | switch (Mode) { |
137 | RETURN_CASE(PointerMode, LValueReference, "ref" ); |
138 | RETURN_CASE(PointerMode, Pointer, "pointer" ); |
139 | RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer" ); |
140 | RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer" ); |
141 | RETURN_CASE(PointerMode, RValueReference, "rvalue ref" ); |
142 | } |
143 | return formatUnknownEnum(Value: Mode); |
144 | } |
145 | |
146 | static std::string memberAccess(MemberAccess Access) { |
147 | switch (Access) { |
148 | RETURN_CASE(MemberAccess, None, "" ); |
149 | RETURN_CASE(MemberAccess, Private, "private" ); |
150 | RETURN_CASE(MemberAccess, Protected, "protected" ); |
151 | RETURN_CASE(MemberAccess, Public, "public" ); |
152 | } |
153 | return formatUnknownEnum(Value: Access); |
154 | } |
155 | |
156 | static std::string methodKind(MethodKind Kind) { |
157 | switch (Kind) { |
158 | RETURN_CASE(MethodKind, Vanilla, "" ); |
159 | RETURN_CASE(MethodKind, Virtual, "virtual" ); |
160 | RETURN_CASE(MethodKind, Static, "static" ); |
161 | RETURN_CASE(MethodKind, Friend, "friend" ); |
162 | RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual" ); |
163 | RETURN_CASE(MethodKind, PureVirtual, "pure virtual" ); |
164 | RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual" ); |
165 | } |
166 | return formatUnknownEnum(Value: Kind); |
167 | } |
168 | |
169 | static std::string pointerKind(PointerKind Kind) { |
170 | switch (Kind) { |
171 | RETURN_CASE(PointerKind, Near16, "ptr16" ); |
172 | RETURN_CASE(PointerKind, Far16, "far ptr16" ); |
173 | RETURN_CASE(PointerKind, Huge16, "huge ptr16" ); |
174 | RETURN_CASE(PointerKind, BasedOnSegment, "segment based" ); |
175 | RETURN_CASE(PointerKind, BasedOnValue, "value based" ); |
176 | RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based" ); |
177 | RETURN_CASE(PointerKind, BasedOnAddress, "address based" ); |
178 | RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based" ); |
179 | RETURN_CASE(PointerKind, BasedOnType, "type based" ); |
180 | RETURN_CASE(PointerKind, BasedOnSelf, "self based" ); |
181 | RETURN_CASE(PointerKind, Near32, "ptr32" ); |
182 | RETURN_CASE(PointerKind, Far32, "far ptr32" ); |
183 | RETURN_CASE(PointerKind, Near64, "ptr64" ); |
184 | } |
185 | return formatUnknownEnum(Value: Kind); |
186 | } |
187 | |
188 | static std::string memberAttributes(const MemberAttributes &Attrs) { |
189 | std::vector<std::string> Opts; |
190 | std::string Access = memberAccess(Access: Attrs.getAccess()); |
191 | std::string Kind = methodKind(Kind: Attrs.getMethodKind()); |
192 | if (!Access.empty()) |
193 | Opts.push_back(x: Access); |
194 | if (!Kind.empty()) |
195 | Opts.push_back(x: Kind); |
196 | MethodOptions Flags = Attrs.getFlags(); |
197 | PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo" ); |
198 | PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit" ); |
199 | PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct" ); |
200 | PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated" ); |
201 | PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed" ); |
202 | return join(R&: Opts, Separator: " " ); |
203 | } |
204 | |
205 | static std::string formatPointerAttrs(const PointerRecord &Record) { |
206 | PointerMode Mode = Record.getMode(); |
207 | PointerOptions Opts = Record.getOptions(); |
208 | PointerKind Kind = Record.getPointerKind(); |
209 | return std::string(formatv(Fmt: "mode = {0}, opts = {1}, kind = {2}" , |
210 | Vals: formatPointerMode(Mode), Vals: pointerOptions(Options: Opts), |
211 | Vals: pointerKind(Kind))); |
212 | } |
213 | |
214 | static std::string formatFunctionOptions(FunctionOptions Options) { |
215 | std::vector<std::string> Opts; |
216 | |
217 | PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt" ); |
218 | PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options, |
219 | "constructor with virtual bases" ); |
220 | PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor" ); |
221 | if (Opts.empty()) |
222 | return "None" ; |
223 | return join(R&: Opts, Separator: " | " ); |
224 | } |
225 | |
226 | Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { |
227 | CurrentTypeIndex = Index; |
228 | // formatLine puts the newline at the beginning, so we use formatLine here |
229 | // to start a new line, and then individual visit methods use format to |
230 | // append to the existing line. |
231 | P.formatLine(Fmt: "{0} | {1} [size = {2}" , |
232 | Items: fmt_align(Item&: Index, Where: AlignStyle::Right, Amount: Width), |
233 | Items: formatTypeLeafKind(K: Record.kind()), Items: Record.length()); |
234 | if (Hashes) { |
235 | std::string H; |
236 | if (Index.toArrayIndex() >= HashValues.size()) { |
237 | H = "(not present)" ; |
238 | } else { |
239 | uint32_t Hash = HashValues[Index.toArrayIndex()]; |
240 | Expected<uint32_t> MaybeHash = hashTypeRecord(Type: Record); |
241 | if (!MaybeHash) |
242 | return MaybeHash.takeError(); |
243 | uint32_t OurHash = *MaybeHash; |
244 | OurHash %= NumHashBuckets; |
245 | if (Hash == OurHash) |
246 | H = "0x" + utohexstr(X: Hash); |
247 | else |
248 | H = "0x" + utohexstr(X: Hash) + ", our hash = 0x" + utohexstr(X: OurHash); |
249 | } |
250 | P.format(Fmt: ", hash = {0}" , Items&: H); |
251 | } |
252 | if (RefTracker) { |
253 | if (RefTracker->isTypeReferenced(TI: Index)) |
254 | P.format(Fmt: ", referenced" ); |
255 | else |
256 | P.format(Fmt: ", unreferenced" ); |
257 | } |
258 | P.format(Fmt: "]" ); |
259 | P.Indent(Amount: Width + 3); |
260 | return Error::success(); |
261 | } |
262 | |
263 | Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) { |
264 | P.Unindent(Amount: Width + 3); |
265 | if (RecordBytes) { |
266 | AutoIndent Indent(P, 9); |
267 | P.formatBinary(Label: "Bytes" , Data: Record.RecordData, StartOffset: 0); |
268 | } |
269 | return Error::success(); |
270 | } |
271 | |
272 | Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) { |
273 | P.formatLine(Fmt: "- {0}" , Items: formatTypeLeafKind(K: Record.Kind)); |
274 | return Error::success(); |
275 | } |
276 | |
277 | Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) { |
278 | if (RecordBytes) { |
279 | AutoIndent Indent(P, 2); |
280 | P.formatBinary(Label: "Bytes" , Data: Record.Data, StartOffset: 0); |
281 | } |
282 | return Error::success(); |
283 | } |
284 | |
285 | StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const { |
286 | if (TI.isNoneType()) |
287 | return "" ; |
288 | return Types.getTypeName(Index: TI); |
289 | } |
290 | |
291 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
292 | FieldListRecord &FieldList) { |
293 | if (auto EC = codeview::visitMemberRecordStream(FieldList: FieldList.Data, Callbacks&: *this)) |
294 | return EC; |
295 | |
296 | return Error::success(); |
297 | } |
298 | |
299 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
300 | StringIdRecord &String) { |
301 | P.format(Fmt: " ID: {0}, String: {1}" , Items: String.getId(), Items: String.getString()); |
302 | return Error::success(); |
303 | } |
304 | |
305 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
306 | ArgListRecord &Args) { |
307 | auto Indices = Args.getIndices(); |
308 | if (Indices.empty()) |
309 | return Error::success(); |
310 | |
311 | auto Max = llvm::max_element(Range&: Indices); |
312 | uint32_t W = NumDigits(N: Max->getIndex()) + 2; |
313 | |
314 | for (auto I : Indices) |
315 | P.formatLine(Fmt: "{0}: `{1}`" , Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W), |
316 | Items: getTypeName(TI: I)); |
317 | return Error::success(); |
318 | } |
319 | |
320 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
321 | StringListRecord &Strings) { |
322 | auto Indices = Strings.getIndices(); |
323 | if (Indices.empty()) |
324 | return Error::success(); |
325 | |
326 | auto Max = llvm::max_element(Range&: Indices); |
327 | uint32_t W = NumDigits(N: Max->getIndex()) + 2; |
328 | |
329 | for (auto I : Indices) |
330 | P.formatLine(Fmt: "{0}: `{1}`" , Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W), |
331 | Items: getTypeName(TI: I)); |
332 | return Error::success(); |
333 | } |
334 | |
335 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
336 | ClassRecord &Class) { |
337 | P.format(Fmt: " `{0}`" , Items&: Class.Name); |
338 | if (Class.hasUniqueName()) |
339 | P.formatLine(Fmt: "unique name: `{0}`" , Items&: Class.UniqueName); |
340 | P.formatLine(Fmt: "vtable: {0}, base list: {1}, field list: {2}" , |
341 | Items&: Class.VTableShape, Items&: Class.DerivationList, Items&: Class.FieldList); |
342 | P.formatLine(Fmt: "options: {0}, sizeof {1}" , |
343 | Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: Class.Options, Stream, |
344 | CurrentTypeIndex), |
345 | Items&: Class.Size); |
346 | return Error::success(); |
347 | } |
348 | |
349 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
350 | UnionRecord &Union) { |
351 | P.format(Fmt: " `{0}`" , Items&: Union.Name); |
352 | if (Union.hasUniqueName()) |
353 | P.formatLine(Fmt: "unique name: `{0}`" , Items&: Union.UniqueName); |
354 | P.formatLine(Fmt: "field list: {0}" , Items&: Union.FieldList); |
355 | P.formatLine(Fmt: "options: {0}, sizeof {1}" , |
356 | Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: Union.Options, Stream, |
357 | CurrentTypeIndex), |
358 | Items&: Union.Size); |
359 | return Error::success(); |
360 | } |
361 | |
362 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { |
363 | P.format(Fmt: " `{0}`" , Items&: Enum.Name); |
364 | if (Enum.hasUniqueName()) |
365 | P.formatLine(Fmt: "unique name: `{0}`" , Items&: Enum.UniqueName); |
366 | P.formatLine(Fmt: "field list: {0}, underlying type: {1}" , Items&: Enum.FieldList, |
367 | Items&: Enum.UnderlyingType); |
368 | P.formatLine(Fmt: "options: {0}" , |
369 | Items: formatClassOptions(IndentLevel: P.getIndentLevel(), Options: Enum.Options, Stream, |
370 | CurrentTypeIndex)); |
371 | return Error::success(); |
372 | } |
373 | |
374 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { |
375 | if (AT.Name.empty()) { |
376 | P.formatLine(Fmt: "size: {0}, index type: {1}, element type: {2}" , Items&: AT.Size, |
377 | Items&: AT.IndexType, Items&: AT.ElementType); |
378 | } else { |
379 | P.formatLine(Fmt: "name: {0}, size: {1}, index type: {2}, element type: {3}" , |
380 | Items&: AT.Name, Items&: AT.Size, Items&: AT.IndexType, Items&: AT.ElementType); |
381 | } |
382 | return Error::success(); |
383 | } |
384 | |
385 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
386 | VFTableRecord &VFT) { |
387 | P.formatLine(Fmt: "offset: {0}, complete class: {1}, overridden vftable: {2}" , |
388 | Items&: VFT.VFPtrOffset, Items&: VFT.CompleteClass, Items&: VFT.OverriddenVFTable); |
389 | P.formatLine(Fmt: "method names: " ); |
390 | if (!VFT.MethodNames.empty()) { |
391 | std::string Sep = |
392 | formatv(Fmt: "\n{0}" , |
393 | Vals: fmt_repeat(Item: ' ', Count: P.getIndentLevel() + strlen(s: "method names: " ))) |
394 | .str(); |
395 | P.print(T: join(R&: VFT.MethodNames, Separator: Sep)); |
396 | } |
397 | return Error::success(); |
398 | } |
399 | |
400 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
401 | MemberFuncIdRecord &Id) { |
402 | P.formatLine(Fmt: "name = {0}, type = {1}, class type = {2}" , Items&: Id.Name, |
403 | Items&: Id.FunctionType, Items&: Id.ClassType); |
404 | return Error::success(); |
405 | } |
406 | |
407 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
408 | ProcedureRecord &Proc) { |
409 | P.formatLine(Fmt: "return type = {0}, # args = {1}, param list = {2}" , |
410 | Items&: Proc.ReturnType, Items&: Proc.ParameterCount, Items&: Proc.ArgumentList); |
411 | P.formatLine(Fmt: "calling conv = {0}, options = {1}" , |
412 | Items: formatCallingConvention(Convention: Proc.CallConv), |
413 | Items: formatFunctionOptions(Options: Proc.Options)); |
414 | return Error::success(); |
415 | } |
416 | |
417 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
418 | MemberFunctionRecord &MF) { |
419 | P.formatLine(Fmt: "return type = {0}, # args = {1}, param list = {2}" , |
420 | Items&: MF.ReturnType, Items&: MF.ParameterCount, Items&: MF.ArgumentList); |
421 | P.formatLine(Fmt: "class type = {0}, this type = {1}, this adjust = {2}" , |
422 | Items&: MF.ClassType, Items&: MF.ThisType, Items&: MF.ThisPointerAdjustment); |
423 | P.formatLine(Fmt: "calling conv = {0}, options = {1}" , |
424 | Items: formatCallingConvention(Convention: MF.CallConv), |
425 | Items: formatFunctionOptions(Options: MF.Options)); |
426 | return Error::success(); |
427 | } |
428 | |
429 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
430 | FuncIdRecord &Func) { |
431 | P.formatLine(Fmt: "name = {0}, type = {1}, parent scope = {2}" , Items&: Func.Name, |
432 | Items&: Func.FunctionType, Items&: Func.ParentScope); |
433 | return Error::success(); |
434 | } |
435 | |
436 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
437 | TypeServer2Record &TS) { |
438 | P.formatLine(Fmt: "name = {0}, age = {1}, guid = {2}" , Items&: TS.Name, Items&: TS.Age, Items&: TS.Guid); |
439 | return Error::success(); |
440 | } |
441 | |
442 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
443 | PointerRecord &Ptr) { |
444 | P.formatLine(Fmt: "referent = {0}, {1}" , Items&: Ptr.ReferentType, |
445 | Items: formatPointerAttrs(Record: Ptr)); |
446 | return Error::success(); |
447 | } |
448 | |
449 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
450 | ModifierRecord &Mod) { |
451 | P.formatLine(Fmt: "referent = {0}, modifiers = {1}" , Items&: Mod.ModifiedType, |
452 | Items: modifierOptions(Options: Mod.Modifiers)); |
453 | return Error::success(); |
454 | } |
455 | |
456 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
457 | VFTableShapeRecord &Shape) { |
458 | return Error::success(); |
459 | } |
460 | |
461 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
462 | UdtModSourceLineRecord &U) { |
463 | P.formatLine(Fmt: "udt = {0}, mod = {1}, file = {2}, line = {3}" , Items&: U.UDT, Items&: U.Module, |
464 | Items: U.SourceFile.getIndex(), Items&: U.LineNumber); |
465 | return Error::success(); |
466 | } |
467 | |
468 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
469 | UdtSourceLineRecord &U) { |
470 | P.formatLine(Fmt: "udt = {0}, file = {1}, line = {2}" , Items&: U.UDT, |
471 | Items: U.SourceFile.getIndex(), Items&: U.LineNumber); |
472 | return Error::success(); |
473 | } |
474 | |
475 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
476 | BitFieldRecord &BF) { |
477 | P.formatLine(Fmt: "type = {0}, bit offset = {1}, # bits = {2}" , Items&: BF.Type, |
478 | Items&: BF.BitOffset, Items&: BF.BitSize); |
479 | return Error::success(); |
480 | } |
481 | |
482 | Error MinimalTypeDumpVisitor::visitKnownRecord( |
483 | CVType &CVR, MethodOverloadListRecord &Overloads) { |
484 | for (auto &M : Overloads.Methods) |
485 | P.formatLine(Fmt: "- Method [type = {0}, vftable offset = {1}, attrs = {2}]" , |
486 | Items&: M.Type, Items&: M.VFTableOffset, Items: memberAttributes(Attrs: M.Attrs)); |
487 | return Error::success(); |
488 | } |
489 | |
490 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
491 | BuildInfoRecord &BI) { |
492 | auto Indices = BI.ArgIndices; |
493 | if (Indices.empty()) |
494 | return Error::success(); |
495 | |
496 | auto Max = llvm::max_element(Range&: Indices); |
497 | uint32_t W = NumDigits(N: Max->getIndex()) + 2; |
498 | |
499 | for (auto I : Indices) |
500 | P.formatLine(Fmt: "{0}: `{1}`" , Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: W), |
501 | Items: getTypeName(TI: I)); |
502 | return Error::success(); |
503 | } |
504 | |
505 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) { |
506 | std::string Type = (R.Mode == LabelType::Far) ? "far" : "near" ; |
507 | P.format(Fmt: " type = {0}" , Items&: Type); |
508 | return Error::success(); |
509 | } |
510 | |
511 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
512 | PrecompRecord &Precomp) { |
513 | P.format(Fmt: " start index = {0:X+}, types count = {1:X+}, signature = {2:X+}," |
514 | " precomp path = {3}" , |
515 | Items&: Precomp.StartTypeIndex, Items&: Precomp.TypesCount, Items&: Precomp.Signature, |
516 | Items&: Precomp.PrecompFilePath); |
517 | return Error::success(); |
518 | } |
519 | |
520 | Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, |
521 | EndPrecompRecord &EP) { |
522 | P.format(Fmt: " signature = {0:X+}" , Items&: EP.Signature); |
523 | return Error::success(); |
524 | } |
525 | |
526 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
527 | NestedTypeRecord &Nested) { |
528 | P.format(Fmt: " [name = `{0}`, parent = {1}]" , Items&: Nested.Name, Items&: Nested.Type); |
529 | return Error::success(); |
530 | } |
531 | |
532 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
533 | OneMethodRecord &Method) { |
534 | P.format(Fmt: " [name = `{0}`]" , Items&: Method.Name); |
535 | AutoIndent Indent(P); |
536 | P.formatLine(Fmt: "type = {0}, vftable offset = {1}, attrs = {2}" , Items&: Method.Type, |
537 | Items&: Method.VFTableOffset, Items: memberAttributes(Attrs: Method.Attrs)); |
538 | return Error::success(); |
539 | } |
540 | |
541 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
542 | OverloadedMethodRecord &Method) { |
543 | P.format(Fmt: " [name = `{0}`, # overloads = {1}, overload list = {2}]" , |
544 | Items&: Method.Name, Items&: Method.NumOverloads, Items&: Method.MethodList); |
545 | return Error::success(); |
546 | } |
547 | |
548 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
549 | DataMemberRecord &Field) { |
550 | P.format(Fmt: " [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]" , Items&: Field.Name, |
551 | Items&: Field.Type, Items&: Field.FieldOffset, Items: memberAttributes(Attrs: Field.Attrs)); |
552 | return Error::success(); |
553 | } |
554 | |
555 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
556 | StaticDataMemberRecord &Field) { |
557 | P.format(Fmt: " [name = `{0}`, type = {1}, attrs = {2}]" , Items&: Field.Name, Items&: Field.Type, |
558 | Items: memberAttributes(Attrs: Field.Attrs)); |
559 | return Error::success(); |
560 | } |
561 | |
562 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
563 | EnumeratorRecord &Enum) { |
564 | P.format(Fmt: " [{0} = {1}]" , Items&: Enum.Name, |
565 | Items: toString(I: Enum.Value, Radix: 10, Signed: Enum.Value.isSigned())); |
566 | return Error::success(); |
567 | } |
568 | |
569 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
570 | BaseClassRecord &Base) { |
571 | AutoIndent Indent(P); |
572 | P.formatLine(Fmt: "type = {0}, offset = {1}, attrs = {2}" , Items&: Base.Type, Items&: Base.Offset, |
573 | Items: memberAttributes(Attrs: Base.Attrs)); |
574 | return Error::success(); |
575 | } |
576 | |
577 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
578 | VirtualBaseClassRecord &Base) { |
579 | AutoIndent Indent(P); |
580 | P.formatLine( |
581 | Fmt: "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}" , |
582 | Items&: Base.BaseType, Items&: Base.VBPtrType, Items&: Base.VBPtrOffset, Items&: Base.VTableIndex); |
583 | P.formatLine(Fmt: "attrs = {0}" , Items: memberAttributes(Attrs: Base.Attrs)); |
584 | return Error::success(); |
585 | } |
586 | |
587 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
588 | ListContinuationRecord &Cont) { |
589 | P.format(Fmt: " continuation = {0}" , Items&: Cont.ContinuationIndex); |
590 | return Error::success(); |
591 | } |
592 | |
593 | Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, |
594 | VFPtrRecord &VFP) { |
595 | P.format(Fmt: " type = {0}" , Items&: VFP.Type); |
596 | return Error::success(); |
597 | } |
598 | |