//===- PDBTypes.h - Defines enums for various fields contained in PDB ----====//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFO_PDB_PDBTYPES_H
#define LLVM_DEBUGINFO_PDB_PDBTYPES_H

#include "llvm/ADT/APFloat.h"
#include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
#include "llvm/DebugInfo/PDB/IPDBFrameData.h"
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>

namespace llvm {
namespace pdb {

typedef uint32_t SymIndexId;

class IPDBDataStream;
class IPDBInjectedSource;
class IPDBLineNumber;
class IPDBSectionContrib;
class IPDBSession;
class IPDBSourceFile;
class IPDBTable;
class PDBSymDumper;
class PDBSymbol;
class PDBSymbolExe;
class PDBSymbolCompiland;
class PDBSymbolCompilandDetails;
class PDBSymbolCompilandEnv;
class PDBSymbolFunc;
class PDBSymbolBlock;
class PDBSymbolData;
class PDBSymbolAnnotation;
class PDBSymbolLabel;
class PDBSymbolPublicSymbol;
class PDBSymbolTypeUDT;
class PDBSymbolTypeEnum;
class PDBSymbolTypeFunctionSig;
class PDBSymbolTypePointer;
class PDBSymbolTypeArray;
class PDBSymbolTypeBuiltin;
class PDBSymbolTypeTypedef;
class PDBSymbolTypeBaseClass;
class PDBSymbolTypeFriend;
class PDBSymbolTypeFunctionArg;
class PDBSymbolFuncDebugStart;
class PDBSymbolFuncDebugEnd;
class PDBSymbolUsingNamespace;
class PDBSymbolTypeVTableShape;
class PDBSymbolTypeVTable;
class PDBSymbolCustom;
class PDBSymbolThunk;
class PDBSymbolTypeCustom;
class PDBSymbolTypeManaged;
class PDBSymbolTypeDimension;
class PDBSymbolUnknown;

using IPDBEnumSymbols = IPDBEnumChildren<PDBSymbol>;
using IPDBEnumSourceFiles = IPDBEnumChildren<IPDBSourceFile>;
using IPDBEnumDataStreams = IPDBEnumChildren<IPDBDataStream>;
using IPDBEnumLineNumbers = IPDBEnumChildren<IPDBLineNumber>;
using IPDBEnumTables = IPDBEnumChildren<IPDBTable>;
using IPDBEnumInjectedSources = IPDBEnumChildren<IPDBInjectedSource>;
using IPDBEnumSectionContribs = IPDBEnumChildren<IPDBSectionContrib>;
using IPDBEnumFrameData = IPDBEnumChildren<IPDBFrameData>;

/// Specifies which PDB reader implementation is to be used.  Only a value
/// of PDB_ReaderType::DIA is currently supported, but Native is in the works.
enum class PDB_ReaderType {
  DIA = 0,
  Native = 1,
};

/// An enumeration indicating the type of data contained in this table.
enum class PDB_TableType {
  TableInvalid = 0,
  Symbols,
  SourceFiles,
  LineNumbers,
  SectionContribs,
  Segments,
  InjectedSources,
  FrameData,
  InputAssemblyFiles,
  Dbg
};

/// Defines flags used for enumerating child symbols.  This corresponds to the
/// NameSearchOptions enumeration which is documented here:
/// https://msdn.microsoft.com/en-us/library/yat28ads.aspx
enum PDB_NameSearchFlags {
  NS_Default = 0x0,
  NS_CaseSensitive = 0x1,
  NS_CaseInsensitive = 0x2,
  NS_FileNameExtMatch = 0x4,
  NS_Regex = 0x8,
  NS_UndecoratedName = 0x10,

