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

#include <mutex>
#include <utility>
#include <vector>

#include "lldb/Core/DumpDataExtractor.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/UniqueCStringMap.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/GoASTContext.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Target.h"

#include "llvm/Support/Threading.h"

#include "Plugins/ExpressionParser/Go/GoUserExpression.h"
#include "Plugins/SymbolFile/DWARF/DWARFASTParserGo.h"

using namespace lldb;

namespace lldb_private {
class GoArray;
class GoFunction;
class GoStruct;

class GoType {
public:
  enum {
    KIND_BOOL = 1,
    KIND_INT = 2,
    KIND_INT8 = 3,
    KIND_INT16 = 4,
    KIND_INT32 = 5,
    KIND_INT64 = 6,
    KIND_UINT = 7,
    KIND_UINT8 = 8,
    KIND_UINT16 = 9,
    KIND_UINT32 = 10,
    KIND_UINT64 = 11,
    KIND_UINTPTR = 12,
    KIND_FLOAT32 = 13,
    KIND_FLOAT64 = 14,
    KIND_COMPLEX64 = 15,
    KIND_COMPLEX128 = 16,
    KIND_ARRAY = 17,
    KIND_CHAN = 18,
    KIND_FUNC = 19,
    KIND_INTERFACE = 20,
    KIND_MAP = 21,
    KIND_PTR = 22,
    KIND_SLICE = 23,
    KIND_STRING = 24,
    KIND_STRUCT = 25,
    KIND_UNSAFEPOINTER = 26,
    KIND_LLDB_VOID, // Extension for LLDB, not used by go runtime.
    KIND_MASK = (1 << 5) - 1,
    KIND_DIRECT_IFACE = 1 << 5
  };
  GoType(int kind, const ConstString &name)
      : m_kind(kind & KIND_MASK), m_name(name) {
    if (m_kind == KIND_FUNC)
      m_kind = KIND_FUNC;
  }
  virtual ~GoType() {}

  int GetGoKind() const { return m_kind; }
  const ConstString &GetName() const { return m_name; }
  virtual CompilerType GetElementType() const { return CompilerType(); }

  bool IsTypedef() const {
    switch (m_kind) {
    case KIND_CHAN:
    case KIND_MAP:
    case KIND_INTERFACE:
      return true;
    default:
      return false;
    }
  }

  GoArray *GetArray();
  GoFunction *GetFunction();
  GoStruct *GetStruct();

private:
  int m_kind;
  ConstString m_name;
  GoType(const GoType &) = delete;
  const GoType &operator=(const GoType &) = delete;
};

class GoElem : public GoType {
public:
  GoElem(int kind, const ConstString &name, const CompilerType &elem)
      : GoType(kind, name), m_elem(elem) {}
  virtual CompilerType GetElementType() const { return m_elem; }

private:
  // TODO: should we store this differently?
  CompilerType m_elem;

  GoElem(const GoElem &) = delete;
  const GoElem &operator=(const GoElem &) = delete;
};

class GoArray : public GoElem {
public:
  GoArray(const ConstString &name, uint64_t length, const CompilerType &elem)
      : GoElem(KIND_ARRAY, name, elem), m_length(length) {}

  uint64_t GetLength() const { return m_length; }

private:
  uint64_t m_length;
  GoArray(const GoArray &) = delete;
  const GoArray &operator=(const GoArray &) = delete;
};

class GoFunction : public GoType {
public:
  GoFunction(const ConstString &name, bool is_variadic)
      : GoType(KIND_FUNC, name), m_is_variadic(is_variadic) {}

  bool IsVariadic() const { return m_is_variadic; }

private:
  bool m_is_variadic;
  GoFunction(const GoFunction &) = delete;
  const GoFunction &operator=(const GoFunction &) = delete;
};

class GoStruct : public GoType {
public:
  struct Field {
    Field(const ConstString &name, const CompilerType &type, uint64_t offset)
        : m_name(name), m_type(type), m_byte_offset(offset) {}
    ConstString m_name;
    CompilerType m_type;
    uint64_t m_byte_offset;
  };

  GoStruct(int kind, const ConstString &name, int64_t byte_size)
      : GoType(kind == 0 ? KIND_STRUCT : kind, name), m_is_complete(false),
        m_byte_size(byte_size) {}

  uint32_t GetNumFields() const { return m_fields.size(); }

  const Field *GetField(uint32_t i) const {
    if (i < m_fields.size())
      return &m_fields[i];
    return nullptr;
  }

  void AddField(const ConstString &name, const CompilerType &type,
                uint64_t offset) {
    m_fields.push_back(Field(name, type, offset));
  }

  bool IsComplete() const { return m_is_complete; }

  void SetComplete() { m_is_complete = true; }

  int64_t GetByteSize() const { return m_byte_size; }

private:
  bool m_is_complete;
  int64_t m_byte_size;
  std::vector<Field> m_fields;

  GoStruct(const GoStruct &) = delete;
  const GoStruct &operator=(const GoStruct &) = delete;
};

GoArray *GoType::GetArray() {
  if (m_kind == KIND_ARRAY) {
    return static_cast<GoArray *>(this);
  }
  return nullptr;
}

GoFunction *GoType::GetFunction() {
  if (m_kind == KIND_FUNC) {
    return static_cast<GoFunction *>(this);
  }
  return nullptr;
}

GoStruct *GoType::GetStruct() {
  switch (m_kind) {
  case KIND_STRING:
  case KIND_STRUCT:
  case KIND_SLICE:
    return static_cast<GoStruct *>(this);
  }
  return nullptr;
}
} // namespace lldb_private
using namespace lldb_private;

GoASTContext::GoASTContext()
    : TypeSystem(eKindGo), m_pointer_byte_size(0), m_int_byte_size(0),
      m_types(new TypeMap) {}
GoASTContext::~GoASTContext() {}

//------------------------------------------------------------------
// PluginInterface functions
//------------------------------------------------------------------

ConstString GoASTContext::GetPluginNameStatic() { return ConstString("go"); }

ConstString GoASTContext::GetPluginName() {
  return GoASTContext::GetPluginNameStatic();
}

