//===- NaClBitcodeParser.h -----------------------------------*- C++ -*-===//
//     Low-level bitcode driver to parse PNaCl bitcode files.
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Parses and processes low-level PNaCl bitcode files. Defines class
// NaClBitcodeParser.
//
// The concepts of PNaCl bitcode files are basically the same as for
// LLVM bitcode files (see http://llvm.org/docs/BitCodeFormat.html for
// details).
//
// The bitstream format is an abstract encoding of structured data,
// very similar to XML in some ways. Like XML, bitstream files contain
// tags, and nested structures, and you can parse the file without
// having to understand the tags. Unlike XML, the bitstream format is
// a binary encoding, and provides a mechanism for the file to
// self-describe "abbreviations".  Abbreviations are effectively size
// optimizations for the content.
//
// The bitcode file is conceptually a sequence of "blocks", defining
// the content. Blocks contain a sequence of records and
// blocks. Nested content is defined using nested blocks.  A (data)
// "record" is a tag, and a vector of (unsigned integer) values.
//
// Blocks are identified using Block IDs. Each kind of block has a
// unique block "ID". Records have two elements:
//
//   a) A "code" identifying what type of record it is.
//   b) A vector of "values" defining the contents of the record.
//
// The bitstream "reader" (defined in NaClBitstreamReader.h) defines
// the implementation that converts the low-level bit file into
// records and blocks. The bit stream is processed by moving a
// "cursor" over the sequence of bits.
//
// The bitstream reader assumes that each block/record is read in by
// first reading the "entry". The entry defines whether it corresponds
// to one of the following:
//
//    a) At the beginning of a (possibly nested) block
//    b) At the end of the current block.
//    c) The input defines an abberviation.
//    d) The input defines a record.
//
// An entry contains two values, a "kind" and an "ID". The kind
// defines which of the four cases above occurs. The ID provides
// identifying information on how to further process the input. For
// case (a), the ID is the identifier associated with the the block
// being processed. For case (b) and (c) the ID is ignored. For case
// (d) the ID identifies the abbreviation that should be used to parse
// the values.
//
// The class NaClBitcodeParser defines a bitcode parser that extracts
// the blocks and records, which are then processed using virtual
// callbacks. In general, you will want to implement derived classes
// for each type of block, so that the corresponding data is processed
// appropriately.
//
// The class NaClBitcodeParser parses a bitcode block, and defines a
// set of callbacks for that block, including:
//
//    a) EnterBlock: What to do once we have entered the block.
//    b) ProcessRecord: What to do with each parsed record.
//    c) ParseBlock: Parse the (nested) block with the given ID.
//    d) ExitBlock: What to do once we have finished processing the block.
//
// Note that a separate instance of NaClBitcodeParser (or a
// corresponding derived class) is created for each nested block. Each
// instance is responsible for only parsing a single block. Method
// ParseBlock creates new instances to parse nested blocks. Method
// GetEnclosingParser() can be used to refer to the parser associated
// with the enclosing block.
//
// Currently, the default processing of abbreviations is handled by
// the PNaCl bitstream reader, rather than by the parser.
//
// If you need to process abbreviations processed by the PNaCl
// bitstream reader, you must explicitly define a
// NaClBitcodeParserListener to listen (within the bitstream reader),
// and make appropriate call backs to the NaClBitcodeParser.
// The listener is glued to parsers using method SetListener.
//
// TODO(kschimpf): Define an intermediate derived class of
// NaClBitcodeParser that defines callbacks based on the actual
// structure of PNaCl bitcode files.  That is, it has callbacks for
// each of the types of blocks (i.e. module, types, global variables,
// function, symbol tables etc). This derivied class can then be used
// as the base class for the bitcode reader.
// ===----------------------------------------------------------------------===//

#ifndef LLVM_BITCODE_NACL_NACLBITCODEPARSER_H
#define LLVM_BITCODE_NACL_NACLBITCODEPARSER_H

