598 lines · cpp
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-exception6//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 31using namespace llvm;32using namespace llvm::codeview;33using namespace llvm::pdb;34 35static 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 81static 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 94static 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 104static 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 135static 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 146static 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 156static 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 169static 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 188static 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 205static 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 214static 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 226Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {227 CurrentTypeIndex = Index;228 // formatLine puts the newline at the beginning, so we use formatLine here229 // to start a new line, and then individual visit methods use format to230 // 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 else248 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 else256 P.format(", unreferenced");257 }258 P.format("]");259 P.Indent(Width + 3);260 return Error::success();261}262 263Error 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 272Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {273 P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));274 return Error::success();275}276 277Error 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 285StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {286 if (TI.isNoneType())287 return "";288 return Types.getTypeName(TI);289}290 291Error 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 299Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,300 StringIdRecord &String) {301 P.format(" ID: {0}, String: {1}", String.getId(), String.getString());302 return Error::success();303}304 305Error 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 320Error 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 335Error 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 349Error 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 362Error 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 374Error 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 385Error 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 400Error 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 407Error 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 417Error 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 429Error 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 436Error 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 442Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,443 PointerRecord &Ptr) {444 P.formatLine("referent = {0}, {1}", Ptr.ReferentType,445 formatPointerAttrs(Ptr));446 return Error::success();447}448 449Error 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 456Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,457 VFTableShapeRecord &Shape) {458 return Error::success();459}460 461Error 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 468Error 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 475Error 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 482Error 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 490Error 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 505Error 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 511Error 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 520Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,521 EndPrecompRecord &EP) {522 P.format(" signature = {0:X+}", EP.Signature);523 return Error::success();524}525 526Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,527 NestedTypeRecord &Nested) {528 P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);529 return Error::success();530}531 532Error 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 541Error 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 548Error 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 555Error 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 562Error 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 569Error 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 577Error 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 587Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,588 ListContinuationRecord &Cont) {589 P.format(" continuation = {0}", Cont.ContinuationIndex);590 return Error::success();591}592 593Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,594 VFPtrRecord &VFP) {595 P.format(" type = {0}", VFP.Type);596 return Error::success();597}598