//===-- MICmdCmdData.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
//
//===----------------------------------------------------------------------===//

// Overview:    CMICmdCmdDataEvaluateExpression     implementation.
//              CMICmdCmdDataDisassemble            implementation.
//              CMICmdCmdDataReadMemoryBytes        implementation.
//              CMICmdCmdDataReadMemory             implementation.
//              CMICmdCmdDataListRegisterNames      implementation.
//              CMICmdCmdDataListRegisterValues     implementation.
//              CMICmdCmdDataListRegisterChanged    implementation.
//              CMICmdCmdDataWriteMemoryBytes       implementation.
//              CMICmdCmdDataWriteMemory            implementation.
//              CMICmdCmdDataInfoLine               implementation.

// Third Party Headers:
#include "lldb/API/SBInstruction.h"
#include "lldb/API/SBInstructionList.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBThread.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include <inttypes.h>
#include <string>

// In-house headers:
#include "MICmdArgValConsume.h"
#include "MICmdArgValListOfN.h"
#include "MICmdArgValNumber.h"
#include "MICmdArgValOptionLong.h"
#include "MICmdArgValOptionShort.h"
#include "MICmdArgValString.h"
#include "MICmdArgValThreadGrp.h"
#include "MICmdCmdData.h"
#include "MICmnLLDBDebugSessionInfo.h"
#include "MICmnLLDBDebugSessionInfoVarObj.h"
#include "MICmnLLDBDebugger.h"
#include "MICmnLLDBProxySBValue.h"
#include "MICmnLLDBUtilSBValue.h"
#include "MICmnMIResultRecord.h"
#include "MICmnMIValueConst.h"
#include "Platform.h"

namespace {
CMIUtilString IntToHexAddrStr(uint32_t number) {
  return CMIUtilString("0x" + llvm::Twine::utohexstr(number).str());
}
} // namespace