#include "llvm/Bitcode/NaCl/NaClBitstreamReader.h"
#include "llvm/Bitcode/NaCl/NaClBitcodeDefs.h"
#include "llvm/Support/raw_ostream.h"
#include <vector>

namespace llvm {

class NaClBitcodeRecord;
class NaClBitcodeParser;
class NaClBitcodeParserListener;

// Defines the base class for data extracted from the input bitstream
// (i.e blocks and records).
class NaClBitcodeData {
  void operator=(const NaClBitcodeData&) = delete;

public:
  /// Create data element to be read from input cursor.
  explicit NaClBitcodeData(NaClBitstreamCursor &Cursor)
      : Cursor(Cursor), StartBit(Cursor.GetCurrentBitNo())
  {}

  /// Create copy of the given data element.
  explicit NaClBitcodeData(const NaClBitcodeData &Data)
      : Cursor(Data.Cursor), StartBit(Data.StartBit)
  {}

  /// Returns the bitstream reader being used.
  NaClBitstreamReader &GetReader() const {
    return *Cursor.getBitStreamReader();
  }

  /// Returns the cursor position within the bitstream.
  NaClBitstreamCursor &GetCursor() const {
    return Cursor;
  }

  /// Returns the number of bits defined by the data.
  uint64_t GetNumBits() const {
    return GetCursor().GetCurrentBitNo() - StartBit;
  }

  /// Returns the first bit of the stream data.
  uint64_t GetStartBit() const {
    return StartBit;
  }

protected:
  /// Change the start bit for the data to the new value.
  void SetStartBit(uint64_t NewValue) {
    StartBit = NewValue;
  }

private:
  // The bitstream cursor defining location within the bitcode file.
  NaClBitstreamCursor &Cursor;

  // Start bit for the record.
  uint64_t StartBit;
};

/// Models the block defined by a (begin) block record, through the
/// (end) block record.
class NaClBitcodeBlock : public NaClBitcodeData {
  NaClBitcodeBlock(const NaClBitcodeBlock &) = delete;
  void operator=(const NaClBitcodeBlock &) = delete;

public:
  /// Given the found (begin) block record for block BlockID, create
  /// the corresponding data associated with that block.
  NaClBitcodeBlock(unsigned BlockID, const NaClBitcodeRecord &Record);

  /// Create block data for block BlockID, using the input cursor.
  NaClBitcodeBlock(unsigned BlockID, NaClBitstreamCursor &Cursor)
      : NaClBitcodeData(Cursor),
        BlockID(BlockID),
        EnclosingBlock(0)
  {
    LocalStartBit = GetStartBit();
  }

  /// Print the contents out to the given stream.
  void Print(raw_ostream& os) const;

  /// Returns pointer to the enclosing block.
  const NaClBitcodeBlock *GetEnclosingBlock() const {
    return EnclosingBlock;
  }

  /// Returns the block ID of the block.
  unsigned GetBlockID() const {
    return BlockID;
  }

  /// Returns the number of bits in the block associated with the
  /// bitcode parser parsing this block, excluding nested blocks.
  unsigned GetLocalNumBits() const {
    return GetCursor().GetCurrentBitNo() - LocalStartBit;
  }

protected:
  // The block ID associated with this record.
  unsigned BlockID;
  // The enclosing block, if defined.
  const NaClBitcodeBlock *EnclosingBlock;
  // Start bit for the block, updated to skip nested blocks.
  uint64_t LocalStartBit;

  // Note: We friend class NaClBitcodeParser, so that it can
  // update field LocalStartBit.
  friend class NaClBitcodeParser;
};

typedef NaClBitcodeRecordVector NaClRecordVector;

class NaClBitcodeRecordData {
  NaClBitcodeRecordData &operator=(const NaClBitcodeRecordData &) = delete;
public:
  NaClBitcodeRecordData(unsigned Code, const NaClRecordVector &Values)
      : Code(Code), Values(Values) {}
  explicit NaClBitcodeRecordData(const NaClBitcodeRecordData &Record)
      : Code(Record.Code), Values(Record.Values) {}
  NaClBitcodeRecordData() : Code(0) {}
  // The selector code associated with the record.
  unsigned Code;
  // The sequence of values defining the parsed record.
  NaClRecordVector Values;

