//===-- DWARFDebugLine.h ----------------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#ifndef SymbolFileDWARF_DWARFDebugLine_h_
#define SymbolFileDWARF_DWARFDebugLine_h_

#include <map>
#include <string>
#include <vector>

#include "lldb/Utility/FileSpec.h"
#include "lldb/lldb-private.h"

#include "DWARFDataExtractor.h"
#include "DWARFDefines.h"

#include "llvm/Support/MD5.h"

class DWARFUnit;
class SymbolFileDWARF;

// DWARFDebugLine
class DWARFDebugLine {
public:
  // FileNameEntry
  struct FileNameEntry {
    FileNameEntry()
        : name(nullptr), dir_idx(0), mod_time(0), length(0), checksum() {}

    const char *name;
    dw_sleb128_t dir_idx;
    dw_sleb128_t mod_time;
    dw_sleb128_t length;
    llvm::MD5::MD5Result checksum;
  };

  // Prologue
  struct Prologue {

    Prologue()
        : total_length(0), version(0), prologue_length(0), min_inst_length(0),
          default_is_stmt(0), line_base(0), line_range(0), opcode_base(0),
          standard_opcode_lengths(), include_directories(), file_names() {}

    typedef std::shared_ptr<Prologue> shared_ptr;

    uint32_t total_length; // The size in bytes of the statement information for
                           // this compilation unit (not including the
                           // total_length field itself).
    uint16_t
        version; // Version identifier for the statement information format.

    uint8_t address_size;
    uint8_t segment_selector_size;

    uint32_t prologue_length; // The number of bytes following the
                              // prologue_length field to the beginning of the
                              // first byte of the statement program itself.
    uint8_t min_inst_length; // The size in bytes of the smallest target machine
                             // instruction. Statement program opcodes that
                             // alter the address register first multiply their
                             // operands by this value.
    uint8_t maximum_operations_per_instruction; // New in DWARF4. The maximum
                                                // number of individual
                                                // operations that may be
                                                // encoded in an instruction.
    uint8_t default_is_stmt; // The initial value of theis_stmtregister.
    int8_t line_base;    // This parameter affects the meaning of the special
                         // opcodes. See below.
    uint8_t line_range;  // This parameter affects the meaning of the special
                         // opcodes. See below.
    uint8_t opcode_base; // The number assigned to the first special opcode.
    std::vector<uint8_t> standard_opcode_lengths;
    std::vector<const char *> include_directories;
    std::vector<FileNameEntry> file_names;

    int32_t MaxLineIncrementForSpecialOpcode() const {
      return line_base + (int8_t)line_range - 1;
    }
    bool IsValid() const;
    //      void Append(BinaryStreamBuf& buff) const;
    void Dump(lldb_private::Log *log);
    void Clear() {
      total_length = version = prologue_length = min_inst_length = line_base =
          line_range = opcode_base = 0;
      line_base = 0;
      standard_opcode_lengths.clear();
      include_directories.clear();
      file_names.clear();
    }
    bool GetFile(uint32_t file_idx, const lldb_private::FileSpec &cu_comp_dir,
                 lldb_private::FileSpec::Style style,
                 lldb_private::FileSpec &file) const;
  };

  // Standard .debug_line state machine structure
  struct Row {
    typedef std::vector<Row> collection;
    typedef collection::iterator iterator;
    typedef collection::const_iterator const_iterator;

    Row(bool default_is_stmt = false);
    virtual ~Row() {}
    void PostAppend();
    void Reset(bool default_is_stmt);
    void Dump(lldb_private::Log *log) const;
    static void Insert(Row::collection &state_coll, const Row &state);

