//===--  BitcodeReader.cpp - ClangDoc Bitcode Reader ------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "BitcodeReader.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/raw_ostream.h"

namespace clang {
namespace doc {

using Record = llvm::SmallVector<uint64_t, 1024>;

bool decodeRecord(Record R, llvm::SmallVectorImpl<char> &Field,
                  llvm::StringRef Blob) {
  Field.assign(Blob.begin(), Blob.end());
  return true;
}

bool decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) {
  if (R[0] != BitCodeConstants::USRHashSize)
    return false;

  // First position in the record is the length of the following array, so we
  // copy the following elements to the field.
  for (int I = 0, E = R[0]; I < E; ++I)
    Field[I] = R[I + 1];
  return true;
}

bool decodeRecord(Record R, bool &Field, llvm::StringRef Blob) {
  Field = R[0] != 0;
  return true;
}

bool decodeRecord(Record R, int &Field, llvm::StringRef Blob) {
  if (R[0] > INT_MAX)
    return false;
  Field = (int)R[0];
  return true;
}

bool decodeRecord(Record R, AccessSpecifier &Field, llvm::StringRef Blob) {
  switch (R[0]) {
  case AS_public:
  case AS_private:
  case AS_protected:
  case AS_none:
    Field = (AccessSpecifier)R[0];
    return true;
  default:
    return false;
  }
}

bool decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) {
  switch (R[0]) {
  case TTK_Struct:
  case TTK_Interface:
  case TTK_Union:
  case TTK_Class:
  case TTK_Enum:
    Field = (TagTypeKind)R[0];
    return true;
  default:
    return false;
  }
}

bool decodeRecord(Record R, llvm::Optional<Location> &Field,
                  llvm::StringRef Blob) {
  if (R[0] > INT_MAX)
    return false;
  Field.emplace((int)R[0], Blob);
  return true;
}

bool decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) {
  switch (auto IT = static_cast<InfoType>(R[0])) {
  case InfoType::IT_namespace:
  case InfoType::IT_record:
  case InfoType::IT_function:
  case InfoType::IT_default:
  case InfoType::IT_enum:
    Field = IT;
    return true;
  }
  return false;
}

bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) {
  switch (auto F = static_cast<FieldId>(R[0])) {
  case FieldId::F_namespace:
  case FieldId::F_parent:
  case FieldId::F_vparent:
  case FieldId::F_type:
  case FieldId::F_default:
    Field = F;
    return true;
  }
  return false;
}

bool decodeRecord(Record R, llvm::SmallVectorImpl<llvm::SmallString<16>> &Field,
                  llvm::StringRef Blob) {
  Field.push_back(Blob);
  return true;
}