  // For backward compatibility.
  NS_CaseInFileNameExt = NS_CaseInsensitive | NS_FileNameExtMatch,
  NS_CaseRegex = NS_Regex | NS_CaseSensitive,
  NS_CaseInRex = NS_Regex | NS_CaseInsensitive
};

/// Specifies the hash algorithm that a source file from a PDB was hashed with.
/// This corresponds to the CV_SourceChksum_t enumeration and are documented
/// here: https://msdn.microsoft.com/en-us/library/e96az21x.aspx
enum class PDB_Checksum { None = 0, MD5 = 1, SHA1 = 2, SHA256 = 3 };

/// These values correspond to the CV_CPU_TYPE_e enumeration, and are documented
/// here: https://msdn.microsoft.com/en-us/library/b2fc64ek.aspx
using PDB_Cpu = codeview::CPUType;

enum class PDB_Machine {
  Invalid = 0xffff,
  Unknown = 0x0,
  Am33 = 0x13,
  Amd64 = 0x8664,
  Arm = 0x1C0,
  Arm64 = 0xaa64,
  ArmNT = 0x1C4,
  Ebc = 0xEBC,
  x86 = 0x14C,
  Ia64 = 0x200,
  M32R = 0x9041,
  Mips16 = 0x266,
  MipsFpu = 0x366,
  MipsFpu16 = 0x466,
  PowerPC = 0x1F0,
  PowerPCFP = 0x1F1,
  R4000 = 0x166,
  SH3 = 0x1A2,
  SH3DSP = 0x1A3,
  SH4 = 0x1A6,
  SH5 = 0x1A8,
  Thumb = 0x1C2,
  WceMipsV2 = 0x169
};

// A struct with an inner unnamed enum with explicit underlying type resuls
// in an enum class that can implicitly convert to the underlying type, which
// is convenient for this enum.
struct PDB_SourceCompression {
  enum : uint32_t {
    // No compression. Produced e.g. by `link.exe /natvis:foo.natvis`.
    None,
    // Not known what produces this.
    RunLengthEncoded,
    // Not known what produces this.
    Huffman,
    // Not known what produces this.
    LZ,
    // Produced e.g. by `csc /debug`. The encoded data is its own mini-stream
    // with the following layout (in little endian):
    //   GUID LanguageTypeGuid;
    //   GUID LanguageVendorGuid;
    //   GUID DocumentTypeGuid;
    //   GUID HashFunctionGuid;
    //   uint32_t HashDataSize;
    //   uint32_t CompressedDataSize;
    // Followed by HashDataSize bytes containing a hash checksum,
    // followed by CompressedDataSize bytes containing source contents.
    //
    // CompressedDataSize can be 0, in this case only the hash data is present.
    // (CompressedDataSize is != 0 e.g. if `/embed` is passed to csc.exe.)
    // The compressed data format is:
    //   uint32_t UncompressedDataSize;
    // If UncompressedDataSize is 0, the data is stored uncompressed and
    // CompressedDataSize stores the uncompressed size.
    // If UncompressedDataSize is != 0, then the data is in raw deflate
    // encoding as described in rfc1951.
    //
    // A GUID is 16 bytes, stored in the usual
    //   uint32_t
    //   uint16_t
    //   uint16_t
    //   uint8_t[24]
    // layout.
    //
    // Well-known GUIDs for LanguageTypeGuid are:
    //   63a08714-fc37-11d2-904c-00c04fa302a1 C
    //   3a12d0b7-c26c-11d0-b442-00a0244a1dd2 C++
    //   3f5162f8-07c6-11d3-9053-00c04fa302a1 C#
    //   af046cd1-d0e1-11d2-977c-00a0c9b4d50c Cobol
    //   ab4f38c9-b6e6-43ba-be3b-58080b2ccce3 F#
    //   3a12d0b4-c26c-11d0-b442-00a0244a1dd2 Java
    //   3a12d0b6-c26c-11d0-b442-00a0244a1dd2 JScript
    //   af046cd2-d0e1-11d2-977c-00a0c9b4d50c Pascal
    //   3a12d0b8-c26c-11d0-b442-00a0244a1dd2 Visual Basic
    //
    // Well-known GUIDs for LanguageVendorGuid are:
    //   994b45c4-e6e9-11d2-903f-00c04fa302a1 Microsoft
    //
    // Well-known GUIDs for DocumentTypeGuid are:
    //   5a869d0b-6611-11d3-bd2a-0000f80849bd Text
    //
    // Well-known GUIDs for HashFunctionGuid are:
    //   406ea660-64cf-4c82-b6f0-42d48172a799 MD5    (HashDataSize is 16)
    //   ff1816ec-aa5e-4d10-87f7-6f4963833460 SHA1   (HashDataSize is 20)
    //   8829d00f-11b8-4213-878b-770e8597ac16 SHA256 (HashDataSize is 32)
    DotNet = 101,
  };
};

/// These values correspond to the CV_call_e enumeration, and are documented
/// at the following locations:
///   https://msdn.microsoft.com/en-us/library/b2fc64ek.aspx
///   https://msdn.microsoft.com/en-us/library/windows/desktop/ms680207(v=vs.85).aspx
using PDB_CallingConv = codeview::CallingConvention;

/// These values correspond to the CV_CFL_LANG enumeration, and are documented
/// here: https://msdn.microsoft.com/en-us/library/bw3aekw6.aspx
using PDB_Lang = codeview::SourceLanguage;

/// These values correspond to the DataKind enumeration, and are documented
/// here: https://msdn.microsoft.com/en-us/library/b2x2t313.aspx
enum class PDB_DataKind {
  Unknown,
  Local,
  StaticLocal,
  Param,
  ObjectPtr,
  FileStatic,
  Global,
  Member,
  StaticMember,
  Constant
};

/// These values correspond to the SymTagEnum enumeration, and are documented
/// here: https://msdn.microsoft.com/en-us/library/bkedss5f.aspx
enum class PDB_SymType {
  None,
  Exe,
  Compiland,
  CompilandDetails,
  CompilandEnv,
  Function,
  Block,
  Data,
  Annotation,
  Label,
  PublicSymbol,
  UDT,
  Enum,
  FunctionSig,
  PointerType,
  ArrayType,
  BuiltinType,
  Typedef,
  BaseClass,
  Friend,
  FunctionArg,
  FuncDebugStart,
  FuncDebugEnd,
  UsingNamespace,
  VTableShape,
  VTable,
  Custom,
  Thunk,
  CustomType,
  ManagedType,
  Dimension,
  CallSite,
  InlineSite,
  BaseInterface,
  VectorType,
  MatrixType,
  HLSLType,
  Caller,
  Callee,
  Export,
  HeapAllocationSite,
  CoffGroup,
  Inlinee,
  Max
};

/// These values correspond to the LocationType enumeration, and are documented
/// here: https://msdn.microsoft.com/en-us/library/f57kaez3.aspx
enum class PDB_LocType {
  Null,
  Static,
  TLS,
  RegRel,
  ThisRel,
  Enregistered,
  BitField,
  Slot,
  IlRel,
  MetaData,
  Constant,
  RegRelAliasIndir,
  Max
};

/// These values correspond to the UdtKind enumeration, and are documented
/// here: https://msdn.microsoft.com/en-us/library/wcstk66t.aspx
enum class PDB_UdtType { Struct, Class, Union, Interface };

/// These values correspond to the StackFrameTypeEnum enumeration, and are
/// documented here: https://msdn.microsoft.com/en-us/library/bc5207xw.aspx.
enum class PDB_StackFrameType : uint16_t {
  FPO,
  KernelTrap,
  KernelTSS,
  EBP,
  FrameData,
  Unknown = 0xffff
};

/// These values correspond to the MemoryTypeEnum enumeration, and are
/// documented here: https://msdn.microsoft.com/en-us/library/ms165609.aspx.
enum class PDB_MemoryType : uint16_t {
  Code,
  Data,
  Stack,
  HeapCode,
  Any = 0xffff
};

/// These values correspond to the Basictype enumeration, and are documented
/// here: https://msdn.microsoft.com/en-us/library/4szdtzc3.aspx
enum class PDB_BuiltinType {
  None = 0,
  Void = 1,
  Char = 2,
  WCharT = 3,
  Int = 6,
  UInt = 7,
  Float = 8,
  BCD = 9,
  Bool = 10,
  Long = 13,
  ULong = 14,
  Currency = 25,
  Date = 26,
  Variant = 27,
  Complex = 28,
  Bitfield = 29,
  BSTR = 30,
  HResult = 31,
  Char16 = 32,
  Char32 = 33,
  Char8 = 34,
};

/// These values correspond to the flags that can be combined to control the
/// return of an undecorated name for a C++ decorated name, and are documented
/// here: https://msdn.microsoft.com/en-us/library/kszfk0fs.aspx
enum PDB_UndnameFlags : uint32_t {
  Undname_Complete = 0x0,
  Undname_NoLeadingUnderscores = 0x1,
  Undname_NoMsKeywords = 0x2,
  Undname_NoFuncReturns = 0x4,
  Undname_NoAllocModel = 0x8,
  Undname_NoAllocLang = 0x10,
  Undname_Reserved1 = 0x20,
  Undname_Reserved2 = 0x40,
  Undname_NoThisType = 0x60,
  Undname_NoAccessSpec = 0x80,
  Undname_NoThrowSig = 0x100,
  Undname_NoMemberType = 0x200,
  Undname_NoReturnUDTModel = 0x400,
  Undname_32BitDecode = 0x800,
  Undname_NameOnly = 0x1000,
  Undname_TypeOnly = 0x2000,
  Undname_HaveParams = 0x4000,
  Undname_NoECSU = 0x8000,
  Undname_NoIdentCharCheck = 0x10000,
  Undname_NoPTR64 = 0x20000
};

enum class PDB_MemberAccess { Private = 1, Protected = 2, Public = 3 };

struct VersionInfo {
  uint32_t Major;
  uint32_t Minor;
  uint32_t Build;
  uint32_t QFE;
};

enum PDB_VariantType {
  Empty,
  Unknown,
  Int8,
  Int16,
  Int32,
  Int64,
  Single,
  Double,
  UInt8,
  UInt16,
  UInt32,
  UInt64,
  Bool,
  String
};

struct Variant {
  Variant() = default;