uint32_t GoASTContext::GetPluginVersion() { return 1; }

lldb::TypeSystemSP GoASTContext::CreateInstance(lldb::LanguageType language,
                                                Module *module,
                                                Target *target) {
  if (language == eLanguageTypeGo) {
    ArchSpec arch;
    std::shared_ptr<GoASTContext> go_ast_sp;
    if (module) {
      arch = module->GetArchitecture();
      go_ast_sp = std::shared_ptr<GoASTContext>(new GoASTContext);
    } else if (target) {
      arch = target->GetArchitecture();
      go_ast_sp = std::shared_ptr<GoASTContextForExpr>(
          new GoASTContextForExpr(target->shared_from_this()));
    }

    if (arch.IsValid()) {
      go_ast_sp->SetAddressByteSize(arch.GetAddressByteSize());
      return go_ast_sp;
    }
  }
  return lldb::TypeSystemSP();
}

void GoASTContext::EnumerateSupportedLanguages(
    std::set<lldb::LanguageType> &languages_for_types,
    std::set<lldb::LanguageType> &languages_for_expressions) {
  static std::vector<lldb::LanguageType> s_supported_languages_for_types(
      {lldb::eLanguageTypeGo});

  static std::vector<lldb::LanguageType> s_supported_languages_for_expressions(
      {});

  languages_for_types.insert(s_supported_languages_for_types.begin(),
                             s_supported_languages_for_types.end());
  languages_for_expressions.insert(
      s_supported_languages_for_expressions.begin(),
      s_supported_languages_for_expressions.end());
}

void GoASTContext::Initialize() {
  PluginManager::RegisterPlugin(GetPluginNameStatic(), "AST context plug-in",
                                CreateInstance, EnumerateSupportedLanguages);
}

void GoASTContext::Terminate() {
  PluginManager::UnregisterPlugin(CreateInstance);
}

//----------------------------------------------------------------------
// Tests
//----------------------------------------------------------------------

bool GoASTContext::IsArrayType(lldb::opaque_compiler_type_t type,
                               CompilerType *element_type, uint64_t *size,
                               bool *is_incomplete) {
  if (element_type)
    element_type->Clear();
  if (size)
    *size = 0;
  if (is_incomplete)
    *is_incomplete = false;
  GoArray *array = static_cast<GoType *>(type)->GetArray();
  if (array) {
    if (size)
      *size = array->GetLength();
    if (element_type)
      *element_type = array->GetElementType();
    return true;
  }
  return false;
}

bool GoASTContext::IsVectorType(lldb::opaque_compiler_type_t type,
                                CompilerType *element_type, uint64_t *size) {
  if (element_type)
    element_type->Clear();
  if (size)
    *size = 0;
  return false;
}

bool GoASTContext::IsAggregateType(lldb::opaque_compiler_type_t type) {
  int kind = static_cast<GoType *>(type)->GetGoKind();
  if (kind < GoType::KIND_ARRAY)
    return false;
  if (kind == GoType::KIND_PTR)
    return false;
  if (kind == GoType::KIND_CHAN)
    return false;
  if (kind == GoType::KIND_MAP)
    return false;
  if (kind == GoType::KIND_STRING)
    return false;
  if (kind == GoType::KIND_UNSAFEPOINTER)
    return false;
  return true;
}

bool GoASTContext::IsBeingDefined(lldb::opaque_compiler_type_t type) {
  return false;
}

bool GoASTContext::IsCharType(lldb::opaque_compiler_type_t type) {
  // Go's DWARF doesn't distinguish between rune and int32.
  return false;
}

bool GoASTContext::IsCompleteType(lldb::opaque_compiler_type_t type) {
  if (!type)
    return false;
  GoType *t = static_cast<GoType *>(type);
  if (GoStruct *s = t->GetStruct())
    return s->IsComplete();
  if (t->IsTypedef() || t->GetGoKind() == GoType::KIND_PTR)
    return t->GetElementType().IsCompleteType();
  return true;
}

bool GoASTContext::IsConst(lldb::opaque_compiler_type_t type) { return false; }

bool GoASTContext::IsCStringType(lldb::opaque_compiler_type_t type,
                                 uint32_t &length) {
  return false;
}

bool GoASTContext::IsDefined(lldb::opaque_compiler_type_t type) {
  return type != nullptr;
}

bool GoASTContext::IsFloatingPointType(lldb::opaque_compiler_type_t type,
                                       uint32_t &count, bool &is_complex) {
  int kind = static_cast<GoType *>(type)->GetGoKind();
  if (kind >= GoType::KIND_FLOAT32 && kind <= GoType::KIND_COMPLEX128) {
    if (kind >= GoType::KIND_COMPLEX64) {
      is_complex = true;
      count = 2;
    } else {
      is_complex = false;
      count = 1;
    }
    return true;
  }
  count = 0;
  is_complex = false;
  return false;
}

bool GoASTContext::IsFunctionType(lldb::opaque_compiler_type_t type,
                                  bool *is_variadic_ptr) {
  GoFunction *func = static_cast<GoType *>(type)->GetFunction();
  if (func) {
    if (is_variadic_ptr)
      *is_variadic_ptr = func->IsVariadic();
    return true;
  }
  if (is_variadic_ptr)
    *is_variadic_ptr = false;
  return false;
}

uint32_t GoASTContext::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type,
                                              CompilerType *base_type_ptr) {
  return false;
}

size_t
GoASTContext::GetNumberOfFunctionArguments(lldb::opaque_compiler_type_t type) {
  return 0;
}

CompilerType
GoASTContext::GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type,
                                         const size_t index) {
  return CompilerType();
}

bool GoASTContext::IsFunctionPointerType(lldb::opaque_compiler_type_t type) {
  return IsFunctionType(type);
}

bool GoASTContext::IsBlockPointerType(lldb::opaque_compiler_type_t type,
                                      CompilerType *function_pointer_type_ptr) {
  return false;
}

