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
formatClassOptions(uint32_t IndentLevel,ClassOptions Options,TpiStream * Stream,TypeIndex CurrentTypeIndex)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(CurrentTypeIndex);
46 if (!ETI) {
47 consumeError(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("forward ref ({0} {1})", Direction, *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, 4, IndentLevel, " | ");
79 }
80
pointerOptions(PointerOptions Options)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(Opts, " | ");
92 }
93
modifierOptions(ModifierOptions Options)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(Opts, " | ");
102 }
103
formatCallingConvention(CallingConvention Convention)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(Convention);
133 }
134
formatPointerMode(PointerMode Mode)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(Mode);
144 }
145
memberAccess(MemberAccess Access)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(Access);
154 }
155
methodKind(MethodKind Kind)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(Kind);
167 }
168
pointerKind(PointerKind Kind)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(Kind);
186 }
187
memberAttributes(const MemberAttributes & Attrs)188 static std::string memberAttributes(const MemberAttributes &Attrs) {
189 std::vector<std::string> Opts;
190 std::string Access = memberAccess(Attrs.getAccess());
191 std::string Kind = methodKind(Attrs.getMethodKind());
192 if (!Access.empty())
193 Opts.push_back(Access);
194 if (!Kind.empty())
195 Opts.push_back(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(Opts, " ");
203 }
204
formatPointerAttrs(const PointerRecord & Record)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("mode = {0}, opts = {1}, kind = {2}",
210 formatPointerMode(Mode), pointerOptions(Opts),
211 pointerKind(Kind)));
212 }
213
formatFunctionOptions(FunctionOptions Options)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(Opts, " | ");
224 }
225
visitTypeBegin(CVType & Record,TypeIndex Index)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("{0} | {1} [size = {2}",
232 fmt_align(Index, AlignStyle::Right, Width),
233 formatTypeLeafKind(Record.kind()), 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(Record);
241 if (!MaybeHash)
242 return MaybeHash.takeError();
243 uint32_t OurHash = *MaybeHash;
244 OurHash %= NumHashBuckets;
245 if (Hash == OurHash)
246 H = "0x" + utohexstr(Hash);
247 else
248 H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash);
249 }
250 P.format(", hash = {0}", H);
251 }
252 if (RefTracker) {
253 if (RefTracker->isTypeReferenced(Index))
254 P.format(", referenced");
255 else
256 P.format(", unreferenced");
257 }
258 P.format("]");
259 P.Indent(Width + 3);
260 return Error::success();
261 }
262
visitTypeEnd(CVType & Record)263 Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
264 P.Unindent(Width + 3);
265 if (RecordBytes) {
266 AutoIndent Indent(P, 9);
267 P.formatBinary("Bytes", Record.RecordData, 0);
268 }
269 return Error::success();
270 }
271
visitMemberBegin(CVMemberRecord & Record)272 Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
273 P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));
274 return Error::success();
275 }
276
visitMemberEnd(CVMemberRecord & Record)277 Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
278 if (RecordBytes) {
279 AutoIndent Indent(P, 2);
280 P.formatBinary("Bytes", Record.Data, 0);
281 }
282 return Error::success();
283 }
284
getTypeName(TypeIndex TI) const285 StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
286 if (TI.isNoneType())
287 return "";
288 return Types.getTypeName(TI);
289 }
290
visitKnownRecord(CVType & CVR,FieldListRecord & FieldList)291 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
292 FieldListRecord &FieldList) {
293 if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
294 return EC;
295
296 return Error::success();
297 }
298
visitKnownRecord(CVType & CVR,StringIdRecord & String)299 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
300 StringIdRecord &String) {
301 P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
302 return Error::success();
303 }
304
visitKnownRecord(CVType & CVR,ArgListRecord & Args)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(Indices);
312 uint32_t W = NumDigits(Max->getIndex()) + 2;
313
314 for (auto I : Indices)
315 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
316 getTypeName(I));
317 return Error::success();
318 }
319
visitKnownRecord(CVType & CVR,StringListRecord & Strings)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(Indices);
327 uint32_t W = NumDigits(Max->getIndex()) + 2;
328
329 for (auto I : Indices)
330 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
331 getTypeName(I));
332 return Error::success();
333 }
334
visitKnownRecord(CVType & CVR,ClassRecord & Class)335 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
336 ClassRecord &Class) {
337 P.format(" `{0}`", Class.Name);
338 if (Class.hasUniqueName())
339 P.formatLine("unique name: `{0}`", Class.UniqueName);
340 P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
341 Class.VTableShape, Class.DerivationList, Class.FieldList);
342 P.formatLine("options: {0}, sizeof {1}",
343 formatClassOptions(P.getIndentLevel(), Class.Options, Stream,
344 CurrentTypeIndex),
345 Class.Size);
346 return Error::success();
347 }
348
visitKnownRecord(CVType & CVR,UnionRecord & Union)349 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
350 UnionRecord &Union) {
351 P.format(" `{0}`", Union.Name);
352 if (Union.hasUniqueName())
353 P.formatLine("unique name: `{0}`", Union.UniqueName);
354 P.formatLine("field list: {0}", Union.FieldList);
355 P.formatLine("options: {0}, sizeof {1}",
356 formatClassOptions(P.getIndentLevel(), Union.Options, Stream,
357 CurrentTypeIndex),
358 Union.Size);
359 return Error::success();
360 }
361
visitKnownRecord(CVType & CVR,EnumRecord & Enum)362 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
363 P.format(" `{0}`", Enum.Name);
364 if (Enum.hasUniqueName())
365 P.formatLine("unique name: `{0}`", Enum.UniqueName);
366 P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
367 Enum.UnderlyingType);
368 P.formatLine("options: {0}",
369 formatClassOptions(P.getIndentLevel(), Enum.Options, Stream,
370 CurrentTypeIndex));
371 return Error::success();
372 }
373
visitKnownRecord(CVType & CVR,ArrayRecord & AT)374 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
375 if (AT.Name.empty()) {
376 P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size,
377 AT.IndexType, AT.ElementType);
378 } else {
379 P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}",
380 AT.Name, AT.Size, AT.IndexType, AT.ElementType);
381 }
382 return Error::success();
383 }
384
visitKnownRecord(CVType & CVR,VFTableRecord & VFT)385 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
386 VFTableRecord &VFT) {
387 P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}",
388 VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable);
389 P.formatLine("method names: ");
390 if (!VFT.MethodNames.empty()) {
391 std::string Sep =
392 formatv("\n{0}",
393 fmt_repeat(' ', P.getIndentLevel() + strlen("method names: ")))
394 .str();
395 P.print(join(VFT.MethodNames, Sep));
396 }
397 return Error::success();
398 }
399
visitKnownRecord(CVType & CVR,MemberFuncIdRecord & Id)400 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
401 MemberFuncIdRecord &Id) {
402 P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name,
403 Id.FunctionType, Id.ClassType);
404 return Error::success();
405 }
406
visitKnownRecord(CVType & CVR,ProcedureRecord & Proc)407 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
408 ProcedureRecord &Proc) {
409 P.formatLine("return type = {0}, # args = {1}, param list = {2}",
410 Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList);
411 P.formatLine("calling conv = {0}, options = {1}",
412 formatCallingConvention(Proc.CallConv),
413 formatFunctionOptions(Proc.Options));
414 return Error::success();
415 }
416
visitKnownRecord(CVType & CVR,MemberFunctionRecord & MF)417 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
418 MemberFunctionRecord &MF) {
419 P.formatLine("return type = {0}, # args = {1}, param list = {2}",
420 MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
421 P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
422 MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
423 P.formatLine("calling conv = {0}, options = {1}",
424 formatCallingConvention(MF.CallConv),
425 formatFunctionOptions(MF.Options));
426 return Error::success();
427 }
428
visitKnownRecord(CVType & CVR,FuncIdRecord & Func)429 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
430 FuncIdRecord &Func) {
431 P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name,
432 Func.FunctionType, Func.ParentScope);
433 return Error::success();
434 }
435
visitKnownRecord(CVType & CVR,TypeServer2Record & TS)436 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
437 TypeServer2Record &TS) {
438 P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid);
439 return Error::success();
440 }
441
visitKnownRecord(CVType & CVR,PointerRecord & Ptr)442 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
443 PointerRecord &Ptr) {
444 P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
445 formatPointerAttrs(Ptr));
446 return Error::success();
447 }
448
visitKnownRecord(CVType & CVR,ModifierRecord & Mod)449 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
450 ModifierRecord &Mod) {
451 P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType,
452 modifierOptions(Mod.Modifiers));
453 return Error::success();
454 }
455
visitKnownRecord(CVType & CVR,VFTableShapeRecord & Shape)456 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
457 VFTableShapeRecord &Shape) {
458 return Error::success();
459 }
460
visitKnownRecord(CVType & CVR,UdtModSourceLineRecord & U)461 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
462 UdtModSourceLineRecord &U) {
463 P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module,
464 U.SourceFile.getIndex(), U.LineNumber);
465 return Error::success();
466 }
467
visitKnownRecord(CVType & CVR,UdtSourceLineRecord & U)468 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
469 UdtSourceLineRecord &U) {
470 P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT,
471 U.SourceFile.getIndex(), U.LineNumber);
472 return Error::success();
473 }
474
visitKnownRecord(CVType & CVR,BitFieldRecord & BF)475 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
476 BitFieldRecord &BF) {
477 P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type,
478 BF.BitOffset, BF.BitSize);
479 return Error::success();
480 }
481
visitKnownRecord(CVType & CVR,MethodOverloadListRecord & Overloads)482 Error MinimalTypeDumpVisitor::visitKnownRecord(
483 CVType &CVR, MethodOverloadListRecord &Overloads) {
484 for (auto &M : Overloads.Methods)
485 P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
486 M.Type, M.VFTableOffset, memberAttributes(M.Attrs));
487 return Error::success();
488 }
489
visitKnownRecord(CVType & CVR,BuildInfoRecord & BI)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(Indices);
497 uint32_t W = NumDigits(Max->getIndex()) + 2;
498
499 for (auto I : Indices)
500 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
501 getTypeName(I));
502 return Error::success();
503 }
504
visitKnownRecord(CVType & CVR,LabelRecord & R)505 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
506 std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
507 P.format(" type = {0}", Type);
508 return Error::success();
509 }
510
visitKnownRecord(CVType & CVR,PrecompRecord & Precomp)511 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
512 PrecompRecord &Precomp) {
513 P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
514 " precomp path = {3}",
515 Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature,
516 Precomp.PrecompFilePath);
517 return Error::success();
518 }
519
visitKnownRecord(CVType & CVR,EndPrecompRecord & EP)520 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
521 EndPrecompRecord &EP) {
522 P.format(" signature = {0:X+}", EP.Signature);
523 return Error::success();
524 }
525
visitKnownMember(CVMemberRecord & CVR,NestedTypeRecord & Nested)526 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
527 NestedTypeRecord &Nested) {
528 P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
529 return Error::success();
530 }
531
visitKnownMember(CVMemberRecord & CVR,OneMethodRecord & Method)532 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
533 OneMethodRecord &Method) {
534 P.format(" [name = `{0}`]", Method.Name);
535 AutoIndent Indent(P);
536 P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type,
537 Method.VFTableOffset, memberAttributes(Method.Attrs));
538 return Error::success();
539 }
540
visitKnownMember(CVMemberRecord & CVR,OverloadedMethodRecord & Method)541 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
542 OverloadedMethodRecord &Method) {
543 P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]",
544 Method.Name, Method.NumOverloads, Method.MethodList);
545 return Error::success();
546 }
547
visitKnownMember(CVMemberRecord & CVR,DataMemberRecord & Field)548 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
549 DataMemberRecord &Field) {
550 P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name,
551 Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs));
552 return Error::success();
553 }
554
visitKnownMember(CVMemberRecord & CVR,StaticDataMemberRecord & Field)555 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
556 StaticDataMemberRecord &Field) {
557 P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type,
558 memberAttributes(Field.Attrs));
559 return Error::success();
560 }
561
visitKnownMember(CVMemberRecord & CVR,EnumeratorRecord & Enum)562 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
563 EnumeratorRecord &Enum) {
564 P.format(" [{0} = {1}]", Enum.Name,
565 toString(Enum.Value, 10, Enum.Value.isSigned()));
566 return Error::success();
567 }
568
visitKnownMember(CVMemberRecord & CVR,BaseClassRecord & Base)569 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
570 BaseClassRecord &Base) {
571 AutoIndent Indent(P);
572 P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset,
573 memberAttributes(Base.Attrs));
574 return Error::success();
575 }
576
visitKnownMember(CVMemberRecord & CVR,VirtualBaseClassRecord & Base)577 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
578 VirtualBaseClassRecord &Base) {
579 AutoIndent Indent(P);
580 P.formatLine(
581 "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
582 Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex);
583 P.formatLine("attrs = {0}", memberAttributes(Base.Attrs));
584 return Error::success();
585 }
586
visitKnownMember(CVMemberRecord & CVR,ListContinuationRecord & Cont)587 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
588 ListContinuationRecord &Cont) {
589 P.format(" continuation = {0}", Cont.ContinuationIndex);
590 return Error::success();
591 }
592
visitKnownMember(CVMemberRecord & CVR,VFPtrRecord & VFP)593 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
594 VFPtrRecord &VFP) {
595 P.format(" type = {0}", VFP.Type);
596 return Error::success();
597 }
598