/* DWARF 2 debugging format support for GDB.

   Copyright (C) 1994-2021 Free Software Foundation, Inc.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef DWARF2_LINE_HEADER_H
#define DWARF2_LINE_HEADER_H

#include "gdbtypes.h"

/* dir_index is 1-based in DWARF 4 and before, and is 0-based in DWARF 5 and
   later.  */
typedef int dir_index;

/* file_name_index is 1-based in DWARF 4 and before, and is 0-based in DWARF 5
   and later.  */
typedef int file_name_index;

struct line_header;

struct file_entry
{
  file_entry () = default;

  file_entry (const char *name_, dir_index d_index_,
	      unsigned int mod_time_, unsigned int length_)
    : name (name_),
      d_index (d_index_),
      mod_time (mod_time_),
      length (length_)
  {}

  /* Return the include directory at D_INDEX stored in LH.  Returns
     NULL if D_INDEX is out of bounds.  */
  const char *include_dir (const line_header *lh) const;

  /* The file name.  Note this is an observing pointer.  The memory is
     owned by debug_line_buffer.  */
  const char *name {};

  /* The directory index (1-based).  */
  dir_index d_index {};

  unsigned int mod_time {};

  unsigned int length {};

  /* True if referenced by the Line Number Program.  */
  bool included_p {};

  /* The associated symbol table, if any.  */
  struct symtab *symtab {};
};

/* The line number information for a compilation unit (found in the
   .debug_line section) begins with a "statement program header",
   which contains the following information.  */
struct line_header
{
  line_header ()
    : offset_in_dwz {}
  {}

  /* Add an entry to the include directory table.  */
  void add_include_dir (const char *include_dir);

  /* Add an entry to the file name table.  */
  void add_file_name (const char *name, dir_index d_index,
		      unsigned int mod_time, unsigned int length);

  /* Return the include dir at INDEX (0-based in DWARF 5 and 1-based before).
     Returns NULL if INDEX is out of bounds.  */
  const char *include_dir_at (dir_index index) const
  {
    int vec_index;
    if (version >= 5)
      vec_index = index;
    else
      vec_index = index - 1;
    if (vec_index < 0 || vec_index >= m_include_dirs.size ())
      return NULL;
    return m_include_dirs[vec_index];
  }

  bool is_valid_file_index (int file_index) const
  {
    if (version >= 5)
      return 0 <= file_index && file_index < file_names_size ();
    return 1 <= file_index && file_index <= file_names_size ();
  }

  /* Return the file name at INDEX (0-based in DWARF 5 and 1-based before).
     Returns NULL if INDEX is out of bounds.  */
  file_entry *file_name_at (file_name_index index)
  {
    int vec_index;
    if (version >= 5)
      vec_index = index;
    else
      vec_index = index - 1;
    if (vec_index < 0 || vec_index >= m_file_names.size ())
      return NULL;
    return &m_file_names[vec_index];
  }

  /* A const overload of the same.  */
  const file_entry *file_name_at (file_name_index index) const
  {
    line_header *lh = const_cast<line_header *> (this);
    return lh->file_name_at (index);
  }

  /* The indexes are 0-based in DWARF 5 and 1-based in DWARF 4. Therefore,
     this method should only be used to iterate through all file entries in an
     index-agnostic manner.  */
  std::vector<file_entry> &file_names ()
  { return m_file_names; }
  /* A const overload of the same.  */
  const std::vector<file_entry> &file_names () const
  { return m_file_names; }

  /* Offset of line number information in .debug_line section.  */
  sect_offset sect_off {};

  /* OFFSET is for struct dwz_file associated with dwarf2_per_objfile.  */
  unsigned offset_in_dwz : 1; /* Can't initialize bitfields in-class.  */

  unsigned int total_length {};
  unsigned short version {};
  unsigned int header_length {};
  unsigned char minimum_instruction_length {};
  unsigned char maximum_ops_per_instruction {};
  unsigned char default_is_stmt {};
  int line_base {};
  unsigned char line_range {};
  unsigned char opcode_base {};

  /* standard_opcode_lengths[i] is the number of operands for the
     standard opcode whose value is i.  This means that
     standard_opcode_lengths[0] is unused, and the last meaningful
     element is standard_opcode_lengths[opcode_base - 1].  */
  std::unique_ptr<unsigned char[]> standard_opcode_lengths;

  int file_names_size () const
  { return m_file_names.size(); }

  /* The start and end of the statement program following this
     header.  These point into dwarf2_per_objfile->line_buffer.  */
  const gdb_byte *statement_program_start {}, *statement_program_end {};

  /* Return the full name of file number I in this object's file name
     table.  Use COMP_DIR as the name of the current directory of the
     compilation.  The result is allocated using xmalloc; the caller
     is responsible for freeing it.  */
  gdb::unique_xmalloc_ptr<char> file_full_name (int file,
						const char *comp_dir) const;

  /* Return file name relative to the compilation directory of file
     number I in this object's file name table.  The result is
     allocated using xmalloc; the caller is responsible for freeing
     it.  */
  gdb::unique_xmalloc_ptr<char> file_file_name (int file) const;

 private:
  /* The include_directories table.  Note these are observing
     pointers.  The memory is owned by debug_line_buffer.  */
  std::vector<const char *> m_include_dirs;

  /* The file_names table. This is private because the meaning of indexes
     differs among DWARF versions (The first valid index is 1 in DWARF 4 and
     before, and is 0 in DWARF 5 and later).  So the client should use
     file_name_at method for access.  */
  std::vector<file_entry> m_file_names;
};

typedef std::unique_ptr<line_header> line_header_up;

inline const char *
file_entry::include_dir (const line_header *lh) const
{
  return lh->include_dir_at (d_index);
}

/* Read the statement program header starting at SECT_OFF in SECTION.
   Return line_header.  Returns nullptr if there is a problem reading
   the header, e.g., if it has a version we don't understand.

   NOTE: the strings in the include directory and file name tables of
   the returned object point into the dwarf line section buffer,
   and must not be freed.  */

extern line_header_up dwarf_decode_line_header
  (sect_offset sect_off, bool is_dwz, dwarf2_per_objfile *per_objfile,
   struct dwarf2_section_info *section, const struct comp_unit_head *cu_header);

#endif /* DWARF2_LINE_HEADER_H */