  explicit Variant(bool V) : Type(PDB_VariantType::Bool) { Value.Bool = V; }
  explicit Variant(int8_t V) : Type(PDB_VariantType::Int8) { Value.Int8 = V; }
  explicit Variant(int16_t V) : Type(PDB_VariantType::Int16) {
    Value.Int16 = V;
  }
  explicit Variant(int32_t V) : Type(PDB_VariantType::Int32) {
    Value.Int32 = V;
  }
  explicit Variant(int64_t V) : Type(PDB_VariantType::Int64) {
    Value.Int64 = V;
  }
  explicit Variant(float V) : Type(PDB_VariantType::Single) {
    Value.Single = V;
  }
  explicit Variant(double V) : Type(PDB_VariantType::Double) {
    Value.Double = V;
  }
  explicit Variant(uint8_t V) : Type(PDB_VariantType::UInt8) {
    Value.UInt8 = V;
  }
  explicit Variant(uint16_t V) : Type(PDB_VariantType::UInt16) {
    Value.UInt16 = V;
  }
  explicit Variant(uint32_t V) : Type(PDB_VariantType::UInt32) {
    Value.UInt32 = V;
  }
  explicit Variant(uint64_t V) : Type(PDB_VariantType::UInt64) {
    Value.UInt64 = V;
  }