  void Print(raw_ostream &strm) const;
};

inline raw_ostream &operator<<(raw_ostream &Strm,
                               const NaClBitcodeRecordData &Data) {
  Data.Print(Strm);
  return Strm;
}

/// Simple container class to convert the values of the corresponding
/// read record to a simpler form, only containing values.
struct NaClBitcodeValues {
public:
  NaClBitcodeValues(const NaClBitcodeRecordData &Record)
      : Record(Record) {}

  size_t size() const {
    return Record.Values.size()+1;
  }

  uint64_t operator[](size_t index) const {
    return index == 0 ? Record.Code : Record.Values[index-1];
  }

private:
  const NaClBitcodeRecordData &Record;
};

/// Defines the data associated with reading a block record in the
/// PNaCl bitcode stream.
class NaClBitcodeRecord : public NaClBitcodeData {
public:
  /// Type for vector of values representing a record.
  typedef NaClRecordVector RecordVector;

  /// Creates a bitcode record, starting at the position defined
  /// by cursor.
  explicit NaClBitcodeRecord(const NaClBitcodeBlock &Block)
      : NaClBitcodeData(Block.GetCursor()),
        Block(Block)
  {}

  /// Print the contents out to the given stream.
  void Print(raw_ostream& os) const;

  /// The block the record appears in.
  const NaClBitcodeBlock &GetBlock() const {
    return Block;
  }

  /// Returns the block ID associated with the record.
  unsigned GetBlockID() const {
    return Block.GetBlockID();
  }

  /// Returns the kind of entry read from the input stream.
  unsigned GetEntryKind() const {
    return Entry.Kind;
  }

  /// Returns the code value (i.e. selector) associated with the
  /// record.
  unsigned GetCode() const {
    return Data.Code;
  }

  /// Returns the EntryID (e.g. abbreviation if !=
  /// naclbitc::UNABBREV_RECORD) associated with the record. Note:
  /// for block-enter, block-exit, and define-abbreviation, EntryID is
  /// not the corresponding abbreviation.
  unsigned GetEntryID() const {
    return Entry.ID;
  }

  /// Returns the (value) record associated with the read record.
  const RecordVector &GetValues() const {
    return Data.Values;
  }

  /// Allows lower level access to data representing record.
  const NaClBitcodeRecordData &GetRecordData() const {
    return Data;
  }

  /// Returns true if the record was read using an abbreviation.
  bool UsedAnAbbreviation() const {
    return GetEntryKind() == NaClBitstreamEntry::Record &&
        GetEntryID() != naclbitc::UNABBREV_RECORD;
  }

  /// Returns the abbrevation index used to read the record.
  /// Returns naclbitc::UNABBREV_RECORD if not applicable.
  unsigned GetAbbreviationIndex() const {
    return UsedAnAbbreviation()
        ? GetEntryID() : static_cast<unsigned>(naclbitc::UNABBREV_RECORD);
  }

  /// Destructively change the abbreviation ID to the given value.
  void SetAbbreviationIndex(unsigned Index) {
    Entry.ID = Index;
  }

protected:
  // The block associated with the record.
  const NaClBitcodeBlock &Block;
  // The data of the record.
  NaClBitcodeRecordData Data;
  // The entry (i.e. value(s) preceding the record that define what
  // value comes next).
  NaClBitstreamEntry Entry;

private:
  // Allows class NaClBitcodeParser to read values into the
  // record, thereby hiding the details of how to read values.
  friend class NaClBitcodeParser;
  friend class NaClBitcodeParserListener;

