//===- ResolveInfo.h ------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_RESOLVE_INFO_H
#define MCLD_RESOLVE_INFO_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif

#include <llvm/Support/DataTypes.h>
#include <llvm/ADT/StringRef.h>

namespace mcld {

class LDSymbol;

/** \class ResolveInfo
 *  \brief ResolveInfo records the information about how to resolve a symbol.
 *
 *  A symbol must have some `attributes':
 *  - Desc - Defined, Reference, Common or Indirect
 *  - Binding - Global, Local, Weak
 *  - IsDyn - appear in dynamic objects or regular objects
 *  - Type - what the symbol refers to
 *  - Size  - the size of the symbol point to
 *  - Value - the pointer to another LDSymbol
 *  In order to save the memory and speed up the performance, FragmentLinker uses
 *  a bit field to store all attributes.
 *
 *  The maximum string length is (2^16 - 1)
 */
class ResolveInfo
{
friend class FragmentLinker;
friend class IRBuilder;
public:
  typedef uint64_t SizeType;

  /** \enum Type
   *  \brief What the symbol stand for
   *
   *  It is like ELF32_ST_TYPE
   *  MachO does not need this, and can not jump between Thumb and ARM code.
   */
  enum Type {
    NoType        = 0,
    Object        = 1,
    Function      = 2,
    Section       = 3,
    File          = 4,
    CommonBlock   = 5,
    ThreadLocal   = 6,
    IndirectFunc  = 10,
    LoProc        = 13,
    HiProc        = 15
  };

  /** \enum Desc
   *  \brief Description of the symbols.
   *
   *   Follow the naming in MachO. Like MachO nlist::n_desc
   *   In ELF, is a part of st_shndx
   */
  enum Desc {
    Undefined    = 0,
    Define       = 1,
    Common       = 2,
    Indirect     = 3,
    NoneDesc
  };

  enum Binding {
    Global       = 0,
    Weak         = 1,
    Local        = 2,
    Absolute     = 3,
    NoneBinding
  };

  enum Visibility {
    Default      = 0,
    Internal     = 1,
    Hidden       = 2,
    Protected    = 3
  };

  // -----  For HashTable  ----- //
  typedef llvm::StringRef key_type;

public:
  // -----  factory method  ----- //
  static ResolveInfo* Create(const key_type& pKey);

  static void Destroy(ResolveInfo*& pInfo);

  static ResolveInfo* Null();

  // -----  modifiers  ----- //
  /// setRegular - set the source of the file is a regular object
  void setRegular();

  /// setDynamic - set the source of the file is a dynamic object
  void setDynamic();

  /// setSource - set the source of the file
  /// @param pIsDyn is the source from a dynamic object?
  void setSource(bool pIsDyn);

  void setType(uint32_t pType);

  void setDesc(uint32_t pDesc);

  void setBinding(uint32_t pBinding);

  void setOther(uint32_t pOther);

  void setVisibility(Visibility pVisibility);

  void setIsSymbol(bool pIsSymbol);

  void setReserved(uint32_t pReserved);

  void setSize(SizeType pSize)
  { m_Size = pSize; }

  void override(const ResolveInfo& pForm);

  void overrideAttributes(const ResolveInfo& pFrom);

  void overrideVisibility(const ResolveInfo& pFrom);

  void setSymPtr(const LDSymbol* pSymPtr)
  { m_Ptr.sym_ptr = const_cast<LDSymbol*>(pSymPtr); }

  void setLink(const ResolveInfo* pTarget) {
    m_Ptr.info_ptr = const_cast<ResolveInfo*>(pTarget);
    m_BitField |= indirect_flag;
  }

  // -----  observers  ----- //
  bool isNull() const;

  bool isSymbol() const;

  bool isString() const;

  bool isGlobal() const;

  bool isWeak() const;

  bool isLocal() const;

  bool isAbsolute() const;

  bool isDefine() const;

  bool isUndef() const;

  bool isDyn() const;

  bool isCommon() const;

  bool isIndirect() const;

  uint32_t type() const;

  uint32_t desc() const;

  uint32_t binding() const;

  uint32_t reserved() const;

  uint8_t other() const
  { return (uint8_t)visibility(); }

  Visibility visibility() const;

