//===-- MICmnLLDBUtilSBValue.cpp --------------------------------*- 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
//
//===----------------------------------------------------------------------===//

// Third party headers:
#include "lldb/API/SBTypeSummary.h"
#include <cinttypes>

// In-house headers:
#include "MICmnLLDBDebugSessionInfo.h"
#include "MICmnLLDBUtilSBValue.h"
#include "MICmnMIValueConst.h"
#include "MICmnMIValueTuple.h"
#include "MIUtilString.h"

static const char *kUnknownValue = "??";
static const char *kUnresolvedCompositeValue = "{...}";

//++
// Details: CMICmnLLDBUtilSBValue constructor.
// Type:    Method.
// Args:    vrValue             - (R) The LLDB value object.
//          vbHandleCharType    - (R) True = Yes return text molding to char
//          type,
//                                    False = just return data.
// Return:  None.
// Throws:  None.
//--
CMICmnLLDBUtilSBValue::CMICmnLLDBUtilSBValue(
    const lldb::SBValue &vrValue, const bool vbHandleCharType /* = false */,
    const bool vbHandleArrayType /* = true */)
    : m_rValue(const_cast<lldb::SBValue &>(vrValue)),
      m_bHandleCharType(vbHandleCharType),
      m_bHandleArrayType(vbHandleArrayType) {
  m_bValidSBValue = m_rValue.IsValid();
}

//++
// Details: CMICmnLLDBUtilSBValue destructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmnLLDBUtilSBValue::~CMICmnLLDBUtilSBValue() {}

//++
// Details: Retrieve from the LLDB SB Value object the name of the variable. If
// the name
//          is invalid (or the SBValue object invalid) then "??" is returned.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString   - Name of the variable or "??" for unknown.
// Throws:  None.
//--
CMIUtilString CMICmnLLDBUtilSBValue::GetName() const {
  const char *pName = m_bValidSBValue ? m_rValue.GetName() : nullptr;
  const CMIUtilString text((pName != nullptr) ? pName : CMIUtilString());

  return text;
}

//++
// Details: Retrieve from the LLDB SB Value object the value of the variable
// described in
//          text. If the value is invalid (or the SBValue object invalid) then
//          "??" is
//          returned.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString   - Text description of the variable's value or "??".
// Throws:  None.
//--
CMIUtilString CMICmnLLDBUtilSBValue::GetValue(
    const bool vbExpandAggregates /* = false */) const {
  if (!m_bValidSBValue)
    return kUnknownValue;

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  bool bPrintExpandAggregates = false;
  bPrintExpandAggregates = rSessionInfo.SharedDataRetrieve<bool>(
                               rSessionInfo.m_constStrPrintExpandAggregates,
                               bPrintExpandAggregates) &&
                           bPrintExpandAggregates;

  const bool bHandleArrayTypeAsSimple =
      m_bHandleArrayType && !vbExpandAggregates && !bPrintExpandAggregates;
  CMIUtilString value;
  const bool bIsSimpleValue = GetSimpleValue(bHandleArrayTypeAsSimple, value);
  if (bIsSimpleValue)
    return value;

  if (!vbExpandAggregates && !bPrintExpandAggregates)
    return kUnresolvedCompositeValue;

  bool bPrintAggregateFieldNames = false;
  bPrintAggregateFieldNames =
      !rSessionInfo.SharedDataRetrieve<bool>(
          rSessionInfo.m_constStrPrintAggregateFieldNames,
          bPrintAggregateFieldNames) ||
      bPrintAggregateFieldNames;

  CMICmnMIValueTuple miValueTuple;
  const bool bOk = GetCompositeValue(bPrintAggregateFieldNames, miValueTuple);
  if (!bOk)
    return kUnknownValue;

  value = miValueTuple.GetString();
  return value;
}