  /// Read bitstream entry. Defines what construct appears next in the
  /// bitstream.
  void ReadEntry() {
    SetStartBit(GetCursor().GetCurrentBitNo());
    Entry = GetCursor().
        advance(NaClBitstreamCursor::AF_DontAutoprocessAbbrevs, 0);
  }

  /// Reads in a record's values, if the entry defines a record (Must
  /// be called after ReadEntry).
  void ReadValues() {
    Data.Values.clear();
    Data.Code = GetCursor().readRecord(Entry.ID, Data.Values);
  }

  NaClBitcodeRecord(const NaClBitcodeRecord &Rcd) = delete;
  void operator=(const NaClBitcodeRecord &Rcd) = delete;
};

inline raw_ostream &operator<<(raw_ostream &Strm,
                               const NaClBitcodeRecord &Record) {
  Record.Print(Strm);
  return Strm;
}

/// Defines a listener to handle abbreviations within a bitcode file.
/// In particular, abbreviations and the BlockInfo block are made more
/// explicit, and then sent to the parser through virtuals
/// ProcessAbbreviation and SetBID.
class NaClBitcodeParserListener : public NaClAbbrevListener {
  friend class NaClBitcodeParser;
public:
  // Constructs a listener for the given parser.  Note: All nested
  // parsers automatically inherit this listener.
  NaClBitcodeParserListener(NaClBitcodeParser *Parser)
      : Parser(Parser), GlobalBlockID(naclbitc::BLOCKINFO_BLOCK_ID) {
  }

  virtual ~NaClBitcodeParserListener() {}

private:
  virtual void BeginBlockInfoBlock(unsigned NumWords);

  virtual void SetBID();

  virtual void EndBlockInfoBlock();

  virtual void ProcessAbbreviation(NaClBitCodeAbbrev *Abbrev,
                                   bool IsLocal);

  /// The block parser currently being listened to.
  NaClBitcodeParser *Parser;

  /// The block ID to use if a global abbreviation. Note: This field is
  /// updated by calls to method SetBID.
  unsigned GlobalBlockID;
};

/// Parses a block in the PNaCl bitcode stream.
class NaClBitcodeParser {
  // Allow listener privledges, so that it can update/call the parser
  // using a clean API.
  friend class NaClBitcodeParserListener;

  // Implements an error handler for errors in the bitstream reader.
  // Redirects bitstream reader errors to corresponding parrser error
  // reporting function.
  class ErrorHandler : public NaClBitstreamCursor::ErrorHandler {
    NaClBitcodeParser *Parser;
  public:
    ErrorHandler(NaClBitcodeParser *Parser,
                 NaClBitstreamCursor &Cursor):
        NaClBitstreamCursor::ErrorHandler(Cursor), Parser(Parser) {}
    LLVM_ATTRIBUTE_NORETURN
    void Fatal(const std::string &ErrorMessage) const final {
      Parser->FatalAt(getCurrentBitNo(), ErrorMessage);
      llvm_unreachable("GCC treats noreturn virtual functions as returning");
    }
    ~ErrorHandler() override {}
  };

public:
  // Creates a parser to parse the the block at the given cursor in
  // the PNaCl bitcode stream. This instance is a "dummy" instance
  // that starts the parser.
  explicit NaClBitcodeParser(NaClBitstreamCursor &Cursor)
      : EnclosingParser(0),
        Block(ILLEGAL_BLOCK_ID, Cursor),
        Record(Block),
        Listener(0),
        ErrStream(&errs()) {
    std::unique_ptr<NaClBitstreamCursor::ErrorHandler>
        ErrHandler(new ErrorHandler(this, Cursor));
    Cursor.setErrorHandler(ErrHandler);
  }

  virtual ~NaClBitcodeParser();

  /// Reads the (top-level) block associated with the given block
  /// record at the stream cursor. Returns true if unable to parse.
  /// Can be called multiple times to parse multiple blocks.
  bool Parse();

