//===- NaClBitstreamReader.cpp --------------------------------------------===//
//     NaClBitstreamReader implementation
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/NaCl/NaClBitstreamReader.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace {

static const char *ErrorLevelName[] = {
  "Warning",
  "Error",
  "Fatal"
};

} // End of anonymous namespace.

std::string llvm::naclbitc::getBitAddress(uint64_t Bit) {
  std::string Buffer;
  raw_string_ostream Stream(Buffer);
  Stream << (Bit / 8) << ":" << (Bit % 8);
  return Stream.str();
}

raw_ostream &llvm::naclbitc::ErrorAt(
    raw_ostream &Out, ErrorLevel Level, uint64_t BitPosition) {
  assert(Level < array_lengthof(::ErrorLevelName));
  return Out << ErrorLevelName[Level] << "("
             << naclbitc::getBitAddress(BitPosition) << "): ";
}

//===----------------------------------------------------------------------===//
//  NaClBitstreamCursor implementation
//===----------------------------------------------------------------------===//

void NaClBitstreamCursor::ErrorHandler::
Fatal(const std::string &ErrorMessage) const {
  // Default implementation is simply print message, and the bit where
  // the error occurred.
  std::string Buffer;
  raw_string_ostream StrBuf(Buffer);
  naclbitc::ErrorAt(StrBuf, naclbitc::Fatal,
                    Cursor.getErrorBitNo(getCurrentBitNo())) << ErrorMessage;
  report_fatal_error(StrBuf.str());
}

void NaClBitstreamCursor::reportInvalidAbbrevNumber(unsigned AbbrevNo) const {
  std::string Buffer;
  raw_string_ostream StrBuf(Buffer);
  StrBuf << "Invalid abbreviation # " << AbbrevNo << " defined for record";
  ErrHandler->Fatal(StrBuf.str());
}

void NaClBitstreamCursor::reportInvalidJumpToBit(uint64_t BitNo) const {
  std::string Buffer;
  raw_string_ostream StrBuf(Buffer);
  StrBuf << "Invalid jump to bit " << BitNo;
  ErrHandler->Fatal(StrBuf.str());
}

/// EnterSubBlock - Having read the ENTER_SUBBLOCK abbrevid, enter
/// the block, and return true if the block has an error.
bool NaClBitstreamCursor::EnterSubBlock(unsigned BlockID, unsigned *NumWordsP) {
  const bool IsFixed = true;
  NaClBitcodeSelectorAbbrev
      CodeAbbrev(IsFixed, ReadVBR(naclbitc::CodeLenWidth));
  BlockScope.push_back(Block(BitStream->getBlockInfo(BlockID), CodeAbbrev));
  SkipToFourByteBoundary();
  unsigned NumWords = Read(naclbitc::BlockSizeWidth);
  if (NumWordsP) *NumWordsP = NumWords;

  // Validate that this block is sane.
  if (BlockScope.back().getCodeAbbrev().NumBits == 0 || AtEndOfStream())
    return true;

  return false;
}

void NaClBitstreamCursor::skipAbbreviatedField(const NaClBitCodeAbbrevOp &Op) {
  // Decode the value as we are commanded.
  switch (Op.getEncoding()) {
  case NaClBitCodeAbbrevOp::Literal:
    // No read necessary for literal.
    break;
  case NaClBitCodeAbbrevOp::Fixed:
    (void)Read((unsigned)Op.getValue());
    break;
  case NaClBitCodeAbbrevOp::VBR:
    (void)ReadVBR64((unsigned)Op.getValue());
    break;
  case NaClBitCodeAbbrevOp::Array:
    // This can't happen because the abbreviation must be valid.
    llvm_unreachable("Bad array abbreviation encoding!");
    break;
  case NaClBitCodeAbbrevOp::Char6:
    (void)Read(6);
    break;
  }
}

