//===-- Type.h --------------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_Type_h_
#define liblldb_Type_h_

#include "lldb/Core/ClangForward.h"
#include "lldb/Symbol/CompilerDecl.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/Declaration.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/UserID.h"
#include "lldb/lldb-private.h"

#include "llvm/ADT/APSInt.h"

#include <set>

namespace lldb_private {
//----------------------------------------------------------------------
// CompilerContext allows an array of these items to be passed to
// perform detailed lookups in SymbolVendor and SymbolFile functions.
//----------------------------------------------------------------------
struct CompilerContext {
  CompilerContext(CompilerContextKind t, const ConstString &n)
      : type(t), name(n) {}

  bool operator==(const CompilerContext &rhs) const {
    return type == rhs.type && name == rhs.name;
  }

  void Dump() const;

  CompilerContextKind type;
  ConstString name;
};

class SymbolFileType : public std::enable_shared_from_this<SymbolFileType>,
                       public UserID {
public:
  SymbolFileType(SymbolFile &symbol_file, lldb::user_id_t uid)
      : UserID(uid), m_symbol_file(symbol_file) {}

  SymbolFileType(SymbolFile &symbol_file, const lldb::TypeSP &type_sp);

  ~SymbolFileType() {}

  Type *operator->() { return GetType(); }

  Type *GetType();

protected:
  SymbolFile &m_symbol_file;
  lldb::TypeSP m_type_sp;
};

class Type : public std::enable_shared_from_this<Type>, public UserID {
public:
  typedef enum EncodingDataTypeTag {
    eEncodingInvalid,
    eEncodingIsUID,      ///< This type is the type whose UID is m_encoding_uid
    eEncodingIsConstUID, ///< This type is the type whose UID is m_encoding_uid
                         ///with the const qualifier added
    eEncodingIsRestrictUID, ///< This type is the type whose UID is
                            ///m_encoding_uid with the restrict qualifier added
    eEncodingIsVolatileUID, ///< This type is the type whose UID is
                            ///m_encoding_uid with the volatile qualifier added
    eEncodingIsTypedefUID,  ///< This type is pointer to a type whose UID is
                            ///m_encoding_uid
    eEncodingIsPointerUID,  ///< This type is pointer to a type whose UID is
                            ///m_encoding_uid
    eEncodingIsLValueReferenceUID, ///< This type is L value reference to a type
                                   ///whose UID is m_encoding_uid
    eEncodingIsRValueReferenceUID, ///< This type is R value reference to a type
                                   ///whose UID is m_encoding_uid
    eEncodingIsSyntheticUID
  } EncodingDataType;

  // We must force the underlying type of the enum to be unsigned here.  Not all
  // compilers
  // behave the same with regards to the default underlying type of an enum, but
  // because
  // this enum is used in an enum bitfield and integer comparisons are done with
  // the value
  // we need to guarantee that it's always unsigned so that, for example,
  // eResolveStateFull
  // doesn't compare less than eResolveStateUnresolved when used in a 2-bit
  // bitfield.
  typedef enum ResolveStateTag : unsigned {
    eResolveStateUnresolved = 0,
    eResolveStateForward = 1,
    eResolveStateLayout = 2,
    eResolveStateFull = 3
  } ResolveState;

  Type(lldb::user_id_t uid, SymbolFile *symbol_file, const ConstString &name,
       uint64_t byte_size, SymbolContextScope *context,
       lldb::user_id_t encoding_uid, EncodingDataType encoding_uid_type,
       const Declaration &decl, const CompilerType &compiler_qual_type,
       ResolveState compiler_type_resolve_state);

  // This makes an invalid type.  Used for functions that return a Type when
  // they
  // get an error.
  Type();

  Type(const Type &rhs);

  const Type &operator=(const Type &rhs);

  void Dump(Stream *s, bool show_context);

  void DumpTypeName(Stream *s);

  // Since Type instances only keep a "SymbolFile *" internally, other classes
  // like TypeImpl need make sure the module is still around before playing with
  // Type instances. They can store a weak pointer to the Module;
  lldb::ModuleSP GetModule();

  void GetDescription(Stream *s, lldb::DescriptionLevel level, bool show_name);

  SymbolFile *GetSymbolFile() { return m_symbol_file; }
  const SymbolFile *GetSymbolFile() const { return m_symbol_file; }

  TypeList *GetTypeList();

  const ConstString &GetName();

  uint64_t GetByteSize();

  uint32_t GetNumChildren(bool omit_empty_base_classes);

  bool IsAggregateType();

  bool IsValidType() { return m_encoding_uid_type != eEncodingInvalid; }

  bool IsTypedef() { return m_encoding_uid_type == eEncodingIsTypedefUID; }

  lldb::TypeSP GetTypedefType();

  const ConstString &GetName() const { return m_name; }

  ConstString GetQualifiedName();

  void DumpValue(ExecutionContext *exe_ctx, Stream *s,
                 const DataExtractor &data, uint32_t data_offset,
                 bool show_type, bool show_summary, bool verbose,
                 lldb::Format format = lldb::eFormatDefault);

  bool DumpValueInMemory(ExecutionContext *exe_ctx, Stream *s,
                         lldb::addr_t address, AddressType address_type,
                         bool show_types, bool show_summary, bool verbose);

  bool ReadFromMemory(ExecutionContext *exe_ctx, lldb::addr_t address,
                      AddressType address_type, DataExtractor &data);

  bool WriteToMemory(ExecutionContext *exe_ctx, lldb::addr_t address,
                     AddressType address_type, DataExtractor &data);

  bool GetIsDeclaration() const;

  void SetIsDeclaration(bool b);

  bool GetIsExternal() const;

  void SetIsExternal(bool b);

  lldb::Format GetFormat();

  lldb::Encoding GetEncoding(uint64_t &count);

  SymbolContextScope *GetSymbolContextScope() { return m_context; }
  const SymbolContextScope *GetSymbolContextScope() const { return m_context; }
  void SetSymbolContextScope(SymbolContextScope *context) {
    m_context = context;
  }

  const lldb_private::Declaration &GetDeclaration() const;

  // Get the clang type, and resolve definitions for any
  // class/struct/union/enum types completely.
  CompilerType GetFullCompilerType();

  // Get the clang type, and resolve definitions enough so that the type could
  // have layout performed. This allows ptrs and refs to class/struct/union/enum
  // types remain forward declarations.
  CompilerType GetLayoutCompilerType();

  // Get the clang type and leave class/struct/union/enum types as forward
  // declarations if they haven't already been fully defined.
  CompilerType GetForwardCompilerType();

  static int Compare(const Type &a, const Type &b);

  // From a fully qualified typename, split the type into the type basename
  // and the remaining type scope (namespaces/classes).
  static bool GetTypeScopeAndBasename(const llvm::StringRef& name,
                                      llvm::StringRef &scope,
                                      llvm::StringRef &basename,
                                      lldb::TypeClass &type_class);
  void SetEncodingType(Type *encoding_type) { m_encoding_type = encoding_type; }

  uint32_t GetEncodingMask();

  bool IsCompleteObjCClass() { return m_flags.is_complete_objc_class; }

  void SetIsCompleteObjCClass(bool is_complete_objc_class) {
    m_flags.is_complete_objc_class = is_complete_objc_class;
  }

protected:
  ConstString m_name;
  SymbolFile *m_symbol_file;
  SymbolContextScope
      *m_context; // The symbol context in which this type is defined
  Type *m_encoding_type;
  lldb::user_id_t m_encoding_uid;
  EncodingDataType m_encoding_uid_type;
  uint64_t m_byte_size;
  Declaration m_decl;
  CompilerType m_compiler_type;

  struct Flags {
#ifdef __GNUC__
    // using unsigned type here to work around a very noisy gcc warning
    unsigned compiler_type_resolve_state : 2;
#else
    ResolveState compiler_type_resolve_state : 2;
#endif
    bool is_complete_objc_class : 1;
  } m_flags;

  Type *GetEncodingType();

  bool ResolveClangType(ResolveState compiler_type_resolve_state);
};

// these classes are used to back the SBType* objects

class TypePair {
public:
  TypePair() : compiler_type(), type_sp() {}

  TypePair(CompilerType type) : compiler_type(type), type_sp() {}

  TypePair(lldb::TypeSP type) : compiler_type(), type_sp(type) {
    compiler_type = type_sp->GetForwardCompilerType();
  }

  bool IsValid() const {
    return compiler_type.IsValid() || (type_sp.get() != nullptr);
  }

  explicit operator bool() const { return IsValid(); }

  bool operator==(const TypePair &rhs) const {
    return compiler_type == rhs.compiler_type &&
           type_sp.get() == rhs.type_sp.get();
  }

  bool operator!=(const TypePair &rhs) const {
    return compiler_type != rhs.compiler_type ||
           type_sp.get() != rhs.type_sp.get();
  }

  void Clear() {
    compiler_type.Clear();
    type_sp.reset();
  }

  ConstString GetName() const {
    if (type_sp)
      return type_sp->GetName();
    if (compiler_type)
      return compiler_type.GetTypeName();
    return ConstString();
  }

  ConstString GetDisplayTypeName() const {
    if (type_sp)
      return type_sp->GetForwardCompilerType().GetDisplayTypeName();
    if (compiler_type)
      return compiler_type.GetDisplayTypeName();
    return ConstString();
  }

  void SetType(CompilerType type) {
    type_sp.reset();
    compiler_type = type;
  }

  void SetType(lldb::TypeSP type) {
    type_sp = type;
    if (type_sp)
      compiler_type = type_sp->GetForwardCompilerType();
    else
      compiler_type.Clear();
  }

  lldb::TypeSP GetTypeSP() const { return type_sp; }

  CompilerType GetCompilerType() const { return compiler_type; }

  CompilerType GetPointerType() const {
    if (type_sp)
      return type_sp->GetForwardCompilerType().GetPointerType();
    return compiler_type.GetPointerType();
  }

  CompilerType GetPointeeType() const {
    if (type_sp)
      return type_sp->GetForwardCompilerType().GetPointeeType();
    return compiler_type.GetPointeeType();
  }

  CompilerType GetReferenceType() const {
    if (type_sp)
      return type_sp->GetForwardCompilerType().GetLValueReferenceType();
    else
      return compiler_type.GetLValueReferenceType();
  }

  CompilerType GetTypedefedType() const {
    if (type_sp)
      return type_sp->GetForwardCompilerType().GetTypedefedType();
    else
      return compiler_type.GetTypedefedType();
  }

  CompilerType GetDereferencedType() const {
    if (type_sp)
      return type_sp->GetForwardCompilerType().GetNonReferenceType();
    else
      return compiler_type.GetNonReferenceType();
  }

  CompilerType GetUnqualifiedType() const {
    if (type_sp)
      return type_sp->GetForwardCompilerType().GetFullyUnqualifiedType();
    else
      return compiler_type.GetFullyUnqualifiedType();
  }

  CompilerType GetCanonicalType() const {
    if (type_sp)
      return type_sp->GetForwardCompilerType().GetCanonicalType();
    return compiler_type.GetCanonicalType();
  }

  TypeSystem *GetTypeSystem() const { return compiler_type.GetTypeSystem(); }

  lldb::ModuleSP GetModule() const {
    if (type_sp)
      return type_sp->GetModule();
    return lldb::ModuleSP();
  }

protected:
  CompilerType compiler_type;
  lldb::TypeSP type_sp;
};

// the two classes here are used by the public API as a backend to
// the SBType and SBTypeList classes

class TypeImpl {
public:
  TypeImpl();

  ~TypeImpl() {}

  TypeImpl(const TypeImpl &rhs);

  TypeImpl(const lldb::TypeSP &type_sp);

  TypeImpl(const CompilerType &compiler_type);

  TypeImpl(const lldb::TypeSP &type_sp, const CompilerType &dynamic);

  TypeImpl(const CompilerType &compiler_type, const CompilerType &dynamic);

  TypeImpl(const TypePair &pair, const CompilerType &dynamic);

  void SetType(const lldb::TypeSP &type_sp);

  void SetType(const CompilerType &compiler_type);

  void SetType(const lldb::TypeSP &type_sp, const CompilerType &dynamic);

  void SetType(const CompilerType &compiler_type, const CompilerType &dynamic);

  void SetType(const TypePair &pair, const CompilerType &dynamic);

  TypeImpl &operator=(const TypeImpl &rhs);

  bool operator==(const TypeImpl &rhs) const;

  bool operator!=(const TypeImpl &rhs) const;

  bool IsValid() const;

  explicit operator bool() const;

  void Clear();

  ConstString GetName() const;

  ConstString GetDisplayTypeName() const;

  TypeImpl GetPointerType() const;

  TypeImpl GetPointeeType() const;

  TypeImpl GetReferenceType() const;

  TypeImpl GetTypedefedType() const;

  TypeImpl GetDereferencedType() const;

  TypeImpl GetUnqualifiedType() const;

  TypeImpl GetCanonicalType() const;

  CompilerType GetCompilerType(bool prefer_dynamic);

  TypeSystem *GetTypeSystem(bool prefer_dynamic);

  bool GetDescription(lldb_private::Stream &strm,
                      lldb::DescriptionLevel description_level);

private:
  bool CheckModule(lldb::ModuleSP &module_sp) const;

  lldb::ModuleWP m_module_wp;
  TypePair m_static_type;
  CompilerType m_dynamic_type;
};

class TypeListImpl {
public:
  TypeListImpl() : m_content() {}

  void Append(const lldb::TypeImplSP &type) { m_content.push_back(type); }

  class AppendVisitor {
  public:
    AppendVisitor(TypeListImpl &type_list) : m_type_list(type_list) {}

    void operator()(const lldb::TypeImplSP &type) { m_type_list.Append(type); }

  private:
    TypeListImpl &m_type_list;
  };

  void Append(const lldb_private::TypeList &type_list);

  lldb::TypeImplSP GetTypeAtIndex(size_t idx) {
    lldb::TypeImplSP type_sp;
    if (idx < GetSize())
      type_sp = m_content[idx];
    return type_sp;
  }

  size_t GetSize() { return m_content.size(); }

private:
  std::vector<lldb::TypeImplSP> m_content;
};

class TypeMemberImpl {
public:
  TypeMemberImpl()
      : m_type_impl_sp(), m_bit_offset(0), m_name(), m_bitfield_bit_size(0),
        m_is_bitfield(false)

  {}

  TypeMemberImpl(const lldb::TypeImplSP &type_impl_sp, uint64_t bit_offset,
                 const ConstString &name, uint32_t bitfield_bit_size = 0,
                 bool is_bitfield = false)
      : m_type_impl_sp(type_impl_sp), m_bit_offset(bit_offset), m_name(name),
        m_bitfield_bit_size(bitfield_bit_size), m_is_bitfield(is_bitfield) {}

  TypeMemberImpl(const lldb::TypeImplSP &type_impl_sp, uint64_t bit_offset)
      : m_type_impl_sp(type_impl_sp), m_bit_offset(bit_offset), m_name(),
        m_bitfield_bit_size(0), m_is_bitfield(false) {
    if (m_type_impl_sp)
      m_name = m_type_impl_sp->GetName();
  }

  const lldb::TypeImplSP &GetTypeImpl() { return m_type_impl_sp; }

  const ConstString &GetName() const { return m_name; }

  uint64_t GetBitOffset() const { return m_bit_offset; }

  uint32_t GetBitfieldBitSize() const { return m_bitfield_bit_size; }

  void SetBitfieldBitSize(uint32_t bitfield_bit_size) {
    m_bitfield_bit_size = bitfield_bit_size;
  }

  bool GetIsBitfield() const { return m_is_bitfield; }

  void SetIsBitfield(bool is_bitfield) { m_is_bitfield = is_bitfield; }

protected:
  lldb::TypeImplSP m_type_impl_sp;
  uint64_t m_bit_offset;
  ConstString m_name;
  uint32_t m_bitfield_bit_size; // Bit size for bitfield members only
  bool m_is_bitfield;
};

///
/// Sometimes you can find the name of the type corresponding to an object, but
/// we don't have debug
/// information for it.  If that is the case, you can return one of these
/// objects, and then if it
/// has a full type, you can use that, but if not at least you can print the
/// name for informational
/// purposes.
///

class TypeAndOrName {
public:
  TypeAndOrName();
  TypeAndOrName(lldb::TypeSP &type_sp);
  TypeAndOrName(const CompilerType &compiler_type);
  TypeAndOrName(const char *type_str);
  TypeAndOrName(const TypeAndOrName &rhs);
  TypeAndOrName(ConstString &type_const_string);

  TypeAndOrName &operator=(const TypeAndOrName &rhs);

  bool operator==(const TypeAndOrName &other) const;

  bool operator!=(const TypeAndOrName &other) const;

  ConstString GetName() const;

  lldb::TypeSP GetTypeSP() const { return m_type_pair.GetTypeSP(); }

  CompilerType GetCompilerType() const { return m_type_pair.GetCompilerType(); }

  void SetName(const ConstString &type_name);

  void SetName(const char *type_name_cstr);

  void SetTypeSP(lldb::TypeSP type_sp);

  void SetCompilerType(CompilerType compiler_type);

  bool IsEmpty() const;

  bool HasName() const;

  bool HasTypeSP() const;

  bool HasCompilerType() const;

  bool HasType() const { return HasTypeSP() || HasCompilerType(); }

  void Clear();

  explicit operator bool() { return !IsEmpty(); }

private:
  TypePair m_type_pair;
  ConstString m_type_name;
};

class TypeMemberFunctionImpl {
public:
  TypeMemberFunctionImpl()
      : m_type(), m_decl(), m_name(), m_kind(lldb::eMemberFunctionKindUnknown) {
  }

  TypeMemberFunctionImpl(const CompilerType &type, const CompilerDecl &decl,
                         const std::string &name,
                         const lldb::MemberFunctionKind &kind)
      : m_type(type), m_decl(decl), m_name(name), m_kind(kind) {}

  bool IsValid();

  ConstString GetName() const;

  ConstString GetMangledName() const;

  CompilerType GetType() const;

  CompilerType GetReturnType() const;

  size_t GetNumArguments() const;

  CompilerType GetArgumentAtIndex(size_t idx) const;

  lldb::MemberFunctionKind GetKind() const;

  bool GetDescription(Stream &stream);

protected:
  std::string GetPrintableTypeName();

private:
  CompilerType m_type;
  CompilerDecl m_decl;
  ConstString m_name;
  lldb::MemberFunctionKind m_kind;
};

class TypeEnumMemberImpl {
public:
  TypeEnumMemberImpl()
      : m_integer_type_sp(), m_name("<invalid>"), m_value(), m_valid(false) {}

  TypeEnumMemberImpl(const lldb::TypeImplSP &integer_type_sp,
                     const ConstString &name, const llvm::APSInt &value);

  TypeEnumMemberImpl(const TypeEnumMemberImpl &rhs)
      : m_integer_type_sp(rhs.m_integer_type_sp), m_name(rhs.m_name),
        m_value(rhs.m_value), m_valid(rhs.m_valid) {}

  TypeEnumMemberImpl &operator=(const TypeEnumMemberImpl &rhs);

  bool IsValid() { return m_valid; }

  const ConstString &GetName() const { return m_name; }

  const lldb::TypeImplSP &GetIntegerType() const { return m_integer_type_sp; }

  uint64_t GetValueAsUnsigned() const { return m_value.getZExtValue(); }

  int64_t GetValueAsSigned() const { return m_value.getSExtValue(); }

protected:
  lldb::TypeImplSP m_integer_type_sp;
  ConstString m_name;
  llvm::APSInt m_value;
  bool m_valid;
};

class TypeEnumMemberListImpl {
public:
  TypeEnumMemberListImpl() : m_content() {}

  void Append(const lldb::TypeEnumMemberImplSP &type) {
    m_content.push_back(type);
  }

  void Append(const lldb_private::TypeEnumMemberListImpl &type_list);

  lldb::TypeEnumMemberImplSP GetTypeEnumMemberAtIndex(size_t idx) {
    lldb::TypeEnumMemberImplSP enum_member;
    if (idx < GetSize())
      enum_member = m_content[idx];
    return enum_member;
  }

  size_t GetSize() { return m_content.size(); }

private:
  std::vector<lldb::TypeEnumMemberImplSP> m_content;
};

} // namespace lldb_private

#endif // liblldb_Type_h_