//++
// Details: CMICmdCmdDataEvaluateExpression constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataEvaluateExpression::CMICmdCmdDataEvaluateExpression()
    : m_bExpressionValid(true), m_bEvaluatedExpression(true), m_strValue("??"),
      m_bFoundInvalidChar(false), m_cExpressionInvalidChar(0x00),
      m_constStrArgExpr("expr") {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-evaluate-expression";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataEvaluateExpression::CreateSelf;
}

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

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataEvaluateExpression::ParseArgs() {
  m_setCmdArgs.Add(
      new CMICmdArgValString(m_constStrArgExpr, true, true, true, true));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataEvaluateExpression::Execute() {
  CMICMDBASE_GETOPTION(pArgExpr, String, m_constStrArgExpr);

  const CMIUtilString &rExpression(pArgExpr->GetValue());
  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::SBProcess sbProcess = rSessionInfo.GetProcess();
  lldb::SBThread thread = sbProcess.GetSelectedThread();
  m_bExpressionValid = (thread.GetNumFrames() > 0);
  if (!m_bExpressionValid)
    return MIstatus::success;

  lldb::SBFrame frame = thread.GetSelectedFrame();
  lldb::SBValue value = frame.EvaluateExpression(rExpression.c_str());
  m_Error = value.GetError();
  if (!value.IsValid() || m_Error.Fail())
    value = frame.FindVariable(rExpression.c_str());
  const CMICmnLLDBUtilSBValue utilValue(value, true);
  if (!utilValue.IsValid() || utilValue.IsValueUnknown()) {
    m_bEvaluatedExpression = false;
    return MIstatus::success;
  }
  if (!utilValue.HasName()) {
    if (HaveInvalidCharacterInExpression(rExpression,
                                         m_cExpressionInvalidChar)) {
      m_bFoundInvalidChar = true;
      return MIstatus::success;
    }

    m_strValue = rExpression;
    return MIstatus::success;
  }
  if (rExpression.IsQuoted()) {
    m_strValue = rExpression.Trim('\"');
    return MIstatus::success;
  }
  m_strValue = utilValue.GetValue(true).Escape().AddSlashes();
  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataEvaluateExpression::Acknowledge() {
  if (m_bExpressionValid) {
    if (m_bEvaluatedExpression) {
      if (m_bFoundInvalidChar) {
        const CMICmnMIValueConst miValueConst(CMIUtilString::Format(
            "Invalid character '%c' in expression", m_cExpressionInvalidChar));
        const CMICmnMIValueResult miValueResult("msg", miValueConst);
        const CMICmnMIResultRecord miRecordResult(
            m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error,
            miValueResult);
        m_miResultRecord = miRecordResult;
        return MIstatus::success;
      }

      const CMICmnMIValueConst miValueConst(m_strValue);
      const CMICmnMIValueResult miValueResult("value", miValueConst);
      const CMICmnMIResultRecord miRecordResult(
          m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done,
          miValueResult);
      m_miResultRecord = miRecordResult;
      return MIstatus::success;
    }
    CMIUtilString mi_error_msg = "Could not evaluate expression";
    if (const char *err_msg = m_Error.GetCString())
      mi_error_msg = err_msg;
    const CMICmnMIValueConst miValueConst(mi_error_msg.Escape(true));
    const CMICmnMIValueResult miValueResult("msg", miValueConst);
    const CMICmnMIResultRecord miRecordResult(
        m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error,
        miValueResult);
    m_miResultRecord = miRecordResult;
    return MIstatus::success;
  }

  const CMICmnMIValueConst miValueConst("Invalid expression");
  const CMICmnMIValueResult miValueResult("msg", miValueConst);
  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error,
      miValueResult);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataEvaluateExpression::CreateSelf() {
  return new CMICmdCmdDataEvaluateExpression();
}

//++
// Details: Examine the expression string to see if it contains invalid
// characters.
// Type:    Method.
// Args:    vrExpr          - (R) Expression string given to *this command.
//          vrwInvalidChar  - (W) True = Invalid character found, false =
//          nothing found.
// Return:  bool - True = Invalid character found, false = nothing found.
// Throws:  None.
//--
bool CMICmdCmdDataEvaluateExpression::HaveInvalidCharacterInExpression(
    const CMIUtilString &vrExpr, char &vrwInvalidChar) {
  static const std::string strInvalidCharacters(";#\\");
  const size_t nInvalidCharacterOffset =
      vrExpr.find_first_of(strInvalidCharacters);
  const bool bFoundInvalidCharInExpression =
      (nInvalidCharacterOffset != CMIUtilString::npos);
  vrwInvalidChar =
      bFoundInvalidCharInExpression ? vrExpr[nInvalidCharacterOffset] : 0x00;
  return bFoundInvalidCharInExpression;
}


//++
// Details: CMICmdCmdDataDisassemble constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataDisassemble::CMICmdCmdDataDisassemble()
    : m_constStrArgAddrStart("s"), m_constStrArgAddrEnd("e"),
      m_constStrArgMode("mode"), m_miValueList(true) {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-disassemble";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataDisassemble::CreateSelf;
}

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

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataDisassemble::ParseArgs() {
  m_setCmdArgs.Add(new CMICmdArgValOptionShort(
      m_constStrArgAddrStart, true, true,
      CMICmdArgValListBase::eArgValType_StringQuotedNumber, 1));
  m_setCmdArgs.Add(new CMICmdArgValOptionShort(
      m_constStrArgAddrEnd, true, true,
      CMICmdArgValListBase::eArgValType_StringQuotedNumber, 1));
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgMode, true, true));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataDisassemble::Execute() {
  CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread);
  CMICMDBASE_GETOPTION(pArgAddrStart, OptionShort, m_constStrArgAddrStart);
  CMICMDBASE_GETOPTION(pArgAddrEnd, OptionShort, m_constStrArgAddrEnd);
  CMICMDBASE_GETOPTION(pArgMode, Number, m_constStrArgMode);

  // Retrieve the --thread option's thread ID (only 1)
  MIuint64 nThreadId = UINT64_MAX;
  if (pArgThread->GetFound() &&
      !pArgThread->GetExpectedOption<CMICmdArgValNumber, MIuint64>(nThreadId)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgThread.c_str()));
    return MIstatus::failure;
  }
  CMIUtilString strAddrStart;
  if (!pArgAddrStart->GetExpectedOption<CMICmdArgValString, CMIUtilString>(
          strAddrStart)) {
    SetError(CMIUtilString::Format(
        MIRSRC(IDS_CMD_ERR_DISASM_ADDR_START_INVALID),
        m_cmdData.strMiCmd.c_str(), m_constStrArgAddrStart.c_str()));
    return MIstatus::failure;
  }
  MIint64 nAddrStart = 0;
  if (!strAddrStart.ExtractNumber(nAddrStart)) {
    SetError(CMIUtilString::Format(
        MIRSRC(IDS_CMD_ERR_DISASM_ADDR_START_INVALID),
        m_cmdData.strMiCmd.c_str(), m_constStrArgAddrStart.c_str()));
    return MIstatus::failure;
  }

  CMIUtilString strAddrEnd;
  if (!pArgAddrEnd->GetExpectedOption<CMICmdArgValString, CMIUtilString>(
          strAddrEnd)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_DISASM_ADDR_END_INVALID),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgAddrEnd.c_str()));
    return MIstatus::failure;
  }
  MIint64 nAddrEnd = 0;
  if (!strAddrEnd.ExtractNumber(nAddrEnd)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_DISASM_ADDR_END_INVALID),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgAddrEnd.c_str()));
    return MIstatus::failure;
  }
  const MIuint nDisasmMode = pArgMode->GetValue();

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::SBTarget sbTarget = rSessionInfo.GetTarget();
  lldb::addr_t lldbStartAddr = static_cast<lldb::addr_t>(nAddrStart);
  lldb::SBInstructionList instructions = sbTarget.ReadInstructions(
      lldb::SBAddress(lldbStartAddr, sbTarget), nAddrEnd - nAddrStart);
  const MIuint nInstructions = instructions.GetSize();
  // Calculate the offset of first instruction so that we can generate offset
  // starting at 0
  lldb::addr_t start_offset = 0;
  if (nInstructions > 0)
    start_offset =
        instructions.GetInstructionAtIndex(0).GetAddress().GetOffset();

  for (size_t i = 0; i < nInstructions; i++) {
    const char *pUnknown = "??";
    lldb::SBInstruction instrt = instructions.GetInstructionAtIndex(i);
    const char *pStrMnemonic = instrt.GetMnemonic(sbTarget);
    pStrMnemonic = (pStrMnemonic != nullptr) ? pStrMnemonic : pUnknown;
    const char *pStrComment = instrt.GetComment(sbTarget);
    CMIUtilString strComment;
    if (pStrComment != nullptr && *pStrComment != '\0')
      strComment = CMIUtilString::Format("; %s", pStrComment);
    lldb::SBAddress address = instrt.GetAddress();
    lldb::addr_t addr = address.GetLoadAddress(sbTarget);
    const char *pFnName = address.GetFunction().GetName();
    pFnName = (pFnName != nullptr) ? pFnName : pUnknown;
    lldb::addr_t addrOffSet = address.GetOffset() - start_offset;
    const char *pStrOperands = instrt.GetOperands(sbTarget);
    pStrOperands = (pStrOperands != nullptr) ? pStrOperands : pUnknown;
    const size_t instrtSize = instrt.GetByteSize();

    // MI "{address=\"0x%016" PRIx64
    // "\",func-name=\"%s\",offset=\"%lld\",inst=\"%s %s\"}"
    const CMICmnMIValueConst miValueConst(
        CMIUtilString::Format("0x%016" PRIx64, addr));
    const CMICmnMIValueResult miValueResult("address", miValueConst);
    CMICmnMIValueTuple miValueTuple(miValueResult);
    const CMICmnMIValueConst miValueConst2(pFnName);
    const CMICmnMIValueResult miValueResult2("func-name", miValueConst2);
    miValueTuple.Add(miValueResult2);
    const CMICmnMIValueConst miValueConst3(
        CMIUtilString::Format("%lld", addrOffSet));
    const CMICmnMIValueResult miValueResult3("offset", miValueConst3);
    miValueTuple.Add(miValueResult3);
    const CMICmnMIValueConst miValueConst4(
        CMIUtilString::Format("%d", instrtSize));
    const CMICmnMIValueResult miValueResult4("size", miValueConst4);
    miValueTuple.Add(miValueResult4);
    const CMICmnMIValueConst miValueConst5(
        CMIUtilString::Format("%s %s%s", pStrMnemonic, pStrOperands,
                              strComment.Escape(true).c_str()));
    const CMICmnMIValueResult miValueResult5("inst", miValueConst5);
    miValueTuple.Add(miValueResult5);

    if (nDisasmMode == 1) {
      lldb::SBLineEntry lineEntry = address.GetLineEntry();
      const MIuint nLine = lineEntry.GetLine();
      const char *pFileName = lineEntry.GetFileSpec().GetFilename();
      pFileName = (pFileName != nullptr) ? pFileName : pUnknown;
      // Get a full path to the file.
      char pathBuffer[PATH_MAX];
      lineEntry.GetFileSpec().GetPath(pathBuffer, PATH_MAX);

      // MI "src_and_asm_line={line=\"%u\",file=\"%s\",line_asm_insn=[ ],
      // fullname=\"%s\"}"
      const CMICmnMIValueConst miValueConst(
          CMIUtilString::Format("%u", nLine));
      const CMICmnMIValueResult miValueResult("line", miValueConst);
      CMICmnMIValueTuple miValueTuple2(miValueResult);
      const CMICmnMIValueConst miValueConst2(pFileName);
      const CMICmnMIValueResult miValueResult2("file", miValueConst2);
      miValueTuple2.Add(miValueResult2);
      const CMICmnMIValueList miValueList(miValueTuple);
      const CMICmnMIValueResult miValueResult3("line_asm_insn", miValueList);
      miValueTuple2.Add(miValueResult3);
      const CMICmnMIValueConst miValueConst5(pathBuffer);
      const CMICmnMIValueResult miValueResult5("fullname", miValueConst5);
      miValueTuple2.Add(miValueResult5);
      const CMICmnMIValueResult miValueResult4("src_and_asm_line",
                                               miValueTuple2);
      m_miValueList.Add(miValueResult4);
    } else {
      m_miValueList.Add(miValueTuple);
    }
  }

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataDisassemble::Acknowledge() {
  const CMICmnMIValueResult miValueResult("asm_insns", m_miValueList);
  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done,
      miValueResult);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataDisassemble::CreateSelf() {
  return new CMICmdCmdDataDisassemble();
}