//++
// Details: Retrieve from the LLDB SB Value object the value of the variable
// described in
//          text if it has a simple format (not composite).
// Type:    Method.
// Args:    vwrValue          - (W) The SBValue in a string format.
// Return:  MIstatus::success - Function succeeded.
//          MIstatus::failure - Function failed.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::GetSimpleValue(const bool vbHandleArrayType,
                                           CMIUtilString &vwrValue) const {
  const MIuint nChildren = m_rValue.GetNumChildren();
  if (nChildren == 0) {
    vwrValue = GetValueSummary(!m_bHandleCharType && IsCharType(), kUnknownValue);
    return MIstatus::success;
  } else if (IsPointerType()) {
    vwrValue =
        GetValueSummary(!m_bHandleCharType && IsPointeeCharType(), kUnknownValue);
    return MIstatus::success;
  } else if (IsArrayType()) {
    CMICmnLLDBDebugSessionInfo &rSessionInfo(
        CMICmnLLDBDebugSessionInfo::Instance());
    bool bPrintCharArrayAsString = false;
    bPrintCharArrayAsString = rSessionInfo.SharedDataRetrieve<bool>(
                                  rSessionInfo.m_constStrPrintCharArrayAsString,
                                  bPrintCharArrayAsString) &&
                              bPrintCharArrayAsString;
    if (bPrintCharArrayAsString && m_bHandleCharType &&
        IsFirstChildCharType()) {
      vwrValue = GetValueSummary(false);
      return MIstatus::success;
    } else if (vbHandleArrayType) {
      vwrValue = CMIUtilString::Format("[%u]", nChildren);
      return MIstatus::success;
    }
  } else {
    // Treat composite value which has registered summary
    // (for example with AddCXXSummary) as simple value
    vwrValue = GetValueSummary(false);
    if (!vwrValue.empty())
      return MIstatus::success;
  }

  // Composite variable type i.e. struct
  return MIstatus::failure;
}

bool CMICmnLLDBUtilSBValue::GetCompositeValue(
    const bool vbPrintFieldNames, CMICmnMIValueTuple &vwrMiValueTuple,
    const MIuint vnDepth /* = 1 */) const {
  const MIuint nMaxDepth = 10;
  const MIuint nChildren = m_rValue.GetNumChildren();
  for (MIuint i = 0; i < nChildren; ++i) {
    const lldb::SBValue member = m_rValue.GetChildAtIndex(i);
    const CMICmnLLDBUtilSBValue utilMember(member, m_bHandleCharType,
                                           m_bHandleArrayType);
    const bool bHandleArrayTypeAsSimple = false;
    CMIUtilString value;
    const bool bIsSimpleValue =
        utilMember.GetSimpleValue(bHandleArrayTypeAsSimple, value);
    if (bIsSimpleValue) {
      // OK. Value is simple (not composite) and was successfully got
    } else if (vnDepth < nMaxDepth) {
      // Need to get value from composite type
      CMICmnMIValueTuple miValueTuple;
      const bool bOk = utilMember.GetCompositeValue(vbPrintFieldNames,
                                                    miValueTuple, vnDepth + 1);
      if (!bOk)
        // Can't obtain composite type
        value = kUnknownValue;
      else
        // OK. Value is composite and was successfully got
        value = miValueTuple.GetString();
    } else {
      // Need to get value from composite type, but vnMaxDepth is reached
      value = kUnresolvedCompositeValue;
    }
    const bool bNoQuotes = true;
    const CMICmnMIValueConst miValueConst(value, bNoQuotes);
    if (vbPrintFieldNames) {
      const bool bUseSpacing = true;
      const CMICmnMIValueResult miValueResult(utilMember.GetName(),
                                              miValueConst, bUseSpacing);
      vwrMiValueTuple.Add(miValueResult, bUseSpacing);
    } else {
      const bool bUseSpacing = false;
      vwrMiValueTuple.Add(miValueConst, bUseSpacing);
    }
  }

  return MIstatus::success;
}

