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