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