llvm-project/lldb/tools/lldb-mi/MICmdInvoker.cpp

307 lines
9.3 KiB
C++

//===-- MICmdInvoker.cpp ----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//++
// File: MICmdInvoker.cpp
//
// Overview: CMICmdInvoker implementation.
//
// Environment: Compilers: Visual C++ 12.
// gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
// Libraries: See MIReadmetxt.
//
// Copyright: None.
//--
// In-house headers:
#include "MICmdInvoker.h"
#include "MICmdBase.h"
#include "MICmdMgr.h"
#include "MICmnLog.h"
#include "MICmnStreamStdout.h"
//++ ------------------------------------------------------------------------------------
// Details: CMICmdInvoker constructor.
// Type: Method.
// Args: None.
// Return: None.
// Throws: None.
//--
CMICmdInvoker::CMICmdInvoker( void )
: m_rStreamOut( CMICmnStreamStdout::Instance() )
{
}
//++ ------------------------------------------------------------------------------------
// Details: CMICmdInvoker destructor.
// Type: Overridable.
// Args: None.
// Return: None.
// Throws: None.
//--
CMICmdInvoker::~CMICmdInvoker( void )
{
Shutdown();
}
//++ ------------------------------------------------------------------------------------
// Details: Initialize resources for *this Command Invoker.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMICmdInvoker::Initialize( void )
{
m_clientUsageRefCnt++;
if( m_bInitialized )
return MIstatus::success;
m_bInitialized = true;
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: Release resources for *this Stdin stream.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMICmdInvoker::Shutdown( void )
{
if( --m_clientUsageRefCnt > 0 )
return MIstatus::success;
if( !m_bInitialized )
return MIstatus::success;
CmdDeleteAll();
m_bInitialized = false;
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: Empty the map of invoked commands doing work. Command objects are deleted too.
// Type: Method.
// Args: None.
// Return: None.
// Throws: None.
//--
void CMICmdInvoker::CmdDeleteAll( void )
{
CMICmdMgr & rMgr = CMICmdMgr::Instance();
MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.begin();
while( it != m_mapCmdIdToCmd.end() )
{
const MIuint cmdId( (*it).first );
CMICmdBase * pCmd = (*it).second;
const CMIUtilString & rCmdName( pCmd->GetCmdData().strMiCmd );
rMgr.CmdDelete( pCmd->GetCmdData() );
// Next
++it;
}
m_mapCmdIdToCmd.clear();
}
//++ ------------------------------------------------------------------------------------
// Details: Remove from the map of invoked commands doing work a command that has finished
// its work. The command object is deleted too.
// Type: Method.
// Args: vId - (R) Command object's unique ID.
// vbYesDeleteCmd - (R) True = Delete command object, false = delete via the Command Manager.
// Return: None.
// Throws: None.
//--
bool CMICmdInvoker::CmdDelete( const MIuint vId, const bool vbYesDeleteCmd /*= false*/ )
{
CMICmdMgr & rMgr = CMICmdMgr::Instance();
MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.find( vId );
if( it != m_mapCmdIdToCmd.end() )
{
CMICmdBase * pCmd = (*it).second;
if( vbYesDeleteCmd )
{
// Via registered interest command manager callback *this object to delete the command
m_mapCmdIdToCmd.erase( it );
delete pCmd;
}
else
// Notify other interested object of this command's pending deletion
rMgr.CmdDelete( pCmd->GetCmdData() );
}
if( m_mapCmdIdToCmd.empty() )
rMgr.CmdUnregisterForDeleteNotification( *this );
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: Add to the map of invoked commands doing work a command that is about to
// start to do work.
// Type: Method.
// Args: vCmd - (R) Command object.
// Return: None.
// Throws: None.
//--
bool CMICmdInvoker::CmdAdd( const CMICmdBase & vCmd )
{
if( m_mapCmdIdToCmd.empty() )
{
CMICmdMgr & rMgr = CMICmdMgr::Instance();
rMgr.CmdRegisterForDeleteNotification( *this );
}
const MIuint & cmdId( vCmd.GetCmdData().id );
MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.find( cmdId );
if( it != m_mapCmdIdToCmd.end() )
return MIstatus::success;
MapPairCmdIdToCmd_t pr( cmdId, const_cast< CMICmdBase *>( &vCmd ));
m_mapCmdIdToCmd.insert( pr );
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: Having previously had the potential command validated and found valid now
// get the command executed.
// If the Functionalityity returns MIstatus::failure call GetErrorDescription().
// This function is used by the application's main thread.
// Type: Method.
// Args: vCmd - (RW) Command object.
// Return: MIstatus::success - Functionality succeeded.
// MIstatus::failure - Functionality failed.
// Throws: None.
//--
bool CMICmdInvoker::CmdExecute( CMICmdBase & vCmd )
{
bool bOk = CmdAdd( vCmd );
if( bOk && !vCmd.ParseArgs() )
{
// Report command execution failed
const SMICmdData cmdData( vCmd.GetCmdData() );
CmdStdout( cmdData );
CmdDelete( cmdData.id );
// Proceed to wait or execute next command
return MIstatus::success;
}
if( bOk && !vCmd.Execute() )
{
// Report command execution failed
const SMICmdData cmdData( vCmd.GetCmdData() );
CmdStdout( cmdData );
CmdDelete( cmdData.id );
// Proceed to wait or execute next command
return MIstatus::success;
}
bOk = CmdExecuteFinished( vCmd );
return bOk;
}
//++ ------------------------------------------------------------------------------------
// Details: Called when a command has finished its Execution() work either synchronously
// because the command executed was the type a non event type or asynchronoulsy
// via the command's callback (because of an SB Listener event). Needs to be called
// so that *this invoker call do some house keeping and then proceed to call
// the command's Acknowledge() function.
// Type: Method.
// Args: vCmd - (R) Command object.
// Return: MIstatus::success - Functionality succeeded.
// MIstatus::failure - Functionality failed.
// Throws: None.
//--
bool CMICmdInvoker::CmdExecuteFinished( CMICmdBase & vCmd )
{
// Command finished now get the command to gather it's information and form the MI
// Result record
if( !vCmd.Acknowledge() )
{
// Report command acknowledge functionality failed
const SMICmdData cmdData( vCmd.GetCmdData() );
CmdStdout( cmdData );
CmdDelete( cmdData.id );
// Proceed to wait or execute next command
return MIstatus::success;
}
// Retrieve the command's latest data/information. Needed for commands of the event type so have
// a record of commands pending finishing execution.
const CMIUtilString & rMIResultRecord( vCmd.GetMIResultRecord() );
SMICmdData cmdData( vCmd.GetCmdData() ); // Make a copy as the command will be deleted soon
cmdData.strMiCmdResultRecord = rMIResultRecord; // Precautionary copy as the command might forget to do this
if( vCmd.HasMIResultRecordExtra() )
{
cmdData.bHasResultRecordExtra = true;
const CMIUtilString & rMIExtra( vCmd.GetMIResultRecordExtra() );
cmdData.strMiCmdResultRecordExtra = rMIExtra; // Precautionary copy as the command might forget to do this
}
// Delete the command object as do not require anymore
bool bOk = CmdStdout( cmdData );
bOk = bOk && CmdDelete( vCmd.GetCmdData().id );
return bOk;
}
//++ ------------------------------------------------------------------------------------
// Details: Write to stdout and the Log file the command's MI formatted result.
// Type: vCmdData - (R) A command's information.
// Return: MIstatus::success - Functionality succeeded.
// MIstatus::failure - Functionality failed.
// Return: None.
// Throws: None.
//--
bool CMICmdInvoker::CmdStdout( const SMICmdData & vCmdData ) const
{
bool bOk = m_pLog->WriteLog( vCmdData.strMiCmdAll );
const bool bLock = bOk && m_rStreamOut.Lock();
bOk = bOk && bLock && m_rStreamOut.WriteMIResponse( vCmdData.strMiCmdResultRecord );
if( bOk && vCmdData.bHasResultRecordExtra )
{
bOk = bOk && m_rStreamOut.WriteMIResponse( vCmdData.strMiCmdResultRecordExtra );
}
bOk = bLock && m_rStreamOut.Unlock();
return bOk;
}
//++ ------------------------------------------------------------------------------------
// Details: Required by the CMICmdMgr::ICmdDeleteCallback. *this object is registered
// with the Command Manager to receive callbacks when a command is being deleted.
// An object, *this invoker, does not delete a command object itself but calls
// the Command Manager to delete a command object. This function is the Invoker's
// called.
// The Invoker owns the command objects and so can delete them but must do it
// via the manager so other objects can be notified of the deletion.
// Type: Method.
// Args: vCmd - (RW) Command.
// Return: None.
// Throws: None.
//--
void CMICmdInvoker::Delete( SMICmdData & vCmd )
{
CmdDelete( vCmd.id, true );
}