/// skipRecord - Read the current record and discard it.
void NaClBitstreamCursor::skipRecord(unsigned AbbrevID) {
  // Skip unabbreviated records by reading past their entries.
  if (AbbrevID == naclbitc::UNABBREV_RECORD) {
    unsigned Code = ReadVBR(6);
    (void)Code;
    unsigned NumElts = ReadVBR(6);
    for (unsigned i = 0; i != NumElts; ++i)
      (void)ReadVBR64(6);
    SkipToByteBoundaryIfAligned();
    return;
  }

  const NaClBitCodeAbbrev *Abbv = getAbbrev(AbbrevID);

  for (unsigned i = 0, e = Abbv->getNumOperandInfos(); i != e; ++i) {
    const NaClBitCodeAbbrevOp &Op = Abbv->getOperandInfo(i);
    switch (Op.getEncoding()) {
    default:
      skipAbbreviatedField(Op);
      break;
    case NaClBitCodeAbbrevOp::Literal:
      break;
    case NaClBitCodeAbbrevOp::Array: {
      // Array case.  Read the number of elements as a vbr6.
      unsigned NumElts = ReadVBR(6);

      // Get the element encoding.
      const NaClBitCodeAbbrevOp &EltEnc = Abbv->getOperandInfo(++i);

      // Read all the elements.
      for (; NumElts; --NumElts)
        skipAbbreviatedField(EltEnc);
      break;
    }
    }
  }
  SkipToByteBoundaryIfAligned();
}

bool NaClBitstreamCursor::readRecordAbbrevField(
    const NaClBitCodeAbbrevOp &Op, uint64_t &Value) {
  switch (Op.getEncoding()) {
  case NaClBitCodeAbbrevOp::Literal:
    Value = Op.getValue();
    break;
  case NaClBitCodeAbbrevOp::Array:
    // Returns number of elements in the array.
    Value = ReadVBR(6);
    return true;
  case NaClBitCodeAbbrevOp::Fixed:
    Value = Read((unsigned)Op.getValue());
    break;
  case NaClBitCodeAbbrevOp::VBR:
    Value = ReadVBR64((unsigned)Op.getValue());
    break;
  case NaClBitCodeAbbrevOp::Char6:
    Value = NaClBitCodeAbbrevOp::DecodeChar6(Read(6));
    break;
  }
  return false;
}

uint64_t NaClBitstreamCursor::readArrayAbbreviatedField(
    const NaClBitCodeAbbrevOp &Op) {
  // Decode the value as we are commanded.
  switch (Op.getEncoding()) {
  case NaClBitCodeAbbrevOp::Literal:
    return Op.getValue();
  case NaClBitCodeAbbrevOp::Fixed:
    return Read((unsigned)Op.getValue());
  case NaClBitCodeAbbrevOp::VBR:
    return ReadVBR64((unsigned)Op.getValue());
  case NaClBitCodeAbbrevOp::Array:
    // This can't happen because the abbreviation must be valid.
    llvm_unreachable("Bad array abbreviation encoding!");
    break;
  case NaClBitCodeAbbrevOp::Char6:
    return NaClBitCodeAbbrevOp::DecodeChar6(Read(6));
  }
  llvm_unreachable("Illegal abbreviation encoding for field!");
}

void NaClBitstreamCursor::readArrayAbbrev(
    const NaClBitCodeAbbrevOp &Op, unsigned NumArrayElements,
    SmallVectorImpl<uint64_t> &Vals) {
  for (; NumArrayElements; --NumArrayElements) {
    Vals.push_back(readArrayAbbreviatedField(Op));
  }
}

unsigned NaClBitstreamCursor::readRecord(unsigned AbbrevID,
                                         SmallVectorImpl<uint64_t> &Vals) {
  if (AbbrevID == naclbitc::UNABBREV_RECORD) {
    unsigned Code = ReadVBR(6);
    unsigned NumElts = ReadVBR(6);
    for (unsigned i = 0; i != NumElts; ++i)
      Vals.push_back(ReadVBR64(6));
    SkipToByteBoundaryIfAligned();
    return Code;
  }

  // Read code.
  const NaClBitCodeAbbrev *Abbv = getAbbrev(AbbrevID);
  uint64_t Value;
  unsigned Code;
  if (readRecordAbbrevField(Abbv->getOperandInfo(0), Value)) {
    // Array found, use to read all elements.
    if (Value == 0)
      ErrHandler->Fatal("No code found for record!");
    const NaClBitCodeAbbrevOp &Op = Abbv->getOperandInfo(1);
    Code = readArrayAbbreviatedField(Op);
    readArrayAbbrev(Op, Value - 1, Vals);
    SkipToByteBoundaryIfAligned();
    return Code;
  }
  Code = Value;

  // Read arguments.
  unsigned NumOperands = Abbv->getNumOperandInfos();
  for (unsigned i = 1; i != NumOperands; ++i) {
    if (readRecordAbbrevField(Abbv->getOperandInfo(i), Value)) {
      ++i;
      readArrayAbbrev(Abbv->getOperandInfo(i), Value, Vals);
      SkipToByteBoundaryIfAligned();
      return Code;
    }
    Vals.push_back(Value);
  }
  SkipToByteBoundaryIfAligned();
  return Code;
}