  LDSymbol* outSymbol()
  { return m_Ptr.sym_ptr; }

  const LDSymbol* outSymbol() const
  { return m_Ptr.sym_ptr; }

  ResolveInfo* link()
  { return m_Ptr.info_ptr; }

  const ResolveInfo* link() const
  { return m_Ptr.info_ptr; }

  SizeType size() const
  { return m_Size; }

  const char* name() const
  { return m_Name; }

  unsigned int nameSize() const
  { return (m_BitField >> NAME_LENGTH_OFFSET); }

  uint32_t info() const
  { return (m_BitField & INFO_MASK); }

  uint32_t bitfield() const
  { return m_BitField; }

  // -----  For HashTable  ----- //
  bool compare(const key_type& pKey);

private:
  static const uint32_t GLOBAL_OFFSET      = 0;
  static const uint32_t GLOBAL_MASK        = 1;

  static const uint32_t DYN_OFFSET         = 1;
  static const uint32_t DYN_MASK           = 1   << DYN_OFFSET;

  static const uint32_t DESC_OFFSET        = 2;
  static const uint32_t DESC_MASK          = 0x3 << DESC_OFFSET;

  static const uint32_t LOCAL_OFFSET       = 4;
  static const uint32_t LOCAL_MASK         = 1   << LOCAL_OFFSET;

  static const uint32_t BINDING_MASK       = GLOBAL_MASK | LOCAL_MASK;

  static const uint32_t VISIBILITY_OFFSET  = 5;
  static const uint32_t VISIBILITY_MASK    = 0x3 << VISIBILITY_OFFSET;

  static const uint32_t TYPE_OFFSET        = 7;
  static const uint32_t TYPE_MASK          = 0xF << TYPE_OFFSET;

  static const uint32_t SYMBOL_OFFSET      = 11;
  static const uint32_t SYMBOL_MASK        = 1   << SYMBOL_OFFSET;

  static const uint32_t RESERVED_OFFSET    = 12;
  static const uint32_t RESERVED_MASK      = 0xF << RESERVED_OFFSET;
  static const uint32_t NAME_LENGTH_OFFSET = 16;
  static const uint32_t INFO_MASK          = 0xF;
  static const uint32_t RESOLVE_MASK       = 0xFFFF;

  union SymOrInfo {
    LDSymbol*    sym_ptr;
    ResolveInfo* info_ptr;
  };

public:
  static const uint32_t global_flag    = 0        << GLOBAL_OFFSET;
  static const uint32_t weak_flag      = 1        << GLOBAL_OFFSET;
  static const uint32_t regular_flag   = 0        << DYN_OFFSET;
  static const uint32_t dynamic_flag   = 1        << DYN_OFFSET;
  static const uint32_t undefine_flag  = 0        << DESC_OFFSET;
  static const uint32_t define_flag    = 1        << DESC_OFFSET;
  static const uint32_t common_flag    = 2        << DESC_OFFSET;
  static const uint32_t indirect_flag  = 3        << DESC_OFFSET;
  static const uint32_t local_flag     = 1        << LOCAL_OFFSET;
  static const uint32_t absolute_flag  = BINDING_MASK;
  static const uint32_t object_flag    = Object   << TYPE_OFFSET;
  static const uint32_t function_flag  = Function << TYPE_OFFSET;
  static const uint32_t section_flag   = Section  << TYPE_OFFSET;
  static const uint32_t file_flag      = File     << TYPE_OFFSET;
  static const uint32_t string_flag    = 0        << SYMBOL_OFFSET;
  static const uint32_t symbol_flag    = 1        << SYMBOL_OFFSET;

private:
  ResolveInfo();
  ResolveInfo(const ResolveInfo& pCopy);
  ResolveInfo& operator=(const ResolveInfo& pCopy);
  ~ResolveInfo();

private:
  SizeType m_Size;
  SymOrInfo m_Ptr;

  /** m_BitField
   *  31     ...    16 15    12 11     10..7 6      ..    5 4     3   2   1   0
   * |length of m_Name|reserved|Symbol|Type |ELF visibility|Local|Com|Def|Dyn|Weak|
   */
  uint32_t m_BitField;
  char m_Name[];
};

} // namespace of mcld

#endif