// Returns value or value + summary, depending on valueOnly parameter value.
// If result is an empty string returns failVal.
CMIUtilString
CMICmnLLDBUtilSBValue::GetValueSummary(bool valueOnly,
                                       const CMIUtilString &failVal) const {
  if (!m_rValue.IsValid())
    return failVal;

  CMIUtilString value, valSummary;
  const char *c_value = m_rValue.GetValue();
  if (valueOnly)
    return c_value == nullptr ? failVal : c_value;

  const char *c_summary = m_rValue.GetSummary();
  if (c_value)
    value = c_value;
  else if (c_summary == nullptr)
    return failVal;

  if (c_summary && c_summary[0]) {
    valSummary = c_summary;
    lldb::SBTypeSummary summary = m_rValue.GetTypeSummary();
    if (summary.IsValid() && summary.DoesPrintValue(m_rValue) &&
        !value.empty()) {
      valSummary.insert(0, value + " ");
    }
    return valSummary;
  }
  // no summary - return just value
  return value;
}

//++
// Details: Check that basic type is a char type. Char type can be signed or
// unsigned.
// Type:    Static.
// Args:    eType   - type to check
// Return:  bool    - True = Yes is a char type, false = some other type.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsCharBasicType(lldb::BasicType eType) {
  switch (eType) {
  case lldb::eBasicTypeChar:
  case lldb::eBasicTypeSignedChar:
  case lldb::eBasicTypeUnsignedChar:
  case lldb::eBasicTypeChar16:
  case lldb::eBasicTypeChar32:
    return true;
  default:
    return false;
  }
}

//++
// Details: Retrieve the flag stating whether this value object is a char type
// or some
//          other type. Char type can be signed or unsigned.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = Yes is a char type, false = some other type.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsCharType() const {
  const lldb::BasicType eType = m_rValue.GetType().GetBasicType();
  return IsCharBasicType(eType);
}

//++
// Details: Retrieve the flag stating whether first child value object of *this
// object is
//          a char type or some other type. Returns false if there are not
//          children. Char
//          type can be signed or unsigned.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = Yes is a char type, false = some other type.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsFirstChildCharType() const {
  const MIuint nChildren = m_rValue.GetNumChildren();

  // Is it a basic type
  if (nChildren == 0)
    return false;

  const lldb::SBValue member = m_rValue.GetChildAtIndex(0);
  const CMICmnLLDBUtilSBValue utilValue(member);
  return utilValue.IsCharType();
}

//++
// Details: Retrieve the flag stating whether pointee object of *this object is
//          a char type or some other type. Returns false if there are not
//          children. Char
//          type can be signed or unsigned.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = Yes is a char type, false = some other type.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsPointeeCharType() const {
  const MIuint nChildren = m_rValue.GetNumChildren();

  // Is it a basic type
  if (nChildren == 0)
    return false;

  const lldb::BasicType eType =
      m_rValue.GetType().GetPointeeType().GetBasicType();
  return IsCharBasicType(eType);
}

//++
// Details: Retrieve the flag stating whether this value object is a integer
// type or some
//          other type. Char type can be signed or unsigned and short or
//          long/very long.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = Yes is a integer type, false = some other type.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsIntegerType() const {
  const lldb::BasicType eType = m_rValue.GetType().GetBasicType();
  return ((eType == lldb::eBasicTypeShort) ||
          (eType == lldb::eBasicTypeUnsignedShort) ||
          (eType == lldb::eBasicTypeInt) ||
          (eType == lldb::eBasicTypeUnsignedInt) ||
          (eType == lldb::eBasicTypeLong) ||
          (eType == lldb::eBasicTypeUnsignedLong) ||
          (eType == lldb::eBasicTypeLongLong) ||
          (eType == lldb::eBasicTypeUnsignedLongLong) ||
          (eType == lldb::eBasicTypeInt128) ||
          (eType == lldb::eBasicTypeUnsignedInt128));
}