NaClBitCodeAbbrevOp::Encoding NaClBitstreamCursor::
getEncoding(uint64_t Value) {
  if (!NaClBitCodeAbbrevOp::isValidEncoding(Value)) {
    std::string Buffer;
    raw_string_ostream StrBuf(Buffer);
    StrBuf << "Invalid abbreviation encoding specified in bitcode file: "
           << Value;
    ErrHandler->Fatal(StrBuf.str());
  }
  return NaClBitCodeAbbrevOp::Encoding(Value);
}

void NaClBitstreamCursor::ReadAbbrevRecord(bool IsLocal,
                                           NaClAbbrevListener *Listener) {
  NaClBitCodeAbbrev *Abbv = BlockScope.back().appendLocalCreate();
  unsigned NumOpInfo = ReadVBR(5);
  if (Listener) Listener->Values.push_back(NumOpInfo);
  for (unsigned i = 0; i != NumOpInfo; ++i) {
    bool IsLiteral = Read(1) ? true : false;
    if (Listener) Listener->Values.push_back(IsLiteral);
    if (IsLiteral) {
      uint64_t Value = ReadVBR64(8);
      if (Listener) Listener->Values.push_back(Value);
      Abbv->Add(NaClBitCodeAbbrevOp(Value));
      continue;
    }
    NaClBitCodeAbbrevOp::Encoding E = getEncoding(Read(3));
    if (Listener) Listener->Values.push_back(E);
    if (NaClBitCodeAbbrevOp::hasValue(E)) {
      unsigned Data = ReadVBR64(5);
      if (Listener) Listener->Values.push_back(Data);

      // As a special case, handle fixed(0) (i.e., a fixed field with zero bits)
      // and vbr(0) as a literal zero.  This is decoded the same way, and avoids
      // a slow path in Read() to have to handle reading zero bits.
      if ((E == NaClBitCodeAbbrevOp::Fixed || E == NaClBitCodeAbbrevOp::VBR) &&
          Data == 0) {
        if (Listener) Listener->Values.push_back(0);
        Abbv->Add(NaClBitCodeAbbrevOp(0));
        continue;
      }
      if (!NaClBitCodeAbbrevOp::isValid(E, Data)) {
        std::string Buffer;
        raw_string_ostream StrBuf(Buffer);
        StrBuf << "Invalid abbreviation encoding ("
               << NaClBitCodeAbbrevOp::getEncodingName(E)
               << ", " << Data << ")";
        ErrHandler->Fatal(StrBuf.str());
      }
      Abbv->Add(NaClBitCodeAbbrevOp(E, Data));
    } else {
      if (!NaClBitCodeAbbrevOp::isValid(E)) {
        std::string Buffer;
        raw_string_ostream StrBuf(Buffer);
        StrBuf << "Invalid abbreviation encoding ("
               << NaClBitCodeAbbrevOp::getEncodingName(E) << ")";
        ErrHandler->Fatal(StrBuf.str());
      }
      Abbv->Add(NaClBitCodeAbbrevOp(E));
    }
  }
  SkipToByteBoundaryIfAligned();
  if (!Abbv->isValid())
    ErrHandler->Fatal("Invalid abbreviation specified in bitcode file");
  if (Listener) {
    Listener->ProcessAbbreviation(Abbv, IsLocal);
    // Reset record information of the listener.
    Listener->Values.clear();
    Listener->StartBit = GetCurrentBitNo();
  }
}

void NaClBitstreamCursor::SkipAbbrevRecord() {
  unsigned NumOpInfo = ReadVBR(5);
  for (unsigned i = 0; i != NumOpInfo; ++i) {
    bool IsLiteral = Read(1) ? true : false;
    if (IsLiteral) {
      ReadVBR64(8);
      continue;
    }
    NaClBitCodeAbbrevOp::Encoding E = getEncoding(Read(3));
    if (NaClBitCodeAbbrevOp::hasValue(E)) {
      ReadVBR64(5);
    }
  }
  SkipToByteBoundaryIfAligned();
}