bool GoASTContext::IsIntegerType(lldb::opaque_compiler_type_t type,
                                 bool &is_signed) {
  is_signed = false;
  // TODO: Is bool an integer?
  if (type) {
    int kind = static_cast<GoType *>(type)->GetGoKind();
    if (kind <= GoType::KIND_UINTPTR) {
      is_signed = (kind != GoType::KIND_BOOL) & (kind <= GoType::KIND_INT64);
      return true;
    }
  }
  return false;
}

bool GoASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) {
  return false;
}

bool GoASTContext::IsPossibleDynamicType(
    lldb::opaque_compiler_type_t type,
    CompilerType *target_type, // Can pass NULL
    bool check_cplusplus, bool check_objc) {
  if (target_type)
    target_type->Clear();
  if (type)
    return static_cast<GoType *>(type)->GetGoKind() == GoType::KIND_INTERFACE;
  return false;
}

bool GoASTContext::IsRuntimeGeneratedType(lldb::opaque_compiler_type_t type) {
  return false;
}

bool GoASTContext::IsPointerType(lldb::opaque_compiler_type_t type,
                                 CompilerType *pointee_type) {
  if (!type)
    return false;
  GoType *t = static_cast<GoType *>(type);
  if (pointee_type) {
    *pointee_type = t->GetElementType();
  }
  switch (t->GetGoKind()) {
  case GoType::KIND_PTR:
  case GoType::KIND_UNSAFEPOINTER:
  case GoType::KIND_CHAN:
  case GoType::KIND_MAP:
    // TODO: is function a pointer?
    return true;
  default:
    return false;
  }
}

bool GoASTContext::IsPointerOrReferenceType(lldb::opaque_compiler_type_t type,
                                            CompilerType *pointee_type) {
  return IsPointerType(type, pointee_type);
}

bool GoASTContext::IsReferenceType(lldb::opaque_compiler_type_t type,
                                   CompilerType *pointee_type,
                                   bool *is_rvalue) {
  return false;
}

bool GoASTContext::IsScalarType(lldb::opaque_compiler_type_t type) {
  return !IsAggregateType(type);
}

bool GoASTContext::IsTypedefType(lldb::opaque_compiler_type_t type) {
  if (type)
    return static_cast<GoType *>(type)->IsTypedef();
  return false;
}

bool GoASTContext::IsVoidType(lldb::opaque_compiler_type_t type) {
  if (!type)
    return false;
  return static_cast<GoType *>(type)->GetGoKind() == GoType::KIND_LLDB_VOID;
}

bool GoASTContext::SupportsLanguage(lldb::LanguageType language) {
  return language == eLanguageTypeGo;
}

//----------------------------------------------------------------------
// Type Completion
//----------------------------------------------------------------------

bool GoASTContext::GetCompleteType(lldb::opaque_compiler_type_t type) {
  if (!type)
    return false;
  GoType *t = static_cast<GoType *>(type);
  if (t->IsTypedef() || t->GetGoKind() == GoType::KIND_PTR || t->GetArray())
    return t->GetElementType().GetCompleteType();
  if (GoStruct *s = t->GetStruct()) {
    if (s->IsComplete())
      return true;
    CompilerType compiler_type(this, s);
    SymbolFile *symbols = GetSymbolFile();
    return symbols && symbols->CompleteType(compiler_type);
  }
  return true;
}

//----------------------------------------------------------------------
// AST related queries
//----------------------------------------------------------------------

uint32_t GoASTContext::GetPointerByteSize() { return m_pointer_byte_size; }

//----------------------------------------------------------------------
// Accessors
//----------------------------------------------------------------------

ConstString GoASTContext::GetTypeName(lldb::opaque_compiler_type_t type) {
  if (type)
    return static_cast<GoType *>(type)->GetName();
  return ConstString();
}

uint32_t
GoASTContext::GetTypeInfo(lldb::opaque_compiler_type_t type,
                          CompilerType *pointee_or_element_compiler_type) {
  if (pointee_or_element_compiler_type)
    pointee_or_element_compiler_type->Clear();
  if (!type)
    return 0;
  GoType *t = static_cast<GoType *>(type);
  if (pointee_or_element_compiler_type)
    *pointee_or_element_compiler_type = t->GetElementType();
  int kind = t->GetGoKind();
  if (kind == GoType::KIND_ARRAY)
    return eTypeHasChildren | eTypeIsArray;
  if (kind < GoType::KIND_ARRAY) {
    uint32_t builtin_type_flags = eTypeIsBuiltIn | eTypeHasValue;
    if (kind < GoType::KIND_FLOAT32) {
      builtin_type_flags |= eTypeIsInteger | eTypeIsScalar;
      if (kind >= GoType::KIND_INT && kind <= GoType::KIND_INT64)
        builtin_type_flags |= eTypeIsSigned;
    } else {
      builtin_type_flags |= eTypeIsFloat;
      if (kind < GoType::KIND_COMPLEX64)
        builtin_type_flags |= eTypeIsComplex;
      else
        builtin_type_flags |= eTypeIsScalar;
    }
    return builtin_type_flags;
  }
  if (kind == GoType::KIND_STRING)
    return eTypeHasValue | eTypeIsBuiltIn;
  if (kind == GoType::KIND_FUNC)
    return eTypeIsFuncPrototype | eTypeHasValue;
  if (IsPointerType(type))
    return eTypeIsPointer | eTypeHasValue | eTypeHasChildren;
  if (kind == GoType::KIND_LLDB_VOID)
    return 0;
  return eTypeHasChildren | eTypeIsStructUnion;
}

lldb::TypeClass GoASTContext::GetTypeClass(lldb::opaque_compiler_type_t type) {
  if (!type)
    return eTypeClassInvalid;
  int kind = static_cast<GoType *>(type)->GetGoKind();
  if (kind == GoType::KIND_FUNC)
    return eTypeClassFunction;
  if (IsPointerType(type))
    return eTypeClassPointer;
  if (kind < GoType::KIND_COMPLEX64)
    return eTypeClassBuiltin;
  if (kind <= GoType::KIND_COMPLEX128)
    return eTypeClassComplexFloat;
  if (kind == GoType::KIND_LLDB_VOID)
    return eTypeClassInvalid;
  return eTypeClassStruct;
}