  // Called once the bitstream reader has entered the corresponding
  // subblock.  Argument NumWords is set to the number of words in the
  // corresponding subblock.
  virtual void EnterBlock(unsigned /*NumWords*/) {}

  // Called when the corresponding EndBlock of the block being parsed
  // is found.
  virtual void ExitBlock() {}

  // Called after each record (within the block) is read (into field Record).
  virtual void ProcessRecord() {}

  // Called if a SetBID record is encountered in the BlockInfo block,
  // and the parser has a listener.
  virtual void SetBID() {}

  // Called to process an abbreviation if the parser has a listener.
  virtual void ProcessAbbreviation(unsigned /*BlockID*/,
                                   NaClBitCodeAbbrev * /*Abbrev*/,
                                   bool /*IsLocal*/) {}

  // Creates an instance of the NaClBitcodeParser to use to parse the
  // block with the given block ID, and then call's method
  // ParseThisBlock() to parse the corresponding block. Note:
  // Each derived class should define it's own version of this
  // method, following the pattern below.
  virtual bool ParseBlock(unsigned BlockID) {
    // Default implementation just builds a parser that does nothing.
    NaClBitcodeParser Parser(BlockID, this);
    return Parser.ParseThisBlock();
  }

  // Changes the stream to print errors to, and returns the old error stream.
  // There are two use cases:
  // 1) To change (from the default errs()) inside the constructor of the
  //    derived class. In this context, it will be used for all error
  //    messages for the derived class.
  // 2) Temporarily modify it for a single error message.
  raw_ostream &setErrStream(raw_ostream &Stream) {
    raw_ostream &OldErrStream = *ErrStream;
    ErrStream = &Stream;
    return OldErrStream;
  }

  // Called when an error occurs. BitPosition is the bit position the
  // error was found, and Message is the error to report. Always
  // returns true (the error return value of Parse). Level is
  // the severity of the error.
  virtual bool ErrorAt(naclbitc::ErrorLevel Level, uint64_t BitPosition,
                       const std::string &Message);

  bool ErrorAt(uint64_t BitPosition, const std::string &Message) {
    return ErrorAt(naclbitc::Error, BitPosition, Message);
  }

  // Called when an error occurs. Message is the error to
  // report. Always returns true (the error return value of Parse).
  bool Error(const std::string &Message) {
    return ErrorAt(Record.GetStartBit(), Message);
  }

  // Called when a fatal error occurs. BitPosition is the bit position
  // the error was found, and Message is the error to report. Does not
  // return.
  LLVM_ATTRIBUTE_NORETURN
  void FatalAt(uint64_t BitPosition, const std::string &Message) {
    ErrorAt(naclbitc::Fatal, BitPosition, Message);
    llvm_unreachable("Fatal errors should not return");
  }

  // Called when a fatal error occurs. Message is the error to
  // report. Does not return.
  LLVM_ATTRIBUTE_NORETURN
  void Fatal(const std::string &Message) {
    FatalAt(Record.GetStartBit(), Message);
    llvm_unreachable("GCC treats noreturn virtual functions as returning");
  }

  // Generates fatal generic error message.
  LLVM_ATTRIBUTE_NORETURN
  void Fatal() {
    Fatal("Fatal error occurred!");
  }

  // Returns the number of bits in this block, including nested blocks.
  unsigned GetBlockNumBits() const {
    return Block.GetNumBits();
  }

  // Returns the number of bits in this block, excluding nested blocks.
  unsigned GetBlockLocalNumBits() const {
    return Block.GetLocalNumBits();
  }

  /// Returns the block ID associated with the Parser.
  unsigned GetBlockID() const {
    return Block.GetBlockID();
  }

  NaClBitcodeBlock &GetBlock() {
    return Block;
  }