//++
// Details: CMICmdCmdDataReadMemoryBytes constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataReadMemoryBytes::CMICmdCmdDataReadMemoryBytes()
    : m_constStrArgByteOffset("o"), m_constStrArgAddrExpr("address"),
      m_constStrArgNumBytes("count"), m_pBufferMemory(nullptr), m_nAddrStart(0),
      m_nAddrNumBytesToRead(0) {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-read-memory-bytes";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataReadMemoryBytes::CreateSelf;
}

//++
// Details: CMICmdCmdDataReadMemoryBytes destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataReadMemoryBytes::~CMICmdCmdDataReadMemoryBytes() {
  if (m_pBufferMemory != nullptr) {
    delete[] m_pBufferMemory;
    m_pBufferMemory = nullptr;
  }
}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataReadMemoryBytes::ParseArgs() {
  m_setCmdArgs.Add(
      new CMICmdArgValOptionShort(m_constStrArgByteOffset, false, true,
                                  CMICmdArgValListBase::eArgValType_Number, 1));
  m_setCmdArgs.Add(
      new CMICmdArgValString(m_constStrArgAddrExpr, true, true, true, true));
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumBytes, true, true));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Function succeeded.
//          MIstatus::failure - Function failed.
// Throws:  None.
//--
bool CMICmdCmdDataReadMemoryBytes::Execute() {
  CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread);
  CMICMDBASE_GETOPTION(pArgFrame, OptionLong, m_constStrArgFrame);
  CMICMDBASE_GETOPTION(pArgAddrOffset, OptionShort, m_constStrArgByteOffset);
  CMICMDBASE_GETOPTION(pArgAddrExpr, String, m_constStrArgAddrExpr);
  CMICMDBASE_GETOPTION(pArgNumBytes, Number, m_constStrArgNumBytes);

  // get the --thread option value
  MIuint64 nThreadId = UINT64_MAX;
  if (pArgThread->GetFound() &&
      !pArgThread->GetExpectedOption<CMICmdArgValNumber, MIuint64>(nThreadId)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgThread.c_str()));
    return MIstatus::failure;
  }

  // get the --frame option value
  MIuint64 nFrame = UINT64_MAX;
  if (pArgFrame->GetFound() &&
      !pArgFrame->GetExpectedOption<CMICmdArgValNumber, MIuint64>(nFrame)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgFrame.c_str()));
    return MIstatus::failure;
  }

  // get the -o option value
  MIuint64 nAddrOffset = 0;
  if (pArgAddrOffset->GetFound() &&
      !pArgAddrOffset->GetExpectedOption<CMICmdArgValNumber, MIuint64>(
          nAddrOffset)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgByteOffset.c_str()));
    return MIstatus::failure;
  }

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::SBProcess sbProcess = rSessionInfo.GetProcess();
  if (!sbProcess.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS),
                                   m_cmdData.strMiCmd.c_str()));
    return MIstatus::failure;
  }

  lldb::SBThread thread = (nThreadId != UINT64_MAX)
                              ? sbProcess.GetThreadByIndexID(nThreadId)
                              : sbProcess.GetSelectedThread();
  if (!thread.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID),
                                   m_cmdData.strMiCmd.c_str()));
    return MIstatus::failure;
  }

  lldb::SBFrame frame = (nFrame != UINT64_MAX) ? thread.GetFrameAtIndex(nFrame)
                                               : thread.GetSelectedFrame();
  if (!frame.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FRAME_INVALID),
                                   m_cmdData.strMiCmd.c_str()));
    return MIstatus::failure;
  }

  const CMIUtilString &rAddrExpr = pArgAddrExpr->GetValue();
  lldb::SBValue addrExprValue = frame.EvaluateExpression(rAddrExpr.c_str());
  lldb::SBError error = addrExprValue.GetError();
  if (error.Fail()) {
    SetError(error.GetCString());
    return MIstatus::failure;
  } else if (!addrExprValue.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_EXPR_INVALID),
                                   rAddrExpr.c_str()));
    return MIstatus::failure;
  }

  MIuint64 nAddrStart = 0;
  if (!CMICmnLLDBProxySBValue::GetValueAsUnsigned(addrExprValue, nAddrStart)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_EXPR_INVALID),
                                   rAddrExpr.c_str()));
    return MIstatus::failure;
  }

  nAddrStart += nAddrOffset;
  const MIuint64 nAddrNumBytes = pArgNumBytes->GetValue();

  m_pBufferMemory = new unsigned char[nAddrNumBytes];
  if (m_pBufferMemory == nullptr) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_MEMORY_ALLOC_FAILURE),
                                   m_cmdData.strMiCmd.c_str(), nAddrNumBytes));
    return MIstatus::failure;
  }

  const MIuint64 nReadBytes =
      sbProcess.ReadMemory(static_cast<lldb::addr_t>(nAddrStart),
                           (void *)m_pBufferMemory, nAddrNumBytes, error);
  if (nReadBytes != nAddrNumBytes) {
    SetError(CMIUtilString::Format(
        MIRSRC(IDS_CMD_ERR_LLDB_ERR_NOT_READ_WHOLE_BLK),
        m_cmdData.strMiCmd.c_str(), nAddrNumBytes, nAddrStart));
    return MIstatus::failure;
  }
  if (error.Fail()) {
    lldb::SBStream err;
    const bool bOk = error.GetDescription(err);
    MIunused(bOk);
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_READ_MEM_BYTES),
                                   m_cmdData.strMiCmd.c_str(), nAddrNumBytes,
                                   nAddrStart, err.GetData()));
    return MIstatus::failure;
  }

  m_nAddrStart = nAddrStart;
  m_nAddrNumBytesToRead = nAddrNumBytes;

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataReadMemoryBytes::Acknowledge() {
  // MI: memory=[{begin=\"0x%016" PRIx64 "\",offset=\"0x%016" PRIx64"
  // \",end=\"0x%016" PRIx64 "\",contents=\" \" }]"
  const CMICmnMIValueConst miValueConst(
      CMIUtilString::Format("0x%016" PRIx64, m_nAddrStart));
  const CMICmnMIValueResult miValueResult("begin", miValueConst);
  CMICmnMIValueTuple miValueTuple(miValueResult);
  const MIuint64 nAddrOffset = 0;
  const CMICmnMIValueConst miValueConst2(
      CMIUtilString::Format("0x%016" PRIx64, nAddrOffset));
  const CMICmnMIValueResult miValueResult2("offset", miValueConst2);
  miValueTuple.Add(miValueResult2);
  const CMICmnMIValueConst miValueConst3(CMIUtilString::Format(
      "0x%016" PRIx64, m_nAddrStart + m_nAddrNumBytesToRead));
  const CMICmnMIValueResult miValueResult3("end", miValueConst3);
  miValueTuple.Add(miValueResult3);

  // MI: contents=\" \"
  CMIUtilString strContent;
  strContent.reserve((m_nAddrNumBytesToRead << 1) + 1);
  for (MIuint64 i = 0; i < m_nAddrNumBytesToRead; i++) {
    strContent += CMIUtilString::Format("%02hhx", m_pBufferMemory[i]);
  }
  const CMICmnMIValueConst miValueConst4(strContent);
  const CMICmnMIValueResult miValueResult4("contents", miValueConst4);
  miValueTuple.Add(miValueResult4);
  const CMICmnMIValueList miValueList(miValueTuple);
  const CMICmnMIValueResult miValueResult5("memory", miValueList);

  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done,
      miValueResult5);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataReadMemoryBytes::CreateSelf() {
  return new CMICmdCmdDataReadMemoryBytes();
}