lldb::BasicType
GoASTContext::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) {
  ConstString name = GetTypeName(type);
  if (name) {
    typedef UniqueCStringMap<lldb::BasicType> TypeNameToBasicTypeMap;
    static TypeNameToBasicTypeMap g_type_map;
    static llvm::once_flag g_once_flag;
    llvm::call_once(g_once_flag, []() {
      // "void"
      g_type_map.Append(ConstString("void"), eBasicTypeVoid);
      // "int"
      g_type_map.Append(ConstString("int"), eBasicTypeInt);
      g_type_map.Append(ConstString("uint"), eBasicTypeUnsignedInt);

      // Miscellaneous
      g_type_map.Append(ConstString("bool"), eBasicTypeBool);

      // Others. Should these map to C types?
      g_type_map.Append(ConstString("byte"), eBasicTypeOther);
      g_type_map.Append(ConstString("uint8"), eBasicTypeOther);
      g_type_map.Append(ConstString("uint16"), eBasicTypeOther);
      g_type_map.Append(ConstString("uint32"), eBasicTypeOther);
      g_type_map.Append(ConstString("uint64"), eBasicTypeOther);
      g_type_map.Append(ConstString("int8"), eBasicTypeOther);
      g_type_map.Append(ConstString("int16"), eBasicTypeOther);
      g_type_map.Append(ConstString("int32"), eBasicTypeOther);
      g_type_map.Append(ConstString("int64"), eBasicTypeOther);
      g_type_map.Append(ConstString("float32"), eBasicTypeOther);
      g_type_map.Append(ConstString("float64"), eBasicTypeOther);
      g_type_map.Append(ConstString("uintptr"), eBasicTypeOther);

      g_type_map.Sort();
    });

    return g_type_map.Find(name, eBasicTypeInvalid);
  }
  return eBasicTypeInvalid;
}

lldb::LanguageType
GoASTContext::GetMinimumLanguage(lldb::opaque_compiler_type_t type) {
  return lldb::eLanguageTypeGo;
}

unsigned GoASTContext::GetTypeQualifiers(lldb::opaque_compiler_type_t type) {
  return 0;
}

//----------------------------------------------------------------------
// Creating related types
//----------------------------------------------------------------------

CompilerType
GoASTContext::GetArrayElementType(lldb::opaque_compiler_type_t type,
                                  uint64_t *stride) {
  GoArray *array = static_cast<GoType *>(type)->GetArray();
  if (array) {
    if (stride) {
      *stride = array->GetElementType().GetByteSize(nullptr);
    }
    return array->GetElementType();
  }
  return CompilerType();
}

CompilerType GoASTContext::GetCanonicalType(lldb::opaque_compiler_type_t type) {
  GoType *t = static_cast<GoType *>(type);
  if (t->IsTypedef())
    return t->GetElementType();
  return CompilerType(this, type);
}

CompilerType
GoASTContext::GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) {
  return CompilerType(this, type);
}

// Returns -1 if this isn't a function of if the function doesn't have a
// prototype
// Returns a value >= 0 if there is a prototype.
int GoASTContext::GetFunctionArgumentCount(lldb::opaque_compiler_type_t type) {
  return GetNumberOfFunctionArguments(type);
}

CompilerType
GoASTContext::GetFunctionArgumentTypeAtIndex(lldb::opaque_compiler_type_t type,
                                             size_t idx) {
  return GetFunctionArgumentAtIndex(type, idx);
}

CompilerType
GoASTContext::GetFunctionReturnType(lldb::opaque_compiler_type_t type) {
  CompilerType result;
  if (type) {
    GoType *t = static_cast<GoType *>(type);
    if (t->GetGoKind() == GoType::KIND_FUNC)
      result = t->GetElementType();
  }
  return result;
}

size_t GoASTContext::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) {
  return 0;
}

TypeMemberFunctionImpl
GoASTContext::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type,
                                       size_t idx) {
  return TypeMemberFunctionImpl();
}

CompilerType
GoASTContext::GetNonReferenceType(lldb::opaque_compiler_type_t type) {
  return CompilerType(this, type);
}

CompilerType GoASTContext::GetPointeeType(lldb::opaque_compiler_type_t type) {
  if (!type)
    return CompilerType();
  return static_cast<GoType *>(type)->GetElementType();
}

CompilerType GoASTContext::GetPointerType(lldb::opaque_compiler_type_t type) {
  if (!type)
    return CompilerType();
  ConstString type_name = GetTypeName(type);
  ConstString pointer_name(std::string("*") + type_name.GetCString());
  GoType *pointer = (*m_types)[pointer_name].get();
  if (pointer == nullptr) {
    pointer =
        new GoElem(GoType::KIND_PTR, pointer_name, CompilerType(this, type));
    (*m_types)[pointer_name].reset(pointer);
  }
  return CompilerType(this, pointer);
}

// If the current object represents a typedef type, get the underlying type
CompilerType GoASTContext::GetTypedefedType(lldb::opaque_compiler_type_t type) {
  if (IsTypedefType(type))
    return static_cast<GoType *>(type)->GetElementType();
  return CompilerType();
}

//----------------------------------------------------------------------
// Create related types using the current type's AST
//----------------------------------------------------------------------
CompilerType GoASTContext::GetBasicTypeFromAST(lldb::BasicType basic_type) {
  return CompilerType();
}

CompilerType
GoASTContext::GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding,
                                                  size_t bit_size) {
  return CompilerType();
}

//----------------------------------------------------------------------
// Exploring the type
//----------------------------------------------------------------------