  Variant(const Variant &Other) {
    *this = Other;
  }

  ~Variant() {
    if (Type == PDB_VariantType::String)
      delete[] Value.String;
  }

  PDB_VariantType Type = PDB_VariantType::Empty;
  union {
    bool Bool;
    int8_t Int8;
    int16_t Int16;
    int32_t Int32;
    int64_t Int64;
    float Single;
    double Double;
    uint8_t UInt8;
    uint16_t UInt16;
    uint32_t UInt32;
    uint64_t UInt64;
    char *String;
  } Value;

  bool isIntegralType() const {
    switch (Type) {
    case Bool:
    case Int8:
    case Int16:
    case Int32:
    case Int64:
    case UInt8:
    case UInt16:
    case UInt32:
    case UInt64:
      return true;
    default:
      return false;
    }
  }

#define VARIANT_WIDTH(Enum, NumBits)                                           \
  case PDB_VariantType::Enum:                                                  \
    return NumBits;

  unsigned getBitWidth() const {
    switch (Type) {
      VARIANT_WIDTH(Bool, 1u)
      VARIANT_WIDTH(Int8, 8u)
      VARIANT_WIDTH(Int16, 16u)
      VARIANT_WIDTH(Int32, 32u)
      VARIANT_WIDTH(Int64, 64u)
      VARIANT_WIDTH(Single, 32u)
      VARIANT_WIDTH(Double, 64u)
      VARIANT_WIDTH(UInt8, 8u)
      VARIANT_WIDTH(UInt16, 16u)
      VARIANT_WIDTH(UInt32, 32u)
      VARIANT_WIDTH(UInt64, 64u)
    default:
      assert(false && "Variant::toAPSInt called on non-numeric type");
      return 0u;
    }
  }

#undef VARIANT_WIDTH

#define VARIANT_APSINT(Enum, NumBits, IsUnsigned)                              \
  case PDB_VariantType::Enum:                                                  \
    return APSInt(                                                             \
        APInt(NumBits, static_cast<uint64_t>(Value.Enum), !IsUnsigned),        \
        IsUnsigned);