//++
// Details: CMICmdCmdDataReadMemory constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataReadMemory::CMICmdCmdDataReadMemory() {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-read-memory";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataReadMemory::CreateSelf;
}

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

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataReadMemory::Execute() {
  // Do nothing - command deprecated use "data-read-memory-bytes" command
  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataReadMemory::Acknowledge() {
  // Command CMICmdCmdSupportListFeatures sends "data-read-memory-bytes" which
  // causes this command not to be called
  const CMICmnMIValueConst miValueConst(
      MIRSRC(IDS_CMD_ERR_NOT_IMPLEMENTED_DEPRECATED));
  const CMICmnMIValueResult miValueResult("msg", miValueConst);
  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error,
      miValueResult);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataReadMemory::CreateSelf() {
  return new CMICmdCmdDataReadMemory();
}


//++
// Details: CMICmdCmdDataListRegisterNames constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataListRegisterNames::CMICmdCmdDataListRegisterNames()
    : m_constStrArgRegNo("regno"), m_miValueList(true) {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-list-register-names";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataListRegisterNames::CreateSelf;
}

//++
// Details: CMICmdCmdDataReadMemoryBytes destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataListRegisterNames::~CMICmdCmdDataListRegisterNames() {}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataListRegisterNames::ParseArgs() {
  m_setCmdArgs.Add(
      new CMICmdArgValListOfN(m_constStrArgRegNo, false, false,
                              CMICmdArgValListBase::eArgValType_Number));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataListRegisterNames::Execute() {
  CMICMDBASE_GETOPTION(pArgRegNo, ListOfN, m_constStrArgRegNo);

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::SBProcess sbProcess = rSessionInfo.GetProcess();
  if (!sbProcess.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS),
                                   m_cmdData.strMiCmd.c_str()));
    return MIstatus::failure;
  }

  const CMICmdArgValListBase::VecArgObjPtr_t &rVecRegNo(
      pArgRegNo->GetExpectedOptions());
  if (!rVecRegNo.empty()) {
    // List of required registers
    CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecRegNo.begin();
    while (it != rVecRegNo.end()) {
      const CMICmdArgValNumber *pRegNo = static_cast<CMICmdArgValNumber *>(*it);
      const MIuint nRegIndex = pRegNo->GetValue();
      lldb::SBValue regValue = GetRegister(nRegIndex);
      if (regValue.IsValid()) {
        const CMICmnMIValueConst miValueConst(
            CMICmnLLDBUtilSBValue(regValue).GetName());
        m_miValueList.Add(miValueConst);
      }

      // Next
      ++it;
    }
  } else {
    // List of all registers
    lldb::SBThread thread = sbProcess.GetSelectedThread();
    lldb::SBFrame frame = thread.GetSelectedFrame();
    lldb::SBValueList registers = frame.GetRegisters();
    const MIuint nRegisters = registers.GetSize();
    for (MIuint i = 0; i < nRegisters; i++) {
      lldb::SBValue value = registers.GetValueAtIndex(i);
      const MIuint nRegChildren = value.GetNumChildren();
      for (MIuint j = 0; j < nRegChildren; j++) {
        lldb::SBValue regValue = value.GetChildAtIndex(j);
        if (regValue.IsValid()) {
          const CMICmnMIValueConst miValueConst(
              CMICmnLLDBUtilSBValue(regValue).GetName());
          m_miValueList.Add(miValueConst);
        }
      }
    }
  }

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataListRegisterNames::Acknowledge() {
  const CMICmnMIValueResult miValueResult("register-names", m_miValueList);
  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done,
      miValueResult);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataListRegisterNames::CreateSelf() {
  return new CMICmdCmdDataListRegisterNames();
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Method.
// Args:    None.
// Return:  lldb::SBValue - LLDB SBValue object.
// Throws:  None.
//--
lldb::SBValue
CMICmdCmdDataListRegisterNames::GetRegister(const MIuint vRegisterIndex) const {
  lldb::SBThread thread =
      CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetSelectedThread();
  lldb::SBFrame frame = thread.GetSelectedFrame();
  lldb::SBValueList registers = frame.GetRegisters();
  const MIuint nRegisters = registers.GetSize();
  MIuint nRegisterIndex(vRegisterIndex);
  for (MIuint i = 0; i < nRegisters; i++) {
    lldb::SBValue value = registers.GetValueAtIndex(i);
    const MIuint nRegChildren = value.GetNumChildren();
    if (nRegisterIndex >= nRegChildren) {
      nRegisterIndex -= nRegChildren;
      continue;
    }

    lldb::SBValue value2 = value.GetChildAtIndex(nRegisterIndex);
    if (value2.IsValid()) {
      return value2;
    }
  }

  return lldb::SBValue();
}


//++
// Details: CMICmdCmdDataListRegisterValues constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataListRegisterValues::CMICmdCmdDataListRegisterValues()
    : m_constStrArgSkip("skip-unavailable"), m_constStrArgFormat("fmt"),
      m_constStrArgRegNo("regno"), m_miValueList(true) {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-list-register-values";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataListRegisterValues::CreateSelf;
}

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

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataListRegisterValues::ParseArgs() {
  m_setCmdArgs.Add(
      new CMICmdArgValOptionLong(m_constStrArgThread, false, false,
                                 CMICmdArgValListBase::eArgValType_Number, 1));
  m_setCmdArgs.Add(new CMICmdArgValOptionLong(m_constStrArgSkip, false, false));
  m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgFormat, true, true));
  m_setCmdArgs.Add(
      new CMICmdArgValListOfN(m_constStrArgRegNo, false, true,
                              CMICmdArgValListBase::eArgValType_Number));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataListRegisterValues::Execute() {
  CMICMDBASE_GETOPTION(pArgFormat, String, m_constStrArgFormat);
  CMICMDBASE_GETOPTION(pArgRegNo, ListOfN, m_constStrArgRegNo);

  const CMIUtilString &rStrFormat(pArgFormat->GetValue());
  if (rStrFormat.length() != 1) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_FORMAT_TYPE),
                                   m_cmdData.strMiCmd.c_str(),
                                   rStrFormat.c_str()));
    return MIstatus::failure;
  }
  const CMICmnLLDBDebugSessionInfoVarObj::varFormat_e eFormat =
      CMICmnLLDBDebugSessionInfoVarObj::GetVarFormatForChar(rStrFormat[0]);
  if (eFormat == CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_FORMAT_TYPE),
                                   m_cmdData.strMiCmd.c_str(),
                                   rStrFormat.c_str()));
    return MIstatus::failure;
  }

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::SBProcess sbProcess = rSessionInfo.GetProcess();
  if (!sbProcess.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS),
                                   m_cmdData.strMiCmd.c_str()));
    return MIstatus::failure;
  }

  const CMICmdArgValListBase::VecArgObjPtr_t &rVecRegNo(
      pArgRegNo->GetExpectedOptions());
  if (!rVecRegNo.empty()) {
    // List of required registers
    CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecRegNo.begin();
    while (it != rVecRegNo.end()) {
      const CMICmdArgValNumber *pRegNo = static_cast<CMICmdArgValNumber *>(*it);
      const MIuint nRegIndex = pRegNo->GetValue();
      lldb::SBValue regValue = GetRegister(nRegIndex);
      if (regValue.IsValid()) {
        AddToOutput(nRegIndex, regValue, eFormat);
      }

      // Next
      ++it;
    }
  } else {
    // No register numbers are provided. Output all registers.
    lldb::SBThread thread = sbProcess.GetSelectedThread();
    lldb::SBFrame frame = thread.GetSelectedFrame();
    lldb::SBValueList registers = frame.GetRegisters();
    const MIuint nRegisters = registers.GetSize();
    MIuint nRegIndex = 0;
    for (MIuint i = 0; i < nRegisters; i++) {
      lldb::SBValue value = registers.GetValueAtIndex(i);
      const MIuint nRegChildren = value.GetNumChildren();
      for (MIuint j = 0; j < nRegChildren; j++) {
        lldb::SBValue regValue = value.GetChildAtIndex(j);
        if (regValue.IsValid()) {
          AddToOutput(nRegIndex, regValue, eFormat);
        }

        // Next
        ++nRegIndex;
      }
    }
  }

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataListRegisterValues::Acknowledge() {
  const CMICmnMIValueResult miValueResult("register-values", m_miValueList);
  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done,
      miValueResult);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataListRegisterValues::CreateSelf() {
  return new CMICmdCmdDataListRegisterValues();
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Method.
// Args:    None.
// Return:  lldb::SBValue - LLDB SBValue object.
// Throws:  None.
//--
lldb::SBValue CMICmdCmdDataListRegisterValues::GetRegister(
    const MIuint vRegisterIndex) const {
  lldb::SBThread thread =
      CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetSelectedThread();
  lldb::SBFrame frame = thread.GetSelectedFrame();
  lldb::SBValueList registers = frame.GetRegisters();
  const MIuint nRegisters = registers.GetSize();
  MIuint nRegisterIndex(vRegisterIndex);
  for (MIuint i = 0; i < nRegisters; i++) {
    lldb::SBValue value = registers.GetValueAtIndex(i);
    const MIuint nRegChildren = value.GetNumChildren();
    if (nRegisterIndex >= nRegChildren) {
      nRegisterIndex -= nRegChildren;
      continue;
    }

    lldb::SBValue value2 = value.GetChildAtIndex(nRegisterIndex);
    if (value2.IsValid()) {
      return value2;
    }
  }

  return lldb::SBValue();
}

//++
// Details: Adds the register value to the output list.
// Type:    Method.
// Args:    Value of the register, its index and output format.
// Return:  None
// Throws:  None.
//--
void CMICmdCmdDataListRegisterValues::AddToOutput(
    const MIuint vnIndex, const lldb::SBValue &vrValue,
    CMICmnLLDBDebugSessionInfoVarObj::varFormat_e veVarFormat) {
  const CMICmnMIValueConst miValueConst(CMIUtilString::Format("%u", vnIndex));
  const CMICmnMIValueResult miValueResult("number", miValueConst);
  CMICmnMIValueTuple miValueTuple(miValueResult);
  const CMIUtilString strRegValue(
      CMICmnLLDBDebugSessionInfoVarObj::GetValueStringFormatted(vrValue,
                                                                veVarFormat));
  const CMICmnMIValueConst miValueConst2(strRegValue);
  const CMICmnMIValueResult miValueResult2("value", miValueConst2);
  miValueTuple.Add(miValueResult2);
  m_miValueList.Add(miValueTuple);
}


//++
// Details: CMICmdCmdDataListRegisterChanged constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataListRegisterChanged::CMICmdCmdDataListRegisterChanged() {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-list-changed-registers";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataListRegisterChanged::CreateSelf;
}

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

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataListRegisterChanged::Execute() {
  // Do nothing

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataListRegisterChanged::Acknowledge() {
  const CMICmnMIValueConst miValueConst(MIRSRC(IDS_WORD_NOT_IMPLEMENTED));
  const CMICmnMIValueResult miValueResult("msg", miValueConst);
  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error,
      miValueResult);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataListRegisterChanged::CreateSelf() {
  return new CMICmdCmdDataListRegisterChanged();
}


//++
// Details: CMICmdCmdDataWriteMemoryBytes constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataWriteMemoryBytes::CMICmdCmdDataWriteMemoryBytes()
    : m_constStrArgAddr("address"), m_constStrArgContents("contents"),
      m_constStrArgCount("count") {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-write-memory-bytes";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataWriteMemoryBytes::CreateSelf;
}

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

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataWriteMemoryBytes::ParseArgs() {
  m_setCmdArgs.Add(
      new CMICmdArgValString(m_constStrArgAddr, true, true, false, true));
  m_setCmdArgs.Add(
      new CMICmdArgValString(m_constStrArgContents, true, true, true, true));
  m_setCmdArgs.Add(
      new CMICmdArgValString(m_constStrArgCount, false, true, false, true));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataWriteMemoryBytes::Execute() {
  // Do nothing - not reproduceable (yet) in Eclipse
  // CMICMDBASE_GETOPTION( pArgOffset, OptionShort, m_constStrArgOffset );
  // CMICMDBASE_GETOPTION( pArgAddr, String, m_constStrArgAddr );
  // CMICMDBASE_GETOPTION( pArgNumber, String, m_constStrArgNumber );
  // CMICMDBASE_GETOPTION( pArgContents, String, m_constStrArgContents );
  //
  // Numbers extracts as string types as they could be hex numbers
  // '&' is not recognised and so has to be removed

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataWriteMemoryBytes::Acknowledge() {
  const CMICmnMIValueConst miValueConst(MIRSRC(IDS_WORD_NOT_IMPLEMENTED));
  const CMICmnMIValueResult miValueResult("msg", miValueConst);
  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error,
      miValueResult);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataWriteMemoryBytes::CreateSelf() {
  return new CMICmdCmdDataWriteMemoryBytes();
}


//++
// Details: CMICmdCmdDataWriteMemory constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataWriteMemory::CMICmdCmdDataWriteMemory()
    : m_constStrArgOffset("o"), m_constStrArgAddr("address"),
      m_constStrArgD("d"), m_constStrArgNumber("a number"),
      m_constStrArgContents("contents"), m_nAddr(0), m_nCount(0),
      m_pBufferMemory(nullptr) {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-write-memory";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataWriteMemory::CreateSelf;
}

//++
// Details: CMICmdCmdDataWriteMemory destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataWriteMemory::~CMICmdCmdDataWriteMemory() {
  if (m_pBufferMemory != nullptr) {
    delete[] m_pBufferMemory;
    m_pBufferMemory = nullptr;
  }
}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataWriteMemory::ParseArgs() {
  m_setCmdArgs.Add(
      new CMICmdArgValOptionShort(m_constStrArgOffset, false, true,
                                  CMICmdArgValListBase::eArgValType_Number, 1));
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgAddr, true, true));
  m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgD, true, true));
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, true, true));
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgContents, true, true));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataWriteMemory::Execute() {
  CMICMDBASE_GETOPTION(pArgOffset, OptionShort, m_constStrArgOffset);
  CMICMDBASE_GETOPTION(pArgAddr, Number, m_constStrArgAddr);
  CMICMDBASE_GETOPTION(pArgNumber, Number, m_constStrArgNumber);
  CMICMDBASE_GETOPTION(pArgContents, Number, m_constStrArgContents);

  MIuint nAddrOffset = 0;
  if (pArgOffset->GetFound() &&
      !pArgOffset->GetExpectedOption<CMICmdArgValNumber, MIuint>(nAddrOffset)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_INVALID),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgAddr.c_str()));
    return MIstatus::failure;
  }
  m_nAddr = pArgAddr->GetValue();
  m_nCount = pArgNumber->GetValue();
  const MIuint64 nValue = pArgContents->GetValue();

  m_pBufferMemory = new unsigned char[m_nCount];
  if (m_pBufferMemory == nullptr) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_MEMORY_ALLOC_FAILURE),
                                   m_cmdData.strMiCmd.c_str(), m_nCount));
    return MIstatus::failure;
  }
  *m_pBufferMemory = static_cast<char>(nValue);

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::SBProcess sbProcess = rSessionInfo.GetProcess();
  lldb::SBError error;
  lldb::addr_t addr = static_cast<lldb::addr_t>(m_nAddr + nAddrOffset);
  const size_t nBytesWritten = sbProcess.WriteMemory(
      addr, (const void *)m_pBufferMemory, (size_t)m_nCount, error);
  if (nBytesWritten != static_cast<size_t>(m_nCount)) {
    SetError(
        CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_NOT_WRITE_WHOLEBLK),
                              m_cmdData.strMiCmd.c_str(), m_nCount, addr));
    return MIstatus::failure;
  }
  if (error.Fail()) {
    lldb::SBStream err;
    const bool bOk = error.GetDescription(err);
    MIunused(bOk);
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_WRITE_MEM_BYTES),
                                   m_cmdData.strMiCmd.c_str(), m_nCount, addr,
                                   err.GetData()));
    return MIstatus::failure;
  }

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataWriteMemory::Acknowledge() {
  const CMICmnMIResultRecord miRecordResult(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done);
  m_miResultRecord = miRecordResult;

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataWriteMemory::CreateSelf() {
  return new CMICmdCmdDataWriteMemory();
}


//++
// Details: CMICmdCmdDataInfoLine constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataInfoLine::CMICmdCmdDataInfoLine()
    : m_constStrArgLocation("location"),
      m_resultRecord(m_cmdData.strMiCmdToken,
                     CMICmnMIResultRecord::eResultClass_Done) {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "data-info-line";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdDataInfoLine::CreateSelf;
}

//++
// Details: CMICmdCmdDataInfoLine destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdDataInfoLine::~CMICmdCmdDataInfoLine() = default;

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataInfoLine::ParseArgs() {
  m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgLocation, true, true));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataInfoLine::Execute() {
  CMICMDBASE_GETOPTION(pArgLocation, String, m_constStrArgLocation);

  lldb::SBLineEntry line;
  bool found_line = false;
  const CMIUtilString &strLocation(pArgLocation->GetValue());
  lldb::SBTarget target = CMICmnLLDBDebugSessionInfo::Instance().GetTarget();

  if (strLocation.at(0) == '*') {
    // Parse argument:
    // *0x12345
    // ^^^^^^^^^ -- address
    lldb::addr_t address = 0x0;
    if (llvm::StringRef(strLocation.substr(1)).getAsInteger(0, address)) {
      SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_SOME_ERROR),
                                     m_cmdData.strMiCmd.c_str(),
                                     "Failed to parse address."));
      return MIstatus::failure;
    }
    line = target.ResolveFileAddress(address).GetLineEntry();
    // Check that found line is valid.
    if (line.GetLine())
      found_line = true;
  } else {
    const size_t nLineStartPos = strLocation.rfind(':');
    if ((nLineStartPos == std::string::npos) || (nLineStartPos == 0) ||
        (nLineStartPos == strLocation.length() - 1)) {
      SetError(CMIUtilString::Format(
          MIRSRC(IDS_CMD_ERR_INVALID_LOCATION_FORMAT),
          m_cmdData.strMiCmd.c_str(), strLocation.c_str()));
      return MIstatus::failure;
    }
    // Parse argument:
    // hello.cpp:5
    // ^^^^^^^^^ -- file
    //           ^ -- line
    const CMIUtilString &strFile(strLocation.substr(0, nLineStartPos));
    uint32_t numLine = 0;
    llvm::StringRef(strLocation.substr(nLineStartPos + 1))
        .getAsInteger(0, numLine);
    lldb::SBSymbolContextList sc_cu_list =
        target.FindCompileUnits(lldb::SBFileSpec(strFile.c_str(), false));
    for (uint32_t i = 0, e = sc_cu_list.GetSize(); i < e; ++i) {
      const lldb::SBCompileUnit &cu =
        sc_cu_list.GetContextAtIndex(i).GetCompileUnit();
      // Break if we have already found requested line.
      if (found_line)
        break;
      for (uint32_t j = 0, e = cu.GetNumLineEntries(); j < e; ++j) {
        const lldb::SBLineEntry &curLine = cu.GetLineEntryAtIndex(j);
        if (curLine.GetLine() == numLine) {
          line = curLine;
          found_line = true;
          break;
        }
      }
    }
  }
  if (!found_line) {
    SetError(CMIUtilString::Format(
        MIRSRC(IDS_CMD_ERR_SOME_ERROR), m_cmdData.strMiCmd.c_str(),
        "The LineEntry is absent or has an unknown format."));
    return MIstatus::failure;
  }
  // Start address.
  m_resultRecord.Add(CMICmnMIValueResult(
      "start", CMICmnMIValueConst(IntToHexAddrStr(
               line.GetStartAddress().GetFileAddress()))));
  // End address.
  m_resultRecord.Add(CMICmnMIValueResult(
      "end", CMICmnMIValueConst(IntToHexAddrStr(
             line.GetEndAddress().GetFileAddress()))));
  // File.
  std::unique_ptr<char[]> upPath(new char[PATH_MAX]);
  line.GetFileSpec().GetPath(upPath.get(), PATH_MAX);
  m_resultRecord.Add(CMICmnMIValueResult(
      "file", CMICmnMIValueConst(CMIUtilString(upPath.get()))));
  // Line.
  m_resultRecord.Add(CMICmnMIValueResult(
      "line", CMICmnMIValueConst(std::to_string(line.GetLine()))));
  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdDataInfoLine::Acknowledge() {
  m_miResultRecord = m_resultRecord;
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdDataInfoLine::CreateSelf() {
  return new CMICmdCmdDataInfoLine();
}