//++
// Details: Retrieve the flag stating whether this value object is a pointer
// type or some
//          other type.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = Yes is a pointer type, false = some other type.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsPointerType() const {
  return m_rValue.GetType().IsPointerType();
}

//++
// Details: Retrieve the flag stating whether this value object is an array type
// or some
//          other type.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = Yes is an array type, false = some other type.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsArrayType() const {
  return m_rValue.GetType().IsArrayType();
}

//++
// Details: Retrieve the C string data of value object by read the memory where
// the
//          variable is held.
// Type:    Method.
// Args:    vrValue         - (R) LLDB SBValue variable object.
// Return:  CMIUtilString   - Text description of the variable's value.
// Throws:  None.
//--
template <typename charT>
CMIUtilString
CMICmnLLDBUtilSBValue::ReadCStringFromHostMemory(lldb::SBValue &vrValue,
                                                 const MIuint vnMaxLen) const {
  std::string result;
  lldb::addr_t addr = vrValue.GetLoadAddress(),
               end_addr = addr + vnMaxLen * sizeof(charT);
  lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetProcess();
  lldb::SBError error;
  while (addr < end_addr) {
    charT ch;
    const MIuint64 nReadBytes =
        process.ReadMemory(addr, &ch, sizeof(ch), error);
    if (error.Fail() || nReadBytes != sizeof(ch))
      return kUnknownValue;
    else if (ch == 0)
      break;
    result.append(
        CMIUtilString::ConvertToPrintableASCII(ch, true /* bEscapeQuotes */));
    addr += sizeof(ch);
  }

  return result;
}

//++
// Details: Retrieve the state of the value object's name.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = yes name is indeterminate, false = name is valid.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsNameUnknown() const {
  const CMIUtilString name(GetName());
  return (name == kUnknownValue);
}

//++
// Details: Retrieve the state of the value object's value data.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = yes value is indeterminate, false = value valid.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsValueUnknown() const {
  const CMIUtilString value(GetValue());
  return (value == kUnknownValue);
}

//++
// Details: Retrieve the value object's type name if valid.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString   - The type name or "??".
// Throws:  None.
//--
CMIUtilString CMICmnLLDBUtilSBValue::GetTypeName() const {
  const char *pName = m_bValidSBValue ? m_rValue.GetTypeName() : nullptr;
  const CMIUtilString text((pName != nullptr) ? pName : kUnknownValue);

  return text;
}

//++
// Details: Retrieve the value object's display type name if valid.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString   - The type name or "??".
// Throws:  None.
//--
CMIUtilString CMICmnLLDBUtilSBValue::GetTypeNameDisplay() const {
  const char *pName = m_bValidSBValue ? m_rValue.GetDisplayTypeName() : nullptr;
  const CMIUtilString text((pName != nullptr) ? pName : kUnknownValue);

  return text;
}

//++
// Details: Retrieve whether the value object's is valid or not.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = valid, false = not valid.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsValid() const { return m_bValidSBValue; }

//++
// Details: Retrieve the value object' has a name. A value object can be valid
// but still
//          have no name which suggest it is not a variable.
// Type:    Method.
// Args:    None.
// Return:  bool    - True = valid, false = not valid.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::HasName() const {
  bool bHasAName = false;

  const char *pName = m_bValidSBValue ? m_rValue.GetDisplayTypeName() : nullptr;
  if (pName != nullptr) {
    bHasAName = (CMIUtilString(pName).length() > 0);
  }

  return bHasAName;
}

//++
// Details: Determine if the value object' represents a LLDB variable i.e. "$0".
// Type:    Method.
// Args:    None.
// Return:  bool    - True = Yes LLDB variable, false = no.
// Throws:  None.
//--
bool CMICmnLLDBUtilSBValue::IsLLDBVariable() const {
  return (GetName().at(0) == '$');
}