    dw_addr_t address; // The program-counter value corresponding to a machine
                       // instruction generated by the compiler.
    uint32_t line; // An unsigned integer indicating a source line number. Lines
                   // are numbered beginning at 1. The compiler may emit the
                   // value 0 in cases where an instruction cannot be attributed
                   // to any source line.
    uint16_t column; // An unsigned integer indicating a column number within a
                     // source line. Columns are numbered beginning at 1. The
                     // value 0 is reserved to indicate that a statement begins
                     // at the 'left edge' of the line.
    uint16_t file; // An unsigned integer indicating the identity of the source
                   // file corresponding to a machine instruction.
    uint8_t is_stmt : 1, // A boolean indicating that the current instruction is
                         // the beginning of a statement.
        basic_block : 1, // A boolean indicating that the current instruction is
                         // the beginning of a basic block.
        end_sequence : 1, // A boolean indicating that the current address is
                          // that of the first byte after the end of a sequence
                          // of target machine instructions.
        prologue_end : 1, // A boolean indicating that the current address is
                          // one (of possibly many) where execution should be
                          // suspended for an entry breakpoint of a function.
        epilogue_begin : 1; // A boolean indicating that the current address is
                            // one (of possibly many) where execution should be
                            // suspended for an exit breakpoint of a function.
    uint32_t isa; // An unsigned integer whose value encodes the applicable
                  // instruction set architecture for the current instruction.
  };

  // LineTable
  struct LineTable {
    typedef std::shared_ptr<LineTable> shared_ptr;

    LineTable() : prologue(), rows() {}

    void AppendRow(const DWARFDebugLine::Row &state);
    void Clear() {
      prologue.reset();
      rows.clear();
    }

    uint32_t LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const;

    Prologue::shared_ptr prologue;
    Row::collection rows;
  };

  // State
  struct State : public Row {
    typedef void (*Callback)(dw_offset_t offset, const State &state,
                             void *userData);

    // Special row codes used when calling the callback
    enum { StartParsingLineTable = 0, DoneParsingLineTable = -1 };

    State(Prologue::shared_ptr &prologue_sp, lldb_private::Log *log,
          Callback callback, void *userData);

    void AppendRowToMatrix(dw_offset_t offset);

    void Finalize(dw_offset_t offset);

    void Reset();

    Prologue::shared_ptr prologue;
    lldb_private::Log *log;
    Callback callback; // Callback function that gets called each time an entry
                       // is to be added to the matrix
    void *callbackUserData;
    int row; // The row number that starts at zero for the prologue, and
             // increases for each row added to the matrix
  private:
    DISALLOW_COPY_AND_ASSIGN(State);
  };

  static bool
  ParseSupportFiles(const lldb::ModuleSP &module_sp,
                    const lldb_private::DWARFDataExtractor &debug_line_data,
                    dw_offset_t stmt_list,
                    lldb_private::FileSpecList &support_files,
                    DWARFUnit *dwarf_cu);
  static bool
  ParsePrologue(const lldb_private::DWARFDataExtractor &debug_line_data,
                lldb::offset_t *offset_ptr, Prologue *prologue,
                DWARFUnit *dwarf_cu = nullptr);
  static bool
  ParseStatementTable(const lldb_private::DWARFDataExtractor &debug_line_data,
                      lldb::offset_t *offset_ptr, State::Callback callback,
                      void *userData, DWARFUnit *dwarf_cu);
  static bool
  ParseStatementTable(const lldb_private::DWARFDataExtractor &debug_line_data,
                      lldb::offset_t *offset_ptr, LineTable *line_table,
                      DWARFUnit *dwarf_cu);
  static void Parse(const lldb_private::DWARFDataExtractor &debug_line_data,
                    DWARFDebugLine::State::Callback callback, void *userData);
  //  static void AppendLineTableData(const DWARFDebugLine::Prologue* prologue,
  //  const DWARFDebugLine::Row::collection& state_coll, const uint32_t
  //  addr_size, BinaryStreamBuf &debug_line_data);

  DWARFDebugLine() : m_lineTableMap() {}

  void Parse(const lldb_private::DWARFDataExtractor &debug_line_data);
  void ParseIfNeeded(const lldb_private::DWARFDataExtractor &debug_line_data);
  LineTable::shared_ptr GetLineTable(const dw_offset_t offset) const;

protected:
  typedef std::map<dw_offset_t, LineTable::shared_ptr> LineTableMap;
  typedef LineTableMap::iterator LineTableIter;
  typedef LineTableMap::const_iterator LineTableConstIter;

  LineTableMap m_lineTableMap;
};

#endif // SymbolFileDWARF_DWARFDebugLine_h_
