| //===- TypeRecordMapping.cpp ------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/DebugInfo/CodeView/EnumTables.h" |
| #include "llvm/Support/MD5.h" |
| |
| using namespace llvm; |
| using namespace llvm::codeview; |
| |
| namespace { |
| |
| #define error(X) \ |
| if (auto EC = X) \ |
| return EC; |
| |
| static const EnumEntry<TypeLeafKind> LeafTypeNames[] = { |
| #define CV_TYPE(enum, val) {#enum, enum}, |
| #include "llvm/DebugInfo/CodeView/CodeViewTypes.def" |
| }; |
| |
| static StringRef getLeafTypeName(TypeLeafKind LT) { |
| switch (LT) { |
| #define TYPE_RECORD(ename, value, name) \ |
| case ename: \ |
| return #name; |
| #include "llvm/DebugInfo/CodeView/CodeViewTypes.def" |
| default: |
| break; |
| } |
| return "UnknownLeaf"; |
| } |
| |
| template <typename T> |
| static bool compEnumNames(const EnumEntry<T> &lhs, const EnumEntry<T> &rhs) { |
| return lhs.Name < rhs.Name; |
| } |
| |
| template <typename T, typename TFlag> |
| static std::string getFlagNames(CodeViewRecordIO &IO, T Value, |
| ArrayRef<EnumEntry<TFlag>> Flags) { |
| if (!IO.isStreaming()) |
| return std::string(""); |
| typedef EnumEntry<TFlag> FlagEntry; |
| typedef SmallVector<FlagEntry, 10> FlagVector; |
| FlagVector SetFlags; |
| for (const auto &Flag : Flags) { |
| if (Flag.Value == 0) |
| continue; |
| if ((Value & Flag.Value) == Flag.Value) { |
| SetFlags.push_back(Flag); |
| } |
| } |
| |
| llvm::sort(SetFlags, &compEnumNames<TFlag>); |
| |
| std::string FlagLabel; |
| bool FirstOcc = true; |
| for (const auto &Flag : SetFlags) { |
| if (FirstOcc) |
| FirstOcc = false; |
| else |
| FlagLabel += (" | "); |
| |
| FlagLabel += (Flag.Name.str() + " (0x" + utohexstr(Flag.Value) + ")"); |
| } |
| |
| if (!FlagLabel.empty()) { |
| std::string LabelWithBraces(" ( "); |
| LabelWithBraces += FlagLabel + " )"; |
| return LabelWithBraces; |
| } else |
| return FlagLabel; |
| } |
| |
| template <typename T, typename TEnum> |
| static StringRef getEnumName(CodeViewRecordIO &IO, T Value, |
| ArrayRef<EnumEntry<TEnum>> EnumValues) { |
| if (!IO.isStreaming()) |
| return ""; |
| StringRef Name; |
| for (const auto &EnumItem : EnumValues) { |
| if (EnumItem.Value == Value) { |
| Name = EnumItem.Name; |
| break; |
| } |
| } |
| |
| return Name; |
| } |
| |
| static std::string getMemberAttributes(CodeViewRecordIO &IO, |
| MemberAccess Access, MethodKind Kind, |
| MethodOptions Options) { |
| if (!IO.isStreaming()) |
| return ""; |
| std::string AccessSpecifier = std::string( |
| getEnumName(IO, uint8_t(Access), makeArrayRef(getMemberAccessNames()))); |
| std::string MemberAttrs(AccessSpecifier); |
| if (Kind != MethodKind::Vanilla) { |
| std::string MethodKind = std::string( |
| getEnumName(IO, unsigned(Kind), makeArrayRef(getMemberKindNames()))); |
| MemberAttrs += ", " + MethodKind; |
| } |
| if (Options != MethodOptions::None) { |
| std::string MethodOptions = getFlagNames( |
| IO, unsigned(Options), makeArrayRef(getMethodOptionNames())); |
| MemberAttrs += ", " + MethodOptions; |
| } |
| return MemberAttrs; |
| } |
| |
| struct MapOneMethodRecord { |
| explicit MapOneMethodRecord(bool IsFromOverloadList) |
| : IsFromOverloadList(IsFromOverloadList) {} |
| |
| Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const { |
| std::string Attrs = getMemberAttributes( |
| IO, Method.getAccess(), Method.getMethodKind(), Method.getOptions()); |
| error(IO.mapInteger(Method.Attrs.Attrs, "Attrs: " + Attrs)); |
| if (IsFromOverloadList) { |
| uint16_t Padding = 0; |
| error(IO.mapInteger(Padding)); |
| } |
| error(IO.mapInteger(Method.Type, "Type")); |
| if (Method.isIntroducingVirtual()) { |
| error(IO.mapInteger(Method.VFTableOffset, "VFTableOffset")); |
| } else if (IO.isReading()) |
| Method.VFTableOffset = -1; |
| |
| if (!IsFromOverloadList) |
| error(IO.mapStringZ(Method.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| private: |
| bool IsFromOverloadList; |
| }; |
| } // namespace |
| |
| // Computes a string representation of a hash of the specified name, suitable |
| // for use when emitting CodeView type names. |
| static void computeHashString(StringRef Name, |
| SmallString<32> &StringifiedHash) { |
| llvm::MD5 Hash; |
| llvm::MD5::MD5Result Result; |
| Hash.update(Name); |
| Hash.final(Result); |
| Hash.stringifyResult(Result, StringifiedHash); |
| } |
| |
| static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name, |
| StringRef &UniqueName, bool HasUniqueName) { |
| if (IO.isWriting()) { |
| // Try to be smart about what we write here. We can't write anything too |
| // large, so if we're going to go over the limit, replace lengthy names with |
| // a stringified hash value. |
| size_t BytesLeft = IO.maxFieldLength(); |
| if (HasUniqueName) { |
| size_t BytesNeeded = Name.size() + UniqueName.size() + 2; |
| if (BytesNeeded > BytesLeft) { |
| // The minimum space required for emitting hashes of both names. |
| assert(BytesLeft >= 70); |
| |
| // Replace the entire unique name with a hash of the unique name. |
| SmallString<32> Hash; |
| computeHashString(UniqueName, Hash); |
| std::string UniqueB = Twine("??@" + Hash + "@").str(); |
| assert(UniqueB.size() == 36); |
| |
| // Truncate the name if necessary and append a hash of the name. |
| // The name length, hash included, is limited to 4096 bytes. |
| const size_t MaxTakeN = 4096; |
| size_t TakeN = std::min(MaxTakeN, BytesLeft - UniqueB.size() - 2) - 32; |
| computeHashString(Name, Hash); |
| std::string NameB = (Name.take_front(TakeN) + Hash).str(); |
| |
| StringRef N = NameB; |
| StringRef U = UniqueB; |
| error(IO.mapStringZ(N)); |
| error(IO.mapStringZ(U)); |
| } else { |
| error(IO.mapStringZ(Name)); |
| error(IO.mapStringZ(UniqueName)); |
| } |
| } else { |
| // Cap the length of the string at however many bytes we have available, |
| // plus one for the required null terminator. |
| auto N = StringRef(Name).take_front(BytesLeft - 1); |
| error(IO.mapStringZ(N)); |
| } |
| } else { |
| // Reading & Streaming mode come after writing mode is executed for each |
| // record. Truncating large names are done during writing, so its not |
| // necessary to do it while reading or streaming. |
| error(IO.mapStringZ(Name, "Name")); |
| if (HasUniqueName) |
| error(IO.mapStringZ(UniqueName, "LinkageName")); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitTypeBegin(CVType &CVR) { |
| assert(!TypeKind.hasValue() && "Already in a type mapping!"); |
| assert(!MemberKind.hasValue() && "Already in a member mapping!"); |
| |
| // FieldList and MethodList records can be any length because they can be |
| // split with continuation records. All other record types cannot be |
| // longer than the maximum record length. |
| Optional<uint32_t> MaxLen; |
| if (CVR.kind() != TypeLeafKind::LF_FIELDLIST && |
| CVR.kind() != TypeLeafKind::LF_METHODLIST) |
| MaxLen = MaxRecordLength - sizeof(RecordPrefix); |
| error(IO.beginRecord(MaxLen)); |
| TypeKind = CVR.kind(); |
| |
| if (IO.isStreaming()) { |
| auto RecordKind = CVR.kind(); |
| uint16_t RecordLen = CVR.length() - 2; |
| std::string RecordKindName = std::string( |
| getEnumName(IO, unsigned(RecordKind), makeArrayRef(LeafTypeNames))); |
| error(IO.mapInteger(RecordLen, "Record length")); |
| error(IO.mapEnum(RecordKind, "Record kind: " + RecordKindName)); |
| } |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitTypeBegin(CVType &CVR, TypeIndex Index) { |
| if (IO.isStreaming()) |
| IO.emitRawComment(" " + getLeafTypeName(CVR.kind()) + " (0x" + |
| utohexstr(Index.getIndex()) + ")"); |
| return visitTypeBegin(CVR); |
| } |
| |
| Error TypeRecordMapping::visitTypeEnd(CVType &Record) { |
| assert(TypeKind.hasValue() && "Not in a type mapping!"); |
| assert(!MemberKind.hasValue() && "Still in a member mapping!"); |
| |
| error(IO.endRecord()); |
| |
| TypeKind.reset(); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitMemberBegin(CVMemberRecord &Record) { |
| assert(TypeKind.hasValue() && "Not in a type mapping!"); |
| assert(!MemberKind.hasValue() && "Already in a member mapping!"); |
| |
| // The largest possible subrecord is one in which there is a record prefix, |
| // followed by the subrecord, followed by a continuation, and that entire |
| // sequence spawns `MaxRecordLength` bytes. So the record's length is |
| // calculated as follows. |
| |
| constexpr uint32_t ContinuationLength = 8; |
| error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix) - |
| ContinuationLength)); |
| |
| MemberKind = Record.Kind; |
| if (IO.isStreaming()) { |
| std::string MemberKindName = std::string(getLeafTypeName(Record.Kind)); |
| MemberKindName += |
| " ( " + |
| (getEnumName(IO, unsigned(Record.Kind), makeArrayRef(LeafTypeNames))) |
| .str() + |
| " )"; |
| error(IO.mapEnum(Record.Kind, "Member kind: " + MemberKindName)); |
| } |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitMemberEnd(CVMemberRecord &Record) { |
| assert(TypeKind.hasValue() && "Not in a type mapping!"); |
| assert(MemberKind.hasValue() && "Not in a member mapping!"); |
| |
| if (IO.isReading()) { |
| if (auto EC = IO.skipPadding()) |
| return EC; |
| } |
| |
| MemberKind.reset(); |
| error(IO.endRecord()); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) { |
| std::string ModifierNames = |
| getFlagNames(IO, static_cast<uint16_t>(Record.Modifiers), |
| makeArrayRef(getTypeModifierNames())); |
| error(IO.mapInteger(Record.ModifiedType, "ModifiedType")); |
| error(IO.mapEnum(Record.Modifiers, "Modifiers" + ModifierNames)); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| ProcedureRecord &Record) { |
| std::string CallingConvName = std::string(getEnumName( |
| IO, uint8_t(Record.CallConv), makeArrayRef(getCallingConventions()))); |
| std::string FuncOptionNames = |
| getFlagNames(IO, static_cast<uint16_t>(Record.Options), |
| makeArrayRef(getFunctionOptionEnum())); |
| error(IO.mapInteger(Record.ReturnType, "ReturnType")); |
| error(IO.mapEnum(Record.CallConv, "CallingConvention: " + CallingConvName)); |
| error(IO.mapEnum(Record.Options, "FunctionOptions" + FuncOptionNames)); |
| error(IO.mapInteger(Record.ParameterCount, "NumParameters")); |
| error(IO.mapInteger(Record.ArgumentList, "ArgListType")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| MemberFunctionRecord &Record) { |
| std::string CallingConvName = std::string(getEnumName( |
| IO, uint8_t(Record.CallConv), makeArrayRef(getCallingConventions()))); |
| std::string FuncOptionNames = |
| getFlagNames(IO, static_cast<uint16_t>(Record.Options), |
| makeArrayRef(getFunctionOptionEnum())); |
| error(IO.mapInteger(Record.ReturnType, "ReturnType")); |
| error(IO.mapInteger(Record.ClassType, "ClassType")); |
| error(IO.mapInteger(Record.ThisType, "ThisType")); |
| error(IO.mapEnum(Record.CallConv, "CallingConvention: " + CallingConvName)); |
| error(IO.mapEnum(Record.Options, "FunctionOptions" + FuncOptionNames)); |
| error(IO.mapInteger(Record.ParameterCount, "NumParameters")); |
| error(IO.mapInteger(Record.ArgumentList, "ArgListType")); |
| error(IO.mapInteger(Record.ThisPointerAdjustment, "ThisAdjustment")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) { |
| error(IO.mapVectorN<uint32_t>( |
| Record.ArgIndices, |
| [](CodeViewRecordIO &IO, TypeIndex &N) { |
| return IO.mapInteger(N, "Argument"); |
| }, |
| "NumArgs")); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| StringListRecord &Record) { |
| error(IO.mapVectorN<uint32_t>( |
| Record.StringIndices, |
| [](CodeViewRecordIO &IO, TypeIndex &N) { |
| return IO.mapInteger(N, "Strings"); |
| }, |
| "NumStrings")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) { |
| |
| SmallString<128> Attr("Attrs: "); |
| |
| if (IO.isStreaming()) { |
| std::string PtrType = |
| std::string(getEnumName(IO, unsigned(Record.getPointerKind()), |
| makeArrayRef(getPtrKindNames()))); |
| Attr += "[ Type: " + PtrType; |
| |
| std::string PtrMode = std::string(getEnumName( |
| IO, unsigned(Record.getMode()), makeArrayRef(getPtrModeNames()))); |
| Attr += ", Mode: " + PtrMode; |
| |
| auto PtrSizeOf = Record.getSize(); |
| Attr += ", SizeOf: " + itostr(PtrSizeOf); |
| |
| if (Record.isFlat()) |
| Attr += ", isFlat"; |
| if (Record.isConst()) |
| Attr += ", isConst"; |
| if (Record.isVolatile()) |
| Attr += ", isVolatile"; |
| if (Record.isUnaligned()) |
| Attr += ", isUnaligned"; |
| if (Record.isRestrict()) |
| Attr += ", isRestricted"; |
| if (Record.isLValueReferenceThisPtr()) |
| Attr += ", isThisPtr&"; |
| if (Record.isRValueReferenceThisPtr()) |
| Attr += ", isThisPtr&&"; |
| Attr += " ]"; |
| } |
| |
| error(IO.mapInteger(Record.ReferentType, "PointeeType")); |
| error(IO.mapInteger(Record.Attrs, Attr)); |
| |
| if (Record.isPointerToMember()) { |
| if (IO.isReading()) |
| Record.MemberInfo.emplace(); |
| |
| MemberPointerInfo &M = *Record.MemberInfo; |
| error(IO.mapInteger(M.ContainingType, "ClassType")); |
| std::string PtrMemberGetRepresentation = std::string(getEnumName( |
| IO, uint16_t(M.Representation), makeArrayRef(getPtrMemberRepNames()))); |
| error(IO.mapEnum(M.Representation, |
| "Representation: " + PtrMemberGetRepresentation)); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) { |
| error(IO.mapInteger(Record.ElementType, "ElementType")); |
| error(IO.mapInteger(Record.IndexType, "IndexType")); |
| error(IO.mapEncodedInteger(Record.Size, "SizeOf")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) { |
| assert((CVR.kind() == TypeLeafKind::LF_STRUCTURE) || |
| (CVR.kind() == TypeLeafKind::LF_CLASS) || |
| (CVR.kind() == TypeLeafKind::LF_INTERFACE)); |
| |
| std::string PropertiesNames = |
| getFlagNames(IO, static_cast<uint16_t>(Record.Options), |
| makeArrayRef(getClassOptionNames())); |
| error(IO.mapInteger(Record.MemberCount, "MemberCount")); |
| error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); |
| error(IO.mapInteger(Record.FieldList, "FieldList")); |
| error(IO.mapInteger(Record.DerivationList, "DerivedFrom")); |
| error(IO.mapInteger(Record.VTableShape, "VShape")); |
| error(IO.mapEncodedInteger(Record.Size, "SizeOf")); |
| error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, |
| Record.hasUniqueName())); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) { |
| std::string PropertiesNames = |
| getFlagNames(IO, static_cast<uint16_t>(Record.Options), |
| makeArrayRef(getClassOptionNames())); |
| error(IO.mapInteger(Record.MemberCount, "MemberCount")); |
| error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); |
| error(IO.mapInteger(Record.FieldList, "FieldList")); |
| error(IO.mapEncodedInteger(Record.Size, "SizeOf")); |
| error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, |
| Record.hasUniqueName())); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) { |
| std::string PropertiesNames = |
| getFlagNames(IO, static_cast<uint16_t>(Record.Options), |
| makeArrayRef(getClassOptionNames())); |
| error(IO.mapInteger(Record.MemberCount, "NumEnumerators")); |
| error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames)); |
| error(IO.mapInteger(Record.UnderlyingType, "UnderlyingType")); |
| error(IO.mapInteger(Record.FieldList, "FieldListType")); |
| error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, |
| Record.hasUniqueName())); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) { |
| error(IO.mapInteger(Record.Type, "Type")); |
| error(IO.mapInteger(Record.BitSize, "BitSize")); |
| error(IO.mapInteger(Record.BitOffset, "BitOffset")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| VFTableShapeRecord &Record) { |
| uint16_t Size; |
| if (!IO.isReading()) { |
| ArrayRef<VFTableSlotKind> Slots = Record.getSlots(); |
| Size = Slots.size(); |
| error(IO.mapInteger(Size, "VFEntryCount")); |
| |
| for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) { |
| uint8_t Byte = static_cast<uint8_t>(Slots[SlotIndex]) << 4; |
| if ((SlotIndex + 1) < Slots.size()) { |
| Byte |= static_cast<uint8_t>(Slots[SlotIndex + 1]); |
| } |
| error(IO.mapInteger(Byte)); |
| } |
| } else { |
| error(IO.mapInteger(Size)); |
| for (uint16_t I = 0; I < Size; I += 2) { |
| uint8_t Byte; |
| error(IO.mapInteger(Byte)); |
| Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte & 0xF)); |
| if ((I + 1) < Size) |
| Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte >> 4)); |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) { |
| error(IO.mapInteger(Record.CompleteClass, "CompleteClass")); |
| error(IO.mapInteger(Record.OverriddenVFTable, "OverriddenVFTable")); |
| error(IO.mapInteger(Record.VFPtrOffset, "VFPtrOffset")); |
| uint32_t NamesLen = 0; |
| if (!IO.isReading()) { |
| for (auto Name : Record.MethodNames) |
| NamesLen += Name.size() + 1; |
| } |
| error(IO.mapInteger(NamesLen)); |
| error(IO.mapVectorTail( |
| Record.MethodNames, |
| [](CodeViewRecordIO &IO, StringRef &S) { |
| return IO.mapStringZ(S, "MethodName"); |
| }, |
| "VFTableName")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) { |
| error(IO.mapInteger(Record.Id, "Id")); |
| error(IO.mapStringZ(Record.String, "StringData")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| UdtSourceLineRecord &Record) { |
| error(IO.mapInteger(Record.UDT, "UDT")); |
| error(IO.mapInteger(Record.SourceFile, "SourceFile")); |
| error(IO.mapInteger(Record.LineNumber, "LineNumber")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| UdtModSourceLineRecord &Record) { |
| error(IO.mapInteger(Record.UDT, "UDT")); |
| error(IO.mapInteger(Record.SourceFile, "SourceFile")); |
| error(IO.mapInteger(Record.LineNumber, "LineNumber")); |
| error(IO.mapInteger(Record.Module, "Module")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) { |
| error(IO.mapInteger(Record.ParentScope, "ParentScope")); |
| error(IO.mapInteger(Record.FunctionType, "FunctionType")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| MemberFuncIdRecord &Record) { |
| error(IO.mapInteger(Record.ClassType, "ClassType")); |
| error(IO.mapInteger(Record.FunctionType, "FunctionType")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| BuildInfoRecord &Record) { |
| error(IO.mapVectorN<uint16_t>( |
| Record.ArgIndices, |
| [](CodeViewRecordIO &IO, TypeIndex &N) { |
| return IO.mapInteger(N, "Argument"); |
| }, |
| "NumArgs")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| MethodOverloadListRecord &Record) { |
| // TODO: Split the list into multiple records if it's longer than 64KB, using |
| // a subrecord of TypeRecordKind::Index to chain the records together. |
| error(IO.mapVectorTail(Record.Methods, MapOneMethodRecord(true), "Method")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| FieldListRecord &Record) { |
| if (IO.isStreaming()) { |
| if (auto EC = codeview::visitMemberRecordStream(Record.Data, *this)) |
| return EC; |
| } else |
| error(IO.mapByteVectorTail(Record.Data)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| TypeServer2Record &Record) { |
| error(IO.mapGuid(Record.Guid, "Guid")); |
| error(IO.mapInteger(Record.Age, "Age")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) { |
| std::string ModeName = std::string( |
| getEnumName(IO, uint16_t(Record.Mode), makeArrayRef(getLabelTypeEnum()))); |
| error(IO.mapEnum(Record.Mode, "Mode: " + ModeName)); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| BaseClassRecord &Record) { |
| std::string Attrs = getMemberAttributes( |
| IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); |
| error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); |
| error(IO.mapInteger(Record.Type, "BaseType")); |
| error(IO.mapEncodedInteger(Record.Offset, "BaseOffset")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| EnumeratorRecord &Record) { |
| std::string Attrs = getMemberAttributes( |
| IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); |
| error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); |
| |
| // FIXME: Handle full APInt such as __int128. |
| error(IO.mapEncodedInteger(Record.Value, "EnumValue")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| DataMemberRecord &Record) { |
| std::string Attrs = getMemberAttributes( |
| IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); |
| error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); |
| error(IO.mapInteger(Record.Type, "Type")); |
| error(IO.mapEncodedInteger(Record.FieldOffset, "FieldOffset")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| OverloadedMethodRecord &Record) { |
| error(IO.mapInteger(Record.NumOverloads, "MethodCount")); |
| error(IO.mapInteger(Record.MethodList, "MethodListIndex")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| OneMethodRecord &Record) { |
| const bool IsFromOverloadList = (TypeKind == LF_METHODLIST); |
| MapOneMethodRecord Mapper(IsFromOverloadList); |
| return Mapper(IO, Record); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| NestedTypeRecord &Record) { |
| uint16_t Padding = 0; |
| error(IO.mapInteger(Padding, "Padding")); |
| error(IO.mapInteger(Record.Type, "Type")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| StaticDataMemberRecord &Record) { |
| |
| std::string Attrs = getMemberAttributes( |
| IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); |
| error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); |
| error(IO.mapInteger(Record.Type, "Type")); |
| error(IO.mapStringZ(Record.Name, "Name")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| VirtualBaseClassRecord &Record) { |
| |
| std::string Attrs = getMemberAttributes( |
| IO, Record.getAccess(), MethodKind::Vanilla, MethodOptions::None); |
| error(IO.mapInteger(Record.Attrs.Attrs, "Attrs: " + Attrs)); |
| error(IO.mapInteger(Record.BaseType, "BaseType")); |
| error(IO.mapInteger(Record.VBPtrType, "VBPtrType")); |
| error(IO.mapEncodedInteger(Record.VBPtrOffset, "VBPtrOffset")); |
| error(IO.mapEncodedInteger(Record.VTableIndex, "VBTableIndex")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| VFPtrRecord &Record) { |
| uint16_t Padding = 0; |
| error(IO.mapInteger(Padding, "Padding")); |
| error(IO.mapInteger(Record.Type, "Type")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| ListContinuationRecord &Record) { |
| uint16_t Padding = 0; |
| error(IO.mapInteger(Padding, "Padding")); |
| error(IO.mapInteger(Record.ContinuationIndex, "ContinuationIndex")); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| PrecompRecord &Precomp) { |
| error(IO.mapInteger(Precomp.StartTypeIndex, "StartIndex")); |
| error(IO.mapInteger(Precomp.TypesCount, "Count")); |
| error(IO.mapInteger(Precomp.Signature, "Signature")); |
| error(IO.mapStringZ(Precomp.PrecompFilePath, "PrecompFile")); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| EndPrecompRecord &EndPrecomp) { |
| error(IO.mapInteger(EndPrecomp.Signature, "Signature")); |
| return Error::success(); |
| } |