bool decodeRecord(Record R, llvm::SmallVectorImpl<Location> &Field,
                  llvm::StringRef Blob) {
  if (R[0] > INT_MAX)
    return false;
  Field.emplace_back((int)R[0], Blob);
  return true;
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
                 const unsigned VersionNo) {
  if (ID == VERSION && R[0] == VersionNo)
    return true;
  return false;
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
                 NamespaceInfo *I) {
  switch (ID) {
  case NAMESPACE_USR:
    return decodeRecord(R, I->USR, Blob);
  case NAMESPACE_NAME:
    return decodeRecord(R, I->Name, Blob);
  default:
    return false;
  }
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, RecordInfo *I) {
  switch (ID) {
  case RECORD_USR:
    return decodeRecord(R, I->USR, Blob);
  case RECORD_NAME:
    return decodeRecord(R, I->Name, Blob);
  case RECORD_DEFLOCATION:
    return decodeRecord(R, I->DefLoc, Blob);
  case RECORD_LOCATION:
    return decodeRecord(R, I->Loc, Blob);
  case RECORD_TAG_TYPE:
    return decodeRecord(R, I->TagType, Blob);
  default:
    return false;
  }
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, EnumInfo *I) {
  switch (ID) {
  case ENUM_USR:
    return decodeRecord(R, I->USR, Blob);
  case ENUM_NAME:
    return decodeRecord(R, I->Name, Blob);
  case ENUM_DEFLOCATION:
    return decodeRecord(R, I->DefLoc, Blob);
  case ENUM_LOCATION:
    return decodeRecord(R, I->Loc, Blob);
  case ENUM_MEMBER:
    return decodeRecord(R, I->Members, Blob);
  case ENUM_SCOPED:
    return decodeRecord(R, I->Scoped, Blob);
  default:
    return false;
  }
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FunctionInfo *I) {
  switch (ID) {
  case FUNCTION_USR:
    return decodeRecord(R, I->USR, Blob);
  case FUNCTION_NAME:
    return decodeRecord(R, I->Name, Blob);
  case FUNCTION_DEFLOCATION:
    return decodeRecord(R, I->DefLoc, Blob);
  case FUNCTION_LOCATION:
    return decodeRecord(R, I->Loc, Blob);
  case FUNCTION_ACCESS:
    return decodeRecord(R, I->Access, Blob);
  case FUNCTION_IS_METHOD:
    return decodeRecord(R, I->IsMethod, Blob);
  default:
    return false;
  }
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, TypeInfo *I) {
  return true;
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
                 FieldTypeInfo *I) {
  switch (ID) {
  case FIELD_TYPE_NAME:
    return decodeRecord(R, I->Name, Blob);
  default:
    return false;
  }
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
                 MemberTypeInfo *I) {
  switch (ID) {
  case MEMBER_TYPE_NAME:
    return decodeRecord(R, I->Name, Blob);
  case MEMBER_TYPE_ACCESS:
    return decodeRecord(R, I->Access, Blob);
  default:
    return false;
  }
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) {
  switch (ID) {
  case COMMENT_KIND:
    return decodeRecord(R, I->Kind, Blob);
  case COMMENT_TEXT:
    return decodeRecord(R, I->Text, Blob);
  case COMMENT_NAME:
    return decodeRecord(R, I->Name, Blob);
  case COMMENT_DIRECTION:
    return decodeRecord(R, I->Direction, Blob);
  case COMMENT_PARAMNAME:
    return decodeRecord(R, I->ParamName, Blob);
  case COMMENT_CLOSENAME:
    return decodeRecord(R, I->CloseName, Blob);
  case COMMENT_ATTRKEY:
    return decodeRecord(R, I->AttrKeys, Blob);
  case COMMENT_ATTRVAL:
    return decodeRecord(R, I->AttrValues, Blob);
  case COMMENT_ARG:
    return decodeRecord(R, I->Args, Blob);
  case COMMENT_SELFCLOSING:
    return decodeRecord(R, I->SelfClosing, Blob);
  case COMMENT_EXPLICIT:
    return decodeRecord(R, I->Explicit, Blob);
  default:
    return false;
  }
}

bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, Reference *I,
                 FieldId &F) {
  switch (ID) {
  case REFERENCE_USR:
    return decodeRecord(R, I->USR, Blob);
  case REFERENCE_NAME:
    return decodeRecord(R, I->Name, Blob);
  case REFERENCE_TYPE:
    return decodeRecord(R, I->RefType, Blob);
  case REFERENCE_FIELD:
    return decodeRecord(R, F, Blob);
  default:
    return false;
  }
}

template <typename T> CommentInfo *getCommentInfo(T I) {
  llvm::errs() << "Cannot have comment subblock.\n";
  exit(1);
}

template <> CommentInfo *getCommentInfo(FunctionInfo *I) {
  I->Description.emplace_back();
  return &I->Description.back();
}

template <> CommentInfo *getCommentInfo(NamespaceInfo *I) {
  I->Description.emplace_back();
  return &I->Description.back();
}

template <> CommentInfo *getCommentInfo(RecordInfo *I) {
  I->Description.emplace_back();
  return &I->Description.back();
}

template <> CommentInfo *getCommentInfo(EnumInfo *I) {
  I->Description.emplace_back();
  return &I->Description.back();
}

template <> CommentInfo *getCommentInfo(CommentInfo *I) {
  I->Children.emplace_back(llvm::make_unique<CommentInfo>());
  return I->Children.back().get();
}

template <> CommentInfo *getCommentInfo(std::unique_ptr<CommentInfo> &I) {
  return getCommentInfo(I.get());
}

template <typename T, typename TTypeInfo>
void addTypeInfo(T I, TTypeInfo &&TI) {
  llvm::errs() << "Invalid type for info.\n";
  exit(1);
}

template <> void addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) {
  I->Members.emplace_back(std::move(T));
}

template <> void addTypeInfo(FunctionInfo *I, TypeInfo &&T) {
  I->ReturnType = std::move(T);
}

template <> void addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) {
  I->Params.emplace_back(std::move(T));
}

template <typename T> void addReference(T I, Reference &&R, FieldId F) {
  llvm::errs() << "Invalid field type for info.\n";
  exit(1);
}