  APSInt toAPSInt() const {
    switch (Type) {
      VARIANT_APSINT(Bool, 1u, true)
      VARIANT_APSINT(Int8, 8u, false)
      VARIANT_APSINT(Int16, 16u, false)
      VARIANT_APSINT(Int32, 32u, false)
      VARIANT_APSINT(Int64, 64u, false)
      VARIANT_APSINT(UInt8, 8u, true)
      VARIANT_APSINT(UInt16, 16u, true)
      VARIANT_APSINT(UInt32, 32u, true)
      VARIANT_APSINT(UInt64, 64u, true)
    default:
      assert(false && "Variant::toAPSInt called on non-integral type");
      return APSInt();
    }
  }

#undef VARIANT_APSINT

  APFloat toAPFloat() const {
    // Float constants may be tagged as integers.
    switch (Type) {
    case PDB_VariantType::Single:
    case PDB_VariantType::UInt32:
    case PDB_VariantType::Int32:
      return APFloat(Value.Single);
    case PDB_VariantType::Double:
    case PDB_VariantType::UInt64:
    case PDB_VariantType::Int64:
      return APFloat(Value.Double);
    default:
      assert(false && "Variant::toAPFloat called on non-floating-point type");
      return APFloat::getZero(APFloat::IEEEsingle());
    }
  }

#define VARIANT_EQUAL_CASE(Enum)                                               \
  case PDB_VariantType::Enum:                                                  \
    return Value.Enum == Other.Value.Enum;

  bool operator==(const Variant &Other) const {
    if (Type != Other.Type)
      return false;
    switch (Type) {
      VARIANT_EQUAL_CASE(Bool)
      VARIANT_EQUAL_CASE(Int8)
      VARIANT_EQUAL_CASE(Int16)
      VARIANT_EQUAL_CASE(Int32)
      VARIANT_EQUAL_CASE(Int64)
      VARIANT_EQUAL_CASE(Single)
      VARIANT_EQUAL_CASE(Double)
      VARIANT_EQUAL_CASE(UInt8)
      VARIANT_EQUAL_CASE(UInt16)
      VARIANT_EQUAL_CASE(UInt32)
      VARIANT_EQUAL_CASE(UInt64)
      VARIANT_EQUAL_CASE(String)
    default:
      return true;
    }
  }

#undef VARIANT_EQUAL_CASE

  bool operator!=(const Variant &Other) const { return !(*this == Other); }
  Variant &operator=(const Variant &Other) {
    if (this == &Other)
      return *this;
    if (Type == PDB_VariantType::String)
      delete[] Value.String;
    Type = Other.Type;
    Value = Other.Value;
    if (Other.Type == PDB_VariantType::String &&
        Other.Value.String != nullptr) {
      Value.String = new char[strlen(Other.Value.String) + 1];
      ::strcpy(Value.String, Other.Value.String);
    }
    return *this;
  }
};

} // end namespace pdb
} // end namespace llvm

namespace std {

template <> struct hash<llvm::pdb::PDB_SymType> {
  using argument_type = llvm::pdb::PDB_SymType;
  using result_type = std::size_t;

  result_type operator()(const argument_type &Arg) const {
    return std::hash<int>()(static_cast<int>(Arg));
  }
};

} // end namespace std

#endif // LLVM_DEBUGINFO_PDB_PDBTYPES_H