  /// Returns the enclosing parser of this block.
  NaClBitcodeParser *GetEnclosingParser() const {
    // Note: The top-level parser instance is a dummy instance
    // and is not considered an enclosing parser.
    return EnclosingParser->EnclosingParser ? EnclosingParser : 0;
  }

  // Parses the block using the parser defined by
  // ParseBlock(unsigned).  Returns true if unable to parse the
  // block. Note: Should only be called by virtual ParseBlock(unsigned).
  bool ParseThisBlock() {
    bool Results;
    if (Listener) {
      NaClBitcodeParser *CallingParser = Listener->Parser;
      Listener->Parser = this;
      Results = ParseThisBlockInternal();
      Listener->Parser = CallingParser;
    } else {
      Results = ParseThisBlockInternal();
    }
    return Results;
  }

  /// Skips the current block, assuming the parser is at the beginning
  /// of the block. That is, Record.GetEntryKind() equals
  /// NaClBitstreamEntry::SubBlock. Returns false if
  /// successful. Otherwise returns 1.
  bool SkipBlock() {
    if (Record.GetEntryKind() != NaClBitstreamEntry::SubBlock)
      return Error("SkipBlock on non-block record");
    return Record.GetCursor().SkipBlock();
  }

protected:
  // The containing parser.
  NaClBitcodeParser *EnclosingParser;

  // The block the parser is associated with.
  NaClBitcodeBlock Block;

  // The current record (within the block) being processed.
  NaClBitcodeRecord Record;

  // The listener (if any) to use.
  NaClBitcodeParserListener *Listener;

  // The error stream to use if non-null (uses errs() if null).
  raw_ostream *ErrStream;

  // Creates a block parser to parse the block associated with the bitcode entry
  // that defines the beginning of a block. This instance actually parses the
  // corresponding block. Inherits the bitstream cursor from the
  // EnclosingParser.
  NaClBitcodeParser(unsigned BlockID, NaClBitcodeParser *EnclosingParser)
      : EnclosingParser(EnclosingParser),
        Block(BlockID, EnclosingParser->Record),
        Record(Block),
        Listener(EnclosingParser->Listener),
        ErrStream(EnclosingParser->ErrStream)
  {}

  // Same as above, but use the supplied bitstream cursor (instead of
  // inheriting from the enclosing parser). This constructor allows
  // parallel parsing of subblocks, by allowing the caller to generate
  // a different Cursor for each block to be parsed in parallel.
  NaClBitcodeParser(unsigned BlockID, NaClBitcodeParser *EnclosingParser,
                    NaClBitstreamCursor &Cursor)
      : EnclosingParser(EnclosingParser),
        Block(BlockID, Cursor),
        Record(Block),
        Listener(EnclosingParser->Listener),
        ErrStream(EnclosingParser->ErrStream)
  {}

  /// Defines the listener for this block, and all enclosing blocks,
  /// to be the given listener. Should be set in the constructor.
  void SetListener(NaClBitcodeParserListener* UseListener) {
    Listener = UseListener;
  }

private:
  // Special constant identifying the top-level instance.
  static const unsigned ILLEGAL_BLOCK_ID = UINT_MAX;

  // Parses the block. Returns true if unable to parse the
  // block. Note: Should only be called by virtual ParseThisBlock.
  bool ParseThisBlockInternal() {
    bool Results;
    if (GetBlockID() == naclbitc::BLOCKINFO_BLOCK_ID) {
      Results = ParseBlockInfoInternal();
    } else {
      Results = ParseBlockInternal();
      ExitBlock();
    }
    return Results;
  }

  // Parses a BlockInfo block, where processing is handled through
  // a listener in the bitstream reader.
  bool ParseBlockInfoInternal();

  // Parses the non-BlockInfo block. Returns true if unable to parse the
  // block.
  bool ParseBlockInternal();

  void operator=(const NaClBitcodeParser &Parser) = delete;
  NaClBitcodeParser(const NaClBitcodeParser &Parser) = delete;

};

}  // namespace llvm

#endif
