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(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 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 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 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, ThisCall, "thiscall"); 129 RETURN_CASE(CallingConvention, TriCall, "tricall"); 130 } 131 return formatUnknownEnum(Convention); 132 } 133 134 static std::string formatPointerMode(PointerMode Mode) { 135 switch (Mode) { 136 RETURN_CASE(PointerMode, LValueReference, "ref"); 137 RETURN_CASE(PointerMode, Pointer, "pointer"); 138 RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer"); 139 RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer"); 140 RETURN_CASE(PointerMode, RValueReference, "rvalue ref"); 141 } 142 return formatUnknownEnum(Mode); 143 } 144 145 static std::string memberAccess(MemberAccess Access) { 146 switch (Access) { 147 RETURN_CASE(MemberAccess, None, ""); 148 RETURN_CASE(MemberAccess, Private, "private"); 149 RETURN_CASE(MemberAccess, Protected, "protected"); 150 RETURN_CASE(MemberAccess, Public, "public"); 151 } 152 return formatUnknownEnum(Access); 153 } 154 155 static std::string methodKind(MethodKind Kind) { 156 switch (Kind) { 157 RETURN_CASE(MethodKind, Vanilla, ""); 158 RETURN_CASE(MethodKind, Virtual, "virtual"); 159 RETURN_CASE(MethodKind, Static, "static"); 160 RETURN_CASE(MethodKind, Friend, "friend"); 161 RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual"); 162 RETURN_CASE(MethodKind, PureVirtual, "pure virtual"); 163 RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual"); 164 } 165 return formatUnknownEnum(Kind); 166 } 167 168 static std::string pointerKind(PointerKind Kind) { 169 switch (Kind) { 170 RETURN_CASE(PointerKind, Near16, "ptr16"); 171 RETURN_CASE(PointerKind, Far16, "far ptr16"); 172 RETURN_CASE(PointerKind, Huge16, "huge ptr16"); 173 RETURN_CASE(PointerKind, BasedOnSegment, "segment based"); 174 RETURN_CASE(PointerKind, BasedOnValue, "value based"); 175 RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based"); 176 RETURN_CASE(PointerKind, BasedOnAddress, "address based"); 177 RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based"); 178 RETURN_CASE(PointerKind, BasedOnType, "type based"); 179 RETURN_CASE(PointerKind, BasedOnSelf, "self based"); 180 RETURN_CASE(PointerKind, Near32, "ptr32"); 181 RETURN_CASE(PointerKind, Far32, "far ptr32"); 182 RETURN_CASE(PointerKind, Near64, "ptr64"); 183 } 184 return formatUnknownEnum(Kind); 185 } 186 187 static std::string memberAttributes(const MemberAttributes &Attrs) { 188 std::vector<std::string> Opts; 189 std::string Access = memberAccess(Attrs.getAccess()); 190 std::string Kind = methodKind(Attrs.getMethodKind()); 191 if (!Access.empty()) 192 Opts.push_back(Access); 193 if (!Kind.empty()) 194 Opts.push_back(Kind); 195 MethodOptions Flags = Attrs.getFlags(); 196 PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo"); 197 PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit"); 198 PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct"); 199 PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated"); 200 PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed"); 201 return join(Opts, " "); 202 } 203 204 static std::string formatPointerAttrs(const PointerRecord &Record) { 205 PointerMode Mode = Record.getMode(); 206 PointerOptions Opts = Record.getOptions(); 207 PointerKind Kind = Record.getPointerKind(); 208 return std::string(formatv("mode = {0}, opts = {1}, kind = {2}", 209 formatPointerMode(Mode), pointerOptions(Opts), 210 pointerKind(Kind))); 211 } 212 213 static std::string formatFunctionOptions(FunctionOptions Options) { 214 std::vector<std::string> Opts; 215 216 PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt"); 217 PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options, 218 "constructor with virtual bases"); 219 PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor"); 220 if (Opts.empty()) 221 return "None"; 222 return join(Opts, " | "); 223 } 224 225 Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { 226 CurrentTypeIndex = Index; 227 // formatLine puts the newline at the beginning, so we use formatLine here 228 // to start a new line, and then individual visit methods use format to 229 // append to the existing line. 230 P.formatLine("{0} | {1} [size = {2}", 231 fmt_align(Index, AlignStyle::Right, Width), 232 formatTypeLeafKind(Record.kind()), Record.length()); 233 if (Hashes) { 234 std::string H; 235 if (Index.toArrayIndex() >= HashValues.size()) { 236 H = "(not present)"; 237 } else { 238 uint32_t Hash = HashValues[Index.toArrayIndex()]; 239 Expected<uint32_t> MaybeHash = hashTypeRecord(Record); 240 if (!MaybeHash) 241 return MaybeHash.takeError(); 242 uint32_t OurHash = *MaybeHash; 243 OurHash %= NumHashBuckets; 244 if (Hash == OurHash) 245 H = "0x" + utohexstr(Hash); 246 else 247 H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash); 248 } 249 P.format(", hash = {0}", H); 250 } 251 if (RefTracker) { 252 if (RefTracker->isTypeReferenced(Index)) 253 P.format(", referenced"); 254 else 255 P.format(", unreferenced"); 256 } 257 P.format("]"); 258 P.Indent(Width + 3); 259 return Error::success(); 260 } 261 262 Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) { 263 P.Unindent(Width + 3); 264 if (RecordBytes) { 265 AutoIndent Indent(P, 9); 266 P.formatBinary("Bytes", Record.RecordData, 0); 267 } 268 return Error::success(); 269 } 270 271 Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) { 272 P.formatLine("- {0}", formatTypeLeafKind(Record.Kind)); 273 return Error::success(); 274 } 275 276 Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) { 277 if (RecordBytes) { 278 AutoIndent Indent(P, 2); 279 P.formatBinary("Bytes", Record.Data, 0); 280 } 281 return Error::success(); 282 } 283 284 StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const { 285 if (TI.isNoneType()) 286 return ""; 287 return Types.getTypeName(TI); 288 } 289 290 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 291 FieldListRecord &FieldList) { 292 if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this)) 293 return EC; 294 295 return Error::success(); 296 } 297 298 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 299 StringIdRecord &String) { 300 P.format(" ID: {0}, String: {1}", String.getId(), String.getString()); 301 return Error::success(); 302 } 303 304 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 305 ArgListRecord &Args) { 306 auto Indices = Args.getIndices(); 307 if (Indices.empty()) 308 return Error::success(); 309 310 auto Max = std::max_element(Indices.begin(), Indices.end()); 311 uint32_t W = NumDigits(Max->getIndex()) + 2; 312 313 for (auto I : Indices) 314 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), 315 getTypeName(I)); 316 return Error::success(); 317 } 318 319 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 320 StringListRecord &Strings) { 321 auto Indices = Strings.getIndices(); 322 if (Indices.empty()) 323 return Error::success(); 324 325 auto Max = std::max_element(Indices.begin(), Indices.end()); 326 uint32_t W = NumDigits(Max->getIndex()) + 2; 327 328 for (auto I : Indices) 329 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), 330 getTypeName(I)); 331 return Error::success(); 332 } 333 334 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 335 ClassRecord &Class) { 336 P.format(" `{0}`", Class.Name); 337 if (Class.hasUniqueName()) 338 P.formatLine("unique name: `{0}`", Class.UniqueName); 339 P.formatLine("vtable: {0}, base list: {1}, field list: {2}", 340 Class.VTableShape, Class.DerivationList, Class.FieldList); 341 P.formatLine("options: {0}, sizeof {1}", 342 formatClassOptions(P.getIndentLevel(), Class.Options, Stream, 343 CurrentTypeIndex), 344 Class.Size); 345 return Error::success(); 346 } 347 348 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 349 UnionRecord &Union) { 350 P.format(" `{0}`", Union.Name); 351 if (Union.hasUniqueName()) 352 P.formatLine("unique name: `{0}`", Union.UniqueName); 353 P.formatLine("field list: {0}", Union.FieldList); 354 P.formatLine("options: {0}, sizeof {1}", 355 formatClassOptions(P.getIndentLevel(), Union.Options, Stream, 356 CurrentTypeIndex), 357 Union.Size); 358 return Error::success(); 359 } 360 361 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { 362 P.format(" `{0}`", Enum.Name); 363 if (Enum.hasUniqueName()) 364 P.formatLine("unique name: `{0}`", Enum.UniqueName); 365 P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList, 366 Enum.UnderlyingType); 367 P.formatLine("options: {0}", 368 formatClassOptions(P.getIndentLevel(), Enum.Options, Stream, 369 CurrentTypeIndex)); 370 return Error::success(); 371 } 372 373 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { 374 if (AT.Name.empty()) { 375 P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size, 376 AT.IndexType, AT.ElementType); 377 } else { 378 P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}", 379 AT.Name, AT.Size, AT.IndexType, AT.ElementType); 380 } 381 return Error::success(); 382 } 383 384 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 385 VFTableRecord &VFT) { 386 P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}", 387 VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable); 388 P.formatLine("method names: "); 389 if (!VFT.MethodNames.empty()) { 390 std::string Sep = 391 formatv("\n{0}", 392 fmt_repeat(' ', P.getIndentLevel() + strlen("method names: "))) 393 .str(); 394 P.print(join(VFT.MethodNames, Sep)); 395 } 396 return Error::success(); 397 } 398 399 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 400 MemberFuncIdRecord &Id) { 401 P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name, 402 Id.FunctionType, Id.ClassType); 403 return Error::success(); 404 } 405 406 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 407 ProcedureRecord &Proc) { 408 P.formatLine("return type = {0}, # args = {1}, param list = {2}", 409 Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList); 410 P.formatLine("calling conv = {0}, options = {1}", 411 formatCallingConvention(Proc.CallConv), 412 formatFunctionOptions(Proc.Options)); 413 return Error::success(); 414 } 415 416 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 417 MemberFunctionRecord &MF) { 418 P.formatLine("return type = {0}, # args = {1}, param list = {2}", 419 MF.ReturnType, MF.ParameterCount, MF.ArgumentList); 420 P.formatLine("class type = {0}, this type = {1}, this adjust = {2}", 421 MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment); 422 P.formatLine("calling conv = {0}, options = {1}", 423 formatCallingConvention(MF.CallConv), 424 formatFunctionOptions(MF.Options)); 425 return Error::success(); 426 } 427 428 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 429 FuncIdRecord &Func) { 430 P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name, 431 Func.FunctionType, Func.ParentScope); 432 return Error::success(); 433 } 434 435 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 436 TypeServer2Record &TS) { 437 P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid); 438 return Error::success(); 439 } 440 441 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 442 PointerRecord &Ptr) { 443 P.formatLine("referent = {0}, {1}", Ptr.ReferentType, 444 formatPointerAttrs(Ptr)); 445 return Error::success(); 446 } 447 448 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 449 ModifierRecord &Mod) { 450 P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType, 451 modifierOptions(Mod.Modifiers)); 452 return Error::success(); 453 } 454 455 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 456 VFTableShapeRecord &Shape) { 457 return Error::success(); 458 } 459 460 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 461 UdtModSourceLineRecord &U) { 462 P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module, 463 U.SourceFile.getIndex(), U.LineNumber); 464 return Error::success(); 465 } 466 467 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 468 UdtSourceLineRecord &U) { 469 P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT, 470 U.SourceFile.getIndex(), U.LineNumber); 471 return Error::success(); 472 } 473 474 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 475 BitFieldRecord &BF) { 476 P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type, 477 BF.BitOffset, BF.BitSize); 478 return Error::success(); 479 } 480 481 Error MinimalTypeDumpVisitor::visitKnownRecord( 482 CVType &CVR, MethodOverloadListRecord &Overloads) { 483 for (auto &M : Overloads.Methods) 484 P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]", 485 M.Type, M.VFTableOffset, memberAttributes(M.Attrs)); 486 return Error::success(); 487 } 488 489 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 490 BuildInfoRecord &BI) { 491 auto Indices = BI.ArgIndices; 492 if (Indices.empty()) 493 return Error::success(); 494 495 auto Max = std::max_element(Indices.begin(), Indices.end()); 496 uint32_t W = NumDigits(Max->getIndex()) + 2; 497 498 for (auto I : Indices) 499 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), 500 getTypeName(I)); 501 return Error::success(); 502 } 503 504 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) { 505 std::string Type = (R.Mode == LabelType::Far) ? "far" : "near"; 506 P.format(" type = {0}", Type); 507 return Error::success(); 508 } 509 510 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 511 PrecompRecord &Precomp) { 512 P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+}," 513 " precomp path = {3}", 514 Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature, 515 Precomp.PrecompFilePath); 516 return Error::success(); 517 } 518 519 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, 520 EndPrecompRecord &EP) { 521 P.format(" signature = {0:X+}", EP.Signature); 522 return Error::success(); 523 } 524 525 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 526 NestedTypeRecord &Nested) { 527 P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type); 528 return Error::success(); 529 } 530 531 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 532 OneMethodRecord &Method) { 533 P.format(" [name = `{0}`]", Method.Name); 534 AutoIndent Indent(P); 535 P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type, 536 Method.VFTableOffset, memberAttributes(Method.Attrs)); 537 return Error::success(); 538 } 539 540 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 541 OverloadedMethodRecord &Method) { 542 P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]", 543 Method.Name, Method.NumOverloads, Method.MethodList); 544 return Error::success(); 545 } 546 547 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 548 DataMemberRecord &Field) { 549 P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name, 550 Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs)); 551 return Error::success(); 552 } 553 554 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 555 StaticDataMemberRecord &Field) { 556 P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type, 557 memberAttributes(Field.Attrs)); 558 return Error::success(); 559 } 560 561 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 562 EnumeratorRecord &Enum) { 563 P.format(" [{0} = {1}]", Enum.Name, 564 toString(Enum.Value, 10, Enum.Value.isSigned())); 565 return Error::success(); 566 } 567 568 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 569 BaseClassRecord &Base) { 570 AutoIndent Indent(P); 571 P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset, 572 memberAttributes(Base.Attrs)); 573 return Error::success(); 574 } 575 576 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 577 VirtualBaseClassRecord &Base) { 578 AutoIndent Indent(P); 579 P.formatLine( 580 "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}", 581 Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex); 582 P.formatLine("attrs = {0}", memberAttributes(Base.Attrs)); 583 return Error::success(); 584 } 585 586 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 587 ListContinuationRecord &Cont) { 588 P.format(" continuation = {0}", Cont.ContinuationIndex); 589 return Error::success(); 590 } 591 592 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, 593 VFPtrRecord &VFP) { 594 P.format(" type = {0}", VFP.Type); 595 return Error::success(); 596 } 597