uint64_t GoASTContext::GetBitSize(lldb::opaque_compiler_type_t type,
                                  ExecutionContextScope *exe_scope) {
  if (!type)
    return 0;
  if (!GetCompleteType(type))
    return 0;
  GoType *t = static_cast<GoType *>(type);
  GoArray *array = nullptr;
  switch (t->GetGoKind()) {
  case GoType::KIND_BOOL:
  case GoType::KIND_INT8:
  case GoType::KIND_UINT8:
    return 8;
  case GoType::KIND_INT16:
  case GoType::KIND_UINT16:
    return 16;
  case GoType::KIND_INT32:
  case GoType::KIND_UINT32:
  case GoType::KIND_FLOAT32:
    return 32;
  case GoType::KIND_INT64:
  case GoType::KIND_UINT64:
  case GoType::KIND_FLOAT64:
  case GoType::KIND_COMPLEX64:
    return 64;
  case GoType::KIND_COMPLEX128:
    return 128;
  case GoType::KIND_INT:
  case GoType::KIND_UINT:
    return m_int_byte_size * 8;
  case GoType::KIND_UINTPTR:
  case GoType::KIND_FUNC: // I assume this is a pointer?
  case GoType::KIND_CHAN:
  case GoType::KIND_PTR:
  case GoType::KIND_UNSAFEPOINTER:
  case GoType::KIND_MAP:
    return m_pointer_byte_size * 8;
  case GoType::KIND_ARRAY:
    array = t->GetArray();
    return array->GetLength() * array->GetElementType().GetBitSize(exe_scope);
  case GoType::KIND_INTERFACE:
    return t->GetElementType().GetBitSize(exe_scope);
  case GoType::KIND_SLICE:
  case GoType::KIND_STRING:
  case GoType::KIND_STRUCT:
    return t->GetStruct()->GetByteSize() * 8;
  default:
    assert(false);
  }
  return 0;
}

lldb::Encoding GoASTContext::GetEncoding(lldb::opaque_compiler_type_t type,
                                         uint64_t &count) {
  count = 1;
  bool is_signed;
  if (IsIntegerType(type, is_signed))
    return is_signed ? lldb::eEncodingSint : eEncodingUint;
  bool is_complex;
  uint32_t complex_count;
  if (IsFloatingPointType(type, complex_count, is_complex)) {
    count = complex_count;
    return eEncodingIEEE754;
  }
  if (IsPointerType(type))
    return eEncodingUint;
  return eEncodingInvalid;
}

lldb::Format GoASTContext::GetFormat(lldb::opaque_compiler_type_t type) {
  if (!type)
    return eFormatDefault;
  switch (static_cast<GoType *>(type)->GetGoKind()) {
  case GoType::KIND_BOOL:
    return eFormatBoolean;
  case GoType::KIND_INT:
  case GoType::KIND_INT8:
  case GoType::KIND_INT16:
  case GoType::KIND_INT32:
  case GoType::KIND_INT64:
    return eFormatDecimal;
  case GoType::KIND_UINT:
  case GoType::KIND_UINT8:
  case GoType::KIND_UINT16:
  case GoType::KIND_UINT32:
  case GoType::KIND_UINT64:
    return eFormatUnsigned;
  case GoType::KIND_FLOAT32:
  case GoType::KIND_FLOAT64:
    return eFormatFloat;
  case GoType::KIND_COMPLEX64:
  case GoType::KIND_COMPLEX128:
    return eFormatComplexFloat;
  case GoType::KIND_UINTPTR:
  case GoType::KIND_CHAN:
  case GoType::KIND_PTR:
  case GoType::KIND_MAP:
  case GoType::KIND_UNSAFEPOINTER:
    return eFormatHex;
  case GoType::KIND_STRING:
    return eFormatCString;
  case GoType::KIND_ARRAY:
  case GoType::KIND_INTERFACE:
  case GoType::KIND_SLICE:
  case GoType::KIND_STRUCT:
  default:
    // Don't know how to display this.
    return eFormatBytes;
  }
}

size_t GoASTContext::GetTypeBitAlign(lldb::opaque_compiler_type_t type) {
  return 0;
}

uint32_t GoASTContext::GetNumChildren(lldb::opaque_compiler_type_t type,
                                      bool omit_empty_base_classes) {
  if (!type || !GetCompleteType(type))
    return 0;
  GoType *t = static_cast<GoType *>(type);
  if (t->GetGoKind() == GoType::KIND_PTR) {
    CompilerType elem = t->GetElementType();
    if (elem.IsAggregateType())
      return elem.GetNumChildren(omit_empty_base_classes);
    return 1;
  } else if (GoArray *array = t->GetArray()) {
    return array->GetLength();
  } else if (t->IsTypedef()) {
    return t->GetElementType().GetNumChildren(omit_empty_base_classes);
  }

  return GetNumFields(type);
}

uint32_t GoASTContext::GetNumFields(lldb::opaque_compiler_type_t type) {
  if (!type || !GetCompleteType(type))
    return 0;
  GoType *t = static_cast<GoType *>(type);
  if (t->IsTypedef())
    return t->GetElementType().GetNumFields();
  GoStruct *s = t->GetStruct();
  if (s)
    return s->GetNumFields();
  return 0;
}

CompilerType GoASTContext::GetFieldAtIndex(lldb::opaque_compiler_type_t type,
                                           size_t idx, std::string &name,
                                           uint64_t *bit_offset_ptr,
                                           uint32_t *bitfield_bit_size_ptr,
                                           bool *is_bitfield_ptr) {
  if (bit_offset_ptr)
    *bit_offset_ptr = 0;
  if (bitfield_bit_size_ptr)
    *bitfield_bit_size_ptr = 0;
  if (is_bitfield_ptr)
    *is_bitfield_ptr = false;

  if (!type || !GetCompleteType(type))
    return CompilerType();

  GoType *t = static_cast<GoType *>(type);
  if (t->IsTypedef())
    return t->GetElementType().GetFieldAtIndex(
        idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr);

  GoStruct *s = t->GetStruct();
  if (s) {
    const auto *field = s->GetField(idx);
    if (field) {
      name = field->m_name.GetStringRef();
      if (bit_offset_ptr)
        *bit_offset_ptr = field->m_byte_offset * 8;
      return field->m_type;
    }
  }
  return CompilerType();
}