template <> void addReference(TypeInfo *I, Reference &&R, FieldId F) {
  switch (F) {
  case FieldId::F_type:
    I->Type = std::move(R);
    break;
  default:
    llvm::errs() << "Invalid field type for info.\n";
    exit(1);
  }
}

template <> void addReference(FieldTypeInfo *I, Reference &&R, FieldId F) {
  switch (F) {
  case FieldId::F_type:
    I->Type = std::move(R);
    break;
  default:
    llvm::errs() << "Invalid field type for info.\n";
    exit(1);
  }
}

template <> void addReference(MemberTypeInfo *I, Reference &&R, FieldId F) {
  switch (F) {
  case FieldId::F_type:
    I->Type = std::move(R);
    break;
  default:
    llvm::errs() << "Invalid field type for info.\n";
    exit(1);
  }
}

template <> void addReference(EnumInfo *I, Reference &&R, FieldId F) {
  switch (F) {
  case FieldId::F_namespace:
    I->Namespace.emplace_back(std::move(R));
    break;
  default:
    llvm::errs() << "Invalid field type for info.\n";
    exit(1);
  }
}

template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) {
  switch (F) {
  case FieldId::F_namespace:
    I->Namespace.emplace_back(std::move(R));
    break;
  default:
    llvm::errs() << "Invalid field type for info.\n";
    exit(1);
  }
}

template <> void addReference(FunctionInfo *I, Reference &&R, FieldId F) {
  switch (F) {
  case FieldId::F_namespace:
    I->Namespace.emplace_back(std::move(R));
    break;
  case FieldId::F_parent:
    I->Parent = std::move(R);
    break;
  default:
    llvm::errs() << "Invalid field type for info.\n";
    exit(1);
  }
}

template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) {
  switch (F) {
  case FieldId::F_namespace:
    I->Namespace.emplace_back(std::move(R));
    break;
  case FieldId::F_parent:
    I->Parents.emplace_back(std::move(R));
    break;
  case FieldId::F_vparent:
    I->VirtualParents.emplace_back(std::move(R));
    break;
  default:
    llvm::errs() << "Invalid field type for info.\n";
    exit(1);
  }
}

// Read records from bitcode into a given info.
template <typename T> bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) {
  Record R;
  llvm::StringRef Blob;
  unsigned RecID = Stream.readRecord(ID, R, &Blob);
  return parseRecord(R, RecID, Blob, I);
}

template <> bool ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) {
  Record R;
  llvm::StringRef Blob;
  unsigned RecID = Stream.readRecord(ID, R, &Blob);
  return parseRecord(R, RecID, Blob, I, CurrentReferenceField);
}

// Read a block of records into a single info.
template <typename T> bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) {
  if (Stream.EnterSubBlock(ID))
    return false;

  while (true) {
    unsigned BlockOrCode = 0;
    Cursor Res = skipUntilRecordOrBlock(BlockOrCode);

    switch (Res) {
    case Cursor::BadBlock:
      return false;
    case Cursor::BlockEnd:
      return true;
    case Cursor::BlockBegin:
      if (readSubBlock(BlockOrCode, I))
        continue;
      if (!Stream.SkipBlock())
        return false;
      continue;
    case Cursor::Record:
      break;
    }
    if (!readRecord(BlockOrCode, I))
      return false;
  }
}

template <typename T>
bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
  switch (ID) {
  // Blocks can only have Comment, Reference, or TypeInfo subblocks
  case BI_COMMENT_BLOCK_ID:
    if (readBlock(ID, getCommentInfo(I)))
      return true;
    return false;
  case BI_TYPE_BLOCK_ID: {
    TypeInfo TI;
    if (readBlock(ID, &TI)) {
      addTypeInfo(I, std::move(TI));
      return true;
    }
    return false;
  }
  case BI_FIELD_TYPE_BLOCK_ID: {
    FieldTypeInfo TI;
    if (readBlock(ID, &TI)) {
      addTypeInfo(I, std::move(TI));
      return true;
    }
    return false;
  }
  case BI_MEMBER_TYPE_BLOCK_ID: {
    MemberTypeInfo TI;
    if (readBlock(ID, &TI)) {
      addTypeInfo(I, std::move(TI));
      return true;
    }
    return false;
  }
  case BI_REFERENCE_BLOCK_ID: {
    Reference R;
    if (readBlock(ID, &R)) {
      addReference(I, std::move(R), CurrentReferenceField);
      return true;
    }
    return false;
  }
  default:
    llvm::errs() << "Invalid subblock type.\n";
    return false;
  }
}

ClangDocBitcodeReader::Cursor
ClangDocBitcodeReader::skipUntilRecordOrBlock(unsigned &BlockOrRecordID) {
  BlockOrRecordID = 0;

  while (!Stream.AtEndOfStream()) {
    unsigned Code = Stream.ReadCode();

    switch ((llvm::bitc::FixedAbbrevIDs)Code) {
    case llvm::bitc::ENTER_SUBBLOCK:
      BlockOrRecordID = Stream.ReadSubBlockID();
      return Cursor::BlockBegin;
    case llvm::bitc::END_BLOCK:
      if (Stream.ReadBlockEnd())
        return Cursor::BadBlock;
      return Cursor::BlockEnd;
    case llvm::bitc::DEFINE_ABBREV:
      Stream.ReadAbbrevRecord();
      continue;
    case llvm::bitc::UNABBREV_RECORD:
      return Cursor::BadBlock;
    default:
      BlockOrRecordID = Code;
      return Cursor::Record;
    }
  }
  llvm_unreachable("Premature stream end.");
}

bool ClangDocBitcodeReader::validateStream() {
  if (Stream.AtEndOfStream())
    return false;

  // Sniff for the signature.
  if (Stream.Read(8) != BitCodeConstants::Signature[0] ||
      Stream.Read(8) != BitCodeConstants::Signature[1] ||
      Stream.Read(8) != BitCodeConstants::Signature[2] ||
      Stream.Read(8) != BitCodeConstants::Signature[3])
    return false;
  return true;
}

bool ClangDocBitcodeReader::readBlockInfoBlock() {
  BlockInfo = Stream.ReadBlockInfoBlock();
  if (!BlockInfo)
    return false;
  Stream.setBlockInfo(&*BlockInfo);
  return true;
}

template <typename T>
std::unique_ptr<Info> ClangDocBitcodeReader::createInfo(unsigned ID) {
  std::unique_ptr<Info> I = llvm::make_unique<T>();
  if (readBlock(ID, static_cast<T *>(I.get())))
    return I;
  llvm::errs() << "Error reading from block.\n";
  return nullptr;
}

std::unique_ptr<Info> ClangDocBitcodeReader::readBlockToInfo(unsigned ID) {
  switch (ID) {
  case BI_NAMESPACE_BLOCK_ID:
    return createInfo<NamespaceInfo>(ID);
  case BI_RECORD_BLOCK_ID:
    return createInfo<RecordInfo>(ID);
  case BI_ENUM_BLOCK_ID:
    return createInfo<EnumInfo>(ID);
  case BI_FUNCTION_BLOCK_ID:
    return createInfo<FunctionInfo>(ID);
  default:
    llvm::errs() << "Error reading from block.\n";
    return nullptr;
  }
}

// Entry point
std::vector<std::unique_ptr<Info>> ClangDocBitcodeReader::readBitcode() {
  std::vector<std::unique_ptr<Info>> Infos;
  if (!validateStream())
    return Infos;

  // Read the top level blocks.
  while (!Stream.AtEndOfStream()) {
    unsigned Code = Stream.ReadCode();
    if (Code != llvm::bitc::ENTER_SUBBLOCK)
      return Infos;

    unsigned ID = Stream.ReadSubBlockID();
    switch (ID) {
    // NamedType and Comment blocks should not appear at the top level
    case BI_TYPE_BLOCK_ID:
    case BI_FIELD_TYPE_BLOCK_ID:
    case BI_MEMBER_TYPE_BLOCK_ID:
    case BI_COMMENT_BLOCK_ID:
    case BI_REFERENCE_BLOCK_ID:
      llvm::errs() << "Invalid top level block.\n";
      return Infos;
    case BI_NAMESPACE_BLOCK_ID:
    case BI_RECORD_BLOCK_ID:
    case BI_ENUM_BLOCK_ID:
    case BI_FUNCTION_BLOCK_ID:
      if (std::unique_ptr<Info> I = readBlockToInfo(ID)) {
        Infos.emplace_back(std::move(I));
      }
      return Infos;
    case BI_VERSION_BLOCK_ID:
      if (readBlock(ID, VersionNumber))
        continue;
      return Infos;
    case llvm::bitc::BLOCKINFO_BLOCK_ID:
      if (readBlockInfoBlock())
        continue;
      return Infos;
    default:
      if (!Stream.SkipBlock())
        continue;
    }
  }
  return Infos;
}

} // namespace doc
} // namespace clang
