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