CompilerType GoASTContext::GetChildCompilerTypeAtIndex(
    lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx,
    bool transparent_pointers, bool omit_empty_base_classes,
    bool ignore_array_bounds, std::string &child_name,
    uint32_t &child_byte_size, int32_t &child_byte_offset,
    uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset,
    bool &child_is_base_class, bool &child_is_deref_of_parent,
    ValueObject *valobj, uint64_t &language_flags) {
  child_name.clear();
  child_byte_size = 0;
  child_byte_offset = 0;
  child_bitfield_bit_size = 0;
  child_bitfield_bit_offset = 0;
  child_is_base_class = false;
  child_is_deref_of_parent = false;
  language_flags = 0;

  if (!type || !GetCompleteType(type))
    return CompilerType();

  GoType *t = static_cast<GoType *>(type);
  if (t->GetStruct()) {
    uint64_t bit_offset;
    CompilerType ret =
        GetFieldAtIndex(type, idx, child_name, &bit_offset, nullptr, nullptr);
    child_byte_size = ret.GetByteSize(
        exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr);
    child_byte_offset = bit_offset / 8;
    return ret;
  } else if (t->GetGoKind() == GoType::KIND_PTR) {
    CompilerType pointee = t->GetElementType();
    if (!pointee.IsValid() || pointee.IsVoidType())
      return CompilerType();
    if (transparent_pointers && pointee.IsAggregateType()) {
      bool tmp_child_is_deref_of_parent = false;
      return pointee.GetChildCompilerTypeAtIndex(
          exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
          ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
          child_bitfield_bit_size, child_bitfield_bit_offset,
          child_is_base_class, tmp_child_is_deref_of_parent, valobj,
          language_flags);
    } else {
      child_is_deref_of_parent = true;
      const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
      if (parent_name) {
        child_name.assign(1, '*');
        child_name += parent_name;
      }

      // We have a pointer to an simple type
      if (idx == 0 && pointee.GetCompleteType()) {
        child_byte_size = pointee.GetByteSize(
            exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL);
        child_byte_offset = 0;
        return pointee;
      }
    }
  } else if (GoArray *a = t->GetArray()) {
    if (ignore_array_bounds || idx < a->GetLength()) {
      CompilerType element_type = a->GetElementType();
      if (element_type.GetCompleteType()) {
        char element_name[64];
        ::snprintf(element_name, sizeof(element_name), "[%zu]", idx);
        child_name.assign(element_name);
        child_byte_size = element_type.GetByteSize(
            exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL);
        child_byte_offset = (int32_t)idx * (int32_t)child_byte_size;
        return element_type;
      }
    }
  } else if (t->IsTypedef()) {
    return t->GetElementType().GetChildCompilerTypeAtIndex(
        exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
        ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
        child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
        child_is_deref_of_parent, valobj, language_flags);
  }
  return CompilerType();
}

// Lookup a child given a name. This function will match base class names
// and member member names in "clang_type" only, not descendants.
uint32_t
GoASTContext::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type,
                                      const char *name,
                                      bool omit_empty_base_classes) {
  if (!type || !GetCompleteType(type))
    return UINT_MAX;

  GoType *t = static_cast<GoType *>(type);
  GoStruct *s = t->GetStruct();
  if (s) {
    for (uint32_t i = 0; i < s->GetNumFields(); ++i) {
      const GoStruct::Field *f = s->GetField(i);
      if (f->m_name.GetStringRef() == name)
        return i;
    }
  } else if (t->GetGoKind() == GoType::KIND_PTR || t->IsTypedef()) {
    return t->GetElementType().GetIndexOfChildWithName(name,
                                                       omit_empty_base_classes);
  }
  return UINT_MAX;
}

// Lookup a child member given a name. This function will match member names
// only and will descend into "clang_type" children in search for the first
// member in this class, or any base class that matches "name".
// TODO: Return all matches for a given name by returning a
// vector<vector<uint32_t>>
// so we catch all names that match a given child name, not just the first.
size_t GoASTContext::GetIndexOfChildMemberWithName(
    lldb::opaque_compiler_type_t type, const char *name,
    bool omit_empty_base_classes, std::vector<uint32_t> &child_indexes) {
  uint32_t index = GetIndexOfChildWithName(type, name, omit_empty_base_classes);
  if (index == UINT_MAX)
    return 0;
  child_indexes.push_back(index);
  return 1;
}

// Converts "s" to a floating point value and place resulting floating
// point bytes in the "dst" buffer.
size_t
GoASTContext::ConvertStringToFloatValue(lldb::opaque_compiler_type_t type,
                                        const char *s, uint8_t *dst,
                                        size_t dst_size) {
  assert(false);
  return 0;
}
//----------------------------------------------------------------------
// Dumping types
//----------------------------------------------------------------------
#define DEPTH_INCREMENT 2

void GoASTContext::DumpValue(lldb::opaque_compiler_type_t type,
                             ExecutionContext *exe_ctx, Stream *s,
                             lldb::Format format, const DataExtractor &data,
                             lldb::offset_t data_byte_offset,
                             size_t data_byte_size, uint32_t bitfield_bit_size,
                             uint32_t bitfield_bit_offset, bool show_types,
                             bool show_summary, bool verbose, uint32_t depth) {
  if (IsTypedefType(type))
    type = GetTypedefedType(type).GetOpaqueQualType();
  if (!type)
    return;
  GoType *t = static_cast<GoType *>(type);

  if (GoStruct *st = t->GetStruct()) {
    if (GetCompleteType(type)) {
      uint32_t field_idx = 0;
      for (auto *field = st->GetField(field_idx); field != nullptr;
           field_idx++) {
        // Print the starting squiggly bracket (if this is the
        // first member) or comma (for member 2 and beyond) for
        // the struct/union/class member.
        if (field_idx == 0)
          s->PutChar('{');
        else
          s->PutChar(',');

        // Indent
        s->Printf("\n%*s", depth + DEPTH_INCREMENT, "");

        // Print the member type if requested
        if (show_types) {
          ConstString field_type_name = field->m_type.GetTypeName();
          s->Printf("(%s) ", field_type_name.AsCString());
        }
        // Print the member name and equal sign
        s->Printf("%s = ", field->m_name.AsCString());

        // Dump the value of the member
        CompilerType field_type = field->m_type;
        field_type.DumpValue(
            exe_ctx,
            s, // Stream to dump to
            field_type
                .GetFormat(), // The format with which to display the member
            data,             // Data buffer containing all bytes for this type
            data_byte_offset + field->m_byte_offset, // Offset into "data" where
                                                     // to grab value from
            field->m_type.GetByteSize(
                exe_ctx->GetBestExecutionContextScope()), // Size of this type
                                                          // in bytes
            0,                                            // Bitfield bit size
            0,                                            // Bitfield bit offset
            show_types,   // Boolean indicating if we should show the variable
                          // types
            show_summary, // Boolean indicating if we should show a summary for
                          // the current type
            verbose,      // Verbose output?
            depth + DEPTH_INCREMENT); // Scope depth for any types that have
                                      // children
      }

      // Indent the trailing squiggly bracket
      if (field_idx > 0)
        s->Printf("\n%*s}", depth, "");
    }
  }

  if (GoArray *a = t->GetArray()) {
    CompilerType element_clang_type = a->GetElementType();
    lldb::Format element_format = element_clang_type.GetFormat();
    uint32_t element_byte_size =
        element_clang_type.GetByteSize(exe_ctx->GetBestExecutionContextScope());

    uint64_t element_idx;
    for (element_idx = 0; element_idx < a->GetLength(); ++element_idx) {
      // Print the starting squiggly bracket (if this is the
      // first member) or comman (for member 2 and beyong) for
      // the struct/union/class member.
      if (element_idx == 0)
        s->PutChar('{');
      else
        s->PutChar(',');

      // Indent and print the index
      s->Printf("\n%*s[%" PRIu64 "] ", depth + DEPTH_INCREMENT, "",
                element_idx);

      // Figure out the field offset within the current struct/union/class type
      uint64_t element_offset = element_idx * element_byte_size;

      // Dump the value of the member
      element_clang_type.DumpValue(
          exe_ctx,
          s,              // Stream to dump to
          element_format, // The format with which to display the element
          data,           // Data buffer containing all bytes for this type
          data_byte_offset +
              element_offset, // Offset into "data" where to grab value from
          element_byte_size,  // Size of this type in bytes
          0,                  // Bitfield bit size
          0,                  // Bitfield bit offset
          show_types, // Boolean indicating if we should show the variable types
          show_summary, // Boolean indicating if we should show a summary for
                        // the current type
          verbose,      // Verbose output?
          depth +
              DEPTH_INCREMENT); // Scope depth for any types that have children
    }

    // Indent the trailing squiggly bracket
    if (element_idx > 0)
      s->Printf("\n%*s}", depth, "");
  }

  if (show_summary)
    DumpSummary(type, exe_ctx, s, data, data_byte_offset, data_byte_size);
}

bool GoASTContext::DumpTypeValue(lldb::opaque_compiler_type_t type, Stream *s,
                                 lldb::Format format, const DataExtractor &data,
                                 lldb::offset_t byte_offset, size_t byte_size,
                                 uint32_t bitfield_bit_size,
                                 uint32_t bitfield_bit_offset,
                                 ExecutionContextScope *exe_scope) {
  if (!type)
    return false;
  if (IsAggregateType(type)) {
    return false;
  } else {
    GoType *t = static_cast<GoType *>(type);
    if (t->IsTypedef()) {
      CompilerType typedef_compiler_type = t->GetElementType();
      if (format == eFormatDefault)
        format = typedef_compiler_type.GetFormat();
      uint64_t typedef_byte_size = typedef_compiler_type.GetByteSize(exe_scope);

      return typedef_compiler_type.DumpTypeValue(
          s,
          format,            // The format with which to display the element
          data,              // Data buffer containing all bytes for this type
          byte_offset,       // Offset into "data" where to grab value from
          typedef_byte_size, // Size of this type in bytes
          bitfield_bit_size, // Size in bits of a bitfield value, if zero don't
                             // treat as a bitfield
          bitfield_bit_offset, // Offset in bits of a bitfield value if
                               // bitfield_bit_size != 0
          exe_scope);
    }

    uint32_t item_count = 1;
    // A few formats, we might need to modify our size and count for depending
    // on how we are trying to display the value...
    switch (format) {
    default:
    case eFormatBoolean:
    case eFormatBinary:
    case eFormatComplex:
    case eFormatCString: // NULL terminated C strings
    case eFormatDecimal:
    case eFormatEnum:
    case eFormatHex:
    case eFormatHexUppercase:
    case eFormatFloat:
    case eFormatOctal:
    case eFormatOSType:
    case eFormatUnsigned:
    case eFormatPointer:
    case eFormatVectorOfChar:
    case eFormatVectorOfSInt8:
    case eFormatVectorOfUInt8:
    case eFormatVectorOfSInt16:
    case eFormatVectorOfUInt16:
    case eFormatVectorOfSInt32:
    case eFormatVectorOfUInt32:
    case eFormatVectorOfSInt64:
    case eFormatVectorOfUInt64:
    case eFormatVectorOfFloat32:
    case eFormatVectorOfFloat64:
    case eFormatVectorOfUInt128:
      break;

    case eFormatChar:
    case eFormatCharPrintable:
    case eFormatCharArray:
    case eFormatBytes:
    case eFormatBytesWithASCII:
      item_count = byte_size;
      byte_size = 1;
      break;

    case eFormatUnicode16:
      item_count = byte_size / 2;
      byte_size = 2;
      break;

    case eFormatUnicode32:
      item_count = byte_size / 4;
      byte_size = 4;
      break;
    }
    return DumpDataExtractor(data, s, byte_offset, format, byte_size,
                             item_count, UINT32_MAX, LLDB_INVALID_ADDRESS,
                             bitfield_bit_size, bitfield_bit_offset, exe_scope);
  }
  return 0;
}

void GoASTContext::DumpSummary(lldb::opaque_compiler_type_t type,
                               ExecutionContext *exe_ctx, Stream *s,
                               const DataExtractor &data,
                               lldb::offset_t data_offset,
                               size_t data_byte_size) {
  if (type && GoType::KIND_STRING == static_cast<GoType *>(type)->GetGoKind()) {
    // TODO(ribrdb): read length and data
  }
}

void GoASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type) {
  // Dump to stdout
  StreamFile s(stdout, false);
  DumpTypeDescription(type, &s);
}

void GoASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type,
                                       Stream *s) {
  if (!type)
    return;
  ConstString name = GetTypeName(type);
  GoType *t = static_cast<GoType *>(type);

  if (GoStruct *st = t->GetStruct()) {
    if (GetCompleteType(type)) {
      if (NULL == strchr(name.AsCString(), '{'))
        s->Printf("type %s ", name.AsCString());
      s->PutCString("struct {");
      if (st->GetNumFields() == 0) {
        s->PutChar('}');
        return;
      }
      s->IndentMore();
      uint32_t field_idx = 0;
      for (auto *field = st->GetField(field_idx); field != nullptr;
           field_idx++) {
        s->PutChar('\n');
        s->Indent();
        s->Printf("%s %s", field->m_name.AsCString(),
                  field->m_type.GetTypeName().AsCString());
      }
      s->IndentLess();
      s->PutChar('\n');
      s->Indent("}");
      return;
    }
  }

  s->PutCString(name.AsCString());
}

CompilerType GoASTContext::CreateArrayType(const ConstString &name,
                                           const CompilerType &element_type,
                                           uint64_t length) {
  GoType *type = new GoArray(name, length, element_type);
  (*m_types)[name].reset(type);
  return CompilerType(this, type);
}

CompilerType GoASTContext::CreateBaseType(int go_kind,
                                          const lldb_private::ConstString &name,
                                          uint64_t byte_size) {
  if (go_kind == GoType::KIND_UINT || go_kind == GoType::KIND_INT)
    m_int_byte_size = byte_size;
  GoType *type = new GoType(go_kind, name);
  (*m_types)[name].reset(type);
  return CompilerType(this, type);
}

CompilerType GoASTContext::CreateTypedefType(int kind, const ConstString &name,
                                             CompilerType impl) {
  GoType *type = new GoElem(kind, name, impl);
  (*m_types)[name].reset(type);
  return CompilerType(this, type);
}

CompilerType
GoASTContext::CreateVoidType(const lldb_private::ConstString &name) {
  GoType *type = new GoType(GoType::KIND_LLDB_VOID, name);
  (*m_types)[name].reset(type);
  return CompilerType(this, type);
}

CompilerType
GoASTContext::CreateStructType(int kind, const lldb_private::ConstString &name,
                               uint32_t byte_size) {
  GoType *type = new GoStruct(kind, name, byte_size);
  (*m_types)[name].reset(type);
  return CompilerType(this, type);
}

void GoASTContext::AddFieldToStruct(
    const lldb_private::CompilerType &struct_type,
    const lldb_private::ConstString &name,
    const lldb_private::CompilerType &field_type, uint32_t byte_offset) {
  if (!struct_type)
    return;
  GoASTContext *ast =
      llvm::dyn_cast_or_null<GoASTContext>(struct_type.GetTypeSystem());
  if (!ast)
    return;
  GoType *type = static_cast<GoType *>(struct_type.GetOpaqueQualType());
  if (GoStruct *s = type->GetStruct())
    s->AddField(name, field_type, byte_offset);
}

void GoASTContext::CompleteStructType(
    const lldb_private::CompilerType &struct_type) {
  if (!struct_type)
    return;
  GoASTContext *ast =
      llvm::dyn_cast_or_null<GoASTContext>(struct_type.GetTypeSystem());
  if (!ast)
    return;
  GoType *type = static_cast<GoType *>(struct_type.GetOpaqueQualType());
  if (GoStruct *s = type->GetStruct())
    s->SetComplete();
}

CompilerType
GoASTContext::CreateFunctionType(const lldb_private::ConstString &name,
                                 CompilerType *params, size_t params_count,
                                 bool is_variadic) {
  GoType *type = new GoFunction(name, is_variadic);
  (*m_types)[name].reset(type);
  return CompilerType(this, type);
}

bool GoASTContext::IsGoString(const lldb_private::CompilerType &type) {
  if (!type.IsValid() ||
      !llvm::dyn_cast_or_null<GoASTContext>(type.GetTypeSystem()))
    return false;
  return GoType::KIND_STRING ==
         static_cast<GoType *>(type.GetOpaqueQualType())->GetGoKind();
}

bool GoASTContext::IsGoSlice(const lldb_private::CompilerType &type) {
  if (!type.IsValid() ||
      !llvm::dyn_cast_or_null<GoASTContext>(type.GetTypeSystem()))
    return false;
  return GoType::KIND_SLICE ==
         static_cast<GoType *>(type.GetOpaqueQualType())->GetGoKind();
}

bool GoASTContext::IsGoInterface(const lldb_private::CompilerType &type) {
  if (!type.IsValid() ||
      !llvm::dyn_cast_or_null<GoASTContext>(type.GetTypeSystem()))
    return false;
  return GoType::KIND_INTERFACE ==
         static_cast<GoType *>(type.GetOpaqueQualType())->GetGoKind();
}

bool GoASTContext::IsPointerKind(uint8_t kind) {
  return (kind & GoType::KIND_MASK) == GoType::KIND_PTR;
}

bool GoASTContext::IsDirectIface(uint8_t kind) {
  return (kind & GoType::KIND_DIRECT_IFACE) == GoType::KIND_DIRECT_IFACE;
}

DWARFASTParser *GoASTContext::GetDWARFParser() {
  if (!m_dwarf_ast_parser_ap)
    m_dwarf_ast_parser_ap.reset(new DWARFASTParserGo(*this));
  return m_dwarf_ast_parser_ap.get();
}

UserExpression *GoASTContextForExpr::GetUserExpression(
    llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language,
    Expression::ResultType desired_type,
    const EvaluateExpressionOptions &options) {
  TargetSP target = m_target_wp.lock();
  if (target)
    return new GoUserExpression(*target, expr, prefix, language, desired_type,
                                options);
  return nullptr;
}