namespace {

unsigned ValidBlockIDs[] = {
  naclbitc::BLOCKINFO_BLOCK_ID,
  naclbitc::CONSTANTS_BLOCK_ID,
  naclbitc::FUNCTION_BLOCK_ID,
  naclbitc::GLOBALVAR_BLOCK_ID,
  naclbitc::MODULE_BLOCK_ID,
  naclbitc::TOP_LEVEL_BLOCKID,
  naclbitc::TYPE_BLOCK_ID_NEW,
  naclbitc::VALUE_SYMTAB_BLOCK_ID
};

} // end of anonymous namespace

NaClBitstreamReader::BlockInfoRecordsMap::
BlockInfoRecordsMap() : IsFrozen(false) {
  for (size_t BlockID : ValidBlockIDs) {
    std::unique_ptr<BlockInfo> Info(new BlockInfo(BlockID));
    KnownInfos.emplace(BlockID, std::move(Info));
  }
}

NaClBitstreamReader::BlockInfo * NaClBitstreamReader::BlockInfoRecordsMap::
getOrCreateUnknownBlockInfo(unsigned BlockID) {
  std::unique_lock<std::mutex> Lock(UnknownBlockInfoLock);
  while (true) {
    auto Pos = UnknownInfos.find(BlockID);
    if (Pos != UnknownInfos.end())
      return Pos->second.get();
    // Install, then let next iteration find.
    std::unique_ptr<BlockInfo> Info(new BlockInfo(BlockID));
    UnknownInfos.emplace(BlockID, std::move(Info));
  }
}

NaClBitstreamReader::BlockInfoRecordsMap::UpdateLock::
UpdateLock(BlockInfoRecordsMap &BlockInfoRecords)
    : BlockInfoRecords(BlockInfoRecords),
      Lock(BlockInfoRecords.UpdateRecordsLock) {}

NaClBitstreamReader::BlockInfoRecordsMap::UpdateLock::
~UpdateLock() {
  if (BlockInfoRecords.freeze())
    report_fatal_error("Global abbreviations block frozen while building.");
}

bool NaClBitstreamCursor::ReadBlockInfoBlock(NaClAbbrevListener *Listener) {
  // If this is the second read of the block info block, skip it.
  if (BitStream->BlockInfoRecords->isFrozen())
    return SkipBlock();

  NaClBitstreamReader::BlockInfoRecordsMap::UpdateLock
      Lock(*BitStream->BlockInfoRecords);
  unsigned NumWords;
  if (EnterSubBlock(naclbitc::BLOCKINFO_BLOCK_ID, &NumWords)) return true;

  if (Listener) Listener->BeginBlockInfoBlock(NumWords);

  NaClBitcodeRecordVector Record;
  Block &CurBlock = BlockScope.back();
  NaClBitstreamReader::AbbrevList *UpdateAbbrevs =
      &CurBlock.GlobalAbbrevs->getAbbrevs();
  bool FoundSetBID = false;

  // Read records of the BlockInfo block.
  while (1) {
    if (Listener) Listener->StartBit = GetCurrentBitNo();
    NaClBitstreamEntry Entry = advance(AF_DontAutoprocessAbbrevs, Listener);

    switch (Entry.Kind) {
    case llvm::NaClBitstreamEntry::SubBlock:  // PNaCl doesn't allow!
    case llvm::NaClBitstreamEntry::Error:
      return true;
    case llvm::NaClBitstreamEntry::EndBlock:
      if (Listener) Listener->EndBlockInfoBlock();
      return false;
    case llvm::NaClBitstreamEntry::Record:
      // The interesting case.
      break;
    }

    // Read abbrev records, associate them with CurBID.
    if (Entry.ID == naclbitc::DEFINE_ABBREV) {
      ReadAbbrevRecord(false, Listener);

      // ReadAbbrevRecord installs a local abbreviation.  Move it to the
      // appropriate BlockInfo if the corresponding SetBID record has been
      // found.
      if (FoundSetBID)
        CurBlock.moveLocalAbbrevToAbbrevList(UpdateAbbrevs);
      continue;
    }

    // Read a record.
    Record.clear();
    switch (readRecord(Entry.ID, Record)) {
      default: 
        // No other records should be found!
        return true;
      case naclbitc::BLOCKINFO_CODE_SETBID:
        if (Record.size() < 1) return true;
        FoundSetBID = true;
        UpdateAbbrevs =
            &BitStream->getBlockInfo((unsigned)Record[0])->getAbbrevs();
        if (Listener) {
          Listener->Values = Record;
          Listener->SetBID();
        }
        break;
    }
  }
}