Click here to Skip to main content
15,897,187 members
Articles / Desktop Programming / ATL

Function Call Tracing in JScript

Rate me:
Please Sign up or sign in to vote.
4.58/5 (18 votes)
3 Jul 2007CPOL15 min read 54K   6.4K   49  
Comprehensive JScript function call tracing without code modification.
/***************************************************************************
*
*	File            : ScriptDebugEngine.cpp
*	Author          : K. Skilling
*	Description     : This object wraps the JScript engine and intercepts the
*					  ParseScriptText method so that it can inject code for 
*					  function entry/exit points.  
*
*					  If the application creating the instance of this object 
*					  is not registered under the JScriptDebug registry key 
*					  a real instance of the JScript engine is returned and 
*					  this object instance is deleted.
*
*	Change History
*	---------------
*	KS	28/02/2006	CREATED
*****************************************************************************/
#include "stdafx.h"
#include "JScriptDebug.h"
#include "ScriptDebugEngine.h"
#include "JSDUtils.h"

/////////////////////////////////////////////////////////////////////////////

HRESULT CScriptDebugEngine::FinalConstruct()
{

#ifdef _SHOWINSTANCES
	sInfo.Format(L"JSD: NEW INSTANCE [%#x] - '%s' [PID = %d]",this,(LPCWSTR)oModule.Name(),GetCurrentProcessId());
	OutputDebugString(sInfo);
	if (g_bBlankLines)
		OutputDebugString(L"JSD:");
#endif

	return S_OK;
}

CScriptDebugEngine::CScriptDebugEngine()
{
}

CScriptDebugEngine::~CScriptDebugEngine()
{
	ATLTRACE(L"~CScriptDebugEngine - %#x\n",this);
}

/////////////////////////////////////////////////////////////////////////////
// IActiveScript


STDMETHODIMP CScriptDebugEngine::SetScriptSite( 
    IActiveScriptSite  *pass)
{
	try
	{
		HRESULT hr = ::CoCreateInstance(CLSID_ScriptSiteProxy,NULL,CLSCTX_INPROC,IID_IScriptSiteProxy,(VOID**)
			&m_pProxy);

		if (SUCCEEDED(hr))
		{
			m_pProxy->Init(pass);
			CComQIPtr<IActiveScriptSite> pSite = m_pProxy;
			hr = m_pEngine->SetScriptSite(pSite);

		 	// Add debugging object using our unique (hopefully) name
			DWORD dwFlags = SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE;
			hr = m_pEngine->AddNamedItem(szJSDID,dwFlags);

			return hr;
		}

		return m_pEngine->SetScriptSite(pass);
	}
	catch(...)
	{
		ATLTRACE(L"Unexpected exception in ScriptDebugEngine::SetScriptSite\n");
		return E_FAIL;
	}
}

/////////////////////////////////////////////////////////////////////////////
//IActiveScriptParse

STDMETHODIMP CScriptDebugEngine::AddScriptlet( 
    LPCOLESTR pstrDefaultName,
    LPCOLESTR pstrCode,
    LPCOLESTR pstrItemName,
    LPCOLESTR pstrSubItemName,
    LPCOLESTR pstrEventName,
    LPCOLESTR pstrDelimiter,
    DWORD dwSourceContextCookie,
    ULONG ulStartingLineNumber,
    DWORD dwFlags,
     BSTR *pbstrName,
     EXCEPINFO *pexcepinfo)
{
	// NOT instrumented - this is usually just a simple function call as an 
	// event handler on an html element.
	return m_pEngineParse->AddScriptlet(
										pstrDefaultName,
										pstrCode,
										pstrItemName,
										pstrSubItemName,
										pstrEventName,
										pstrDelimiter,
										dwSourceContextCookie,
										ulStartingLineNumber,
										dwFlags,
										pbstrName,
										pexcepinfo);
}

#define _JSD_NO_TRACE_  0x1
#define _JSD_FILE_		0x2

DWORD readDirectives(LPCWSTR szCode,DWORD dwSourceContextCookie)
{
	DWORD dwDirectives = 0;

	// Copy first section of code and process this only
	const nBUFSIZE = 256-1;
	WCHAR szCodeBuffer[nBUFSIZE];
	PWCHAR p = (LPWSTR)szCode;
	int iCounter = 0;
	bool bAllBlanks = true;
	while (p && *p && iCounter < nBUFSIZE)
		szCodeBuffer[iCounter++] = *p++;

	szCodeBuffer[iCounter] = 0;

	IRegExp2Ptr	pRegExp;
	HRESULT hr = pRegExp.CreateInstance(CLSID_RegExp);
	if (SUCCEEDED(hr))
	{
		////////////////////////////////////////////////////////////////////////////
		// _JSD_FILE_ 

		_bstr_t bstrPattern(L"/\\*\\s*_JSD_FILE_\\s*([a-z|A-Z|.|0-9]+)");			// Has to be a proper BSTR 
		pRegExp->put_Pattern(bstrPattern);

		IDispatchPtr pDispatch;
		_bstr_t bstrCode = szCodeBuffer;											// Has to be a proper BSTR 
		hr = pRegExp->raw_Execute(bstrCode,&pDispatch);
		if (FAILED(hr)) throw hr;

		CComQIPtr<IMatchCollection> pCollection = pDispatch;
		long lCount = 0;
		hr = pCollection->get_Count(&lCount);
		if (FAILED(hr)) throw hr;

		if (lCount == 1)
			dwDirectives |= _JSD_FILE_;

		////////////////////////////////////////////////////////////////////////////
		// _JSD_NO_TRACE

		bstrPattern = L"/\\*\\s*_JSD_NO_TRACE_";			// Has to be a proper BSTR 
		pRegExp->put_Pattern(bstrPattern);

		hr = pRegExp->raw_Execute(bstrCode,&pDispatch);
		if (FAILED(hr)) throw hr;

		pCollection = pDispatch;
		hr = pCollection->get_Count(&lCount);
		if (FAILED(hr)) throw hr;

		if (lCount == 1)
			dwDirectives |= _JSD_NO_TRACE_;
	}
	
	return dwDirectives;
}

STDMETHODIMP CScriptDebugEngine::ParseScriptText( 
    LPCOLESTR pstrCode,
    LPCOLESTR pstrItemName,
    IUnknown __RPC_FAR *punkContext,
    LPCOLESTR pstrDelimiter,
    DWORD dwSourceContextCookie,
    ULONG ulStartingLineNumber,
    DWORD dwFlags,
     VARIANT *pvarResult,
     EXCEPINFO *pexcepinfo) 
{
	LPCWSTR szLOCATION = L"ParseScriptText";
	HRESULT hRes = E_FAIL;

	::EnterCriticalSection(&g_csInstrument);

	try
	{
		BSTR bstrCode = NULL;
		LPCWSTR pCode = NULL;

		DWORD dwDirectives = readDirectives(pstrCode,dwSourceContextCookie);
		DWORD dwNewFlags = dwFlags;

		if (_JSD_NO_TRACE_ & dwDirectives || false == g_bInstrument)
		{
			pCode = pstrCode;
			hRes = S_OK;
		}
		else
		{
			// Because the code is instrumented we turn off the host management 
			dwNewFlags = dwFlags & ~SCRIPTTEXT_HOSTMANAGESSOURCE;

			hRes = _instrumentCode(pstrCode,&bstrCode);
			pCode = bstrCode;
		}

		// Save script (if required)
		DumpScripts(pCode);

		if (SUCCEEDED(hRes))
		{
			hRes = m_pEngineParse->ParseScriptText(
												pCode,
												pstrItemName,
												punkContext,
												pstrDelimiter,
												dwSourceContextCookie,
												ulStartingLineNumber,
												dwNewFlags,
												pvarResult,
												pexcepinfo);
		}

		if (bstrCode)
			::SysFreeString(bstrCode);

	}
	catch(HRESULT hr)
	{
		hRes = ReportError(szLOCATION,hr);
	}
	catch(...)
	{
		hRes = ReportError(szLOCATION,E_UNEXPECTED);
	}

	// On errror just parse the original script
	if (FAILED(hRes))
		hRes = m_pEngineParse->ParseScriptText(pstrCode,
										pstrItemName,
										punkContext,
										pstrDelimiter,
										dwSourceContextCookie,
										ulStartingLineNumber,
										dwFlags,
										pvarResult,
										pexcepinfo);

	::LeaveCriticalSection(&g_csInstrument);

	return g_bRaiseErrors ? hRes : S_OK;
}

/////////////////////////////////////////////////////////////////////////
// Helpers

void CScriptDebugEngine::DumpScripts(LPCWSTR pCode)
{
	if (g_szScriptDump[0] && pCode && wcslen(pCode))
	{
		CMemoryMappedFile mf;
		CString sFile;
		static int iFile = 0;
		USES_CONVERSION;
		sFile.Format(L"%s\\script_%d.js",g_szScriptDump,iFile++);
		LPCSTR pp = W2A(pCode);
		mf.CreateFile(sFile,(PVOID)pp,wcslen(pCode));
		mf.Close();
	}
}

HRESULT CScriptDebugEngine::GetScriptEngine(CScriptDebugEngine* pTHIS,LPVOID* ppv)
{
	HRESULT hRes = S_OK;

	if (! pTHIS->m_pEngine) 
	{
		// Get the JScript engine that we delegate to and any other interface we need to wrap
		hRes = GetJScriptEngine((VOID**)&pTHIS->m_pEngine);
		if (SUCCEEDED(hRes))
			pTHIS->m_pEngineParse = CComQIPtr<IActiveScriptParse>(pTHIS->m_pEngine);
	}
	
	if (SUCCEEDED(hRes))
	{
		*ppv = dynamic_cast<IActiveScript*>(pTHIS);
		pTHIS->InternalAddRef();
	}

	return hRes;
}
 
HRESULT CScriptDebugEngine::GetJScriptEngine(LPVOID* ppv)
{
	CComPtr<IClassFactory> pFactory;
	HRESULT hRes = JSDUtils::GetJScriptClassObject((VOID**)&pFactory);
	if (SUCCEEDED(hRes))
		hRes = pFactory->CreateInstance(NULL,IID_IActiveScript,ppv);

	return hRes;
} 

HRESULT WINAPI CScriptDebugEngine::fnDelegate(void* pv, REFIID riid, LPVOID* ppv, DWORD dw)
{
	try
	{
		HRESULT hr = E_NOINTERFACE;
		CScriptDebugEngine* pTHIS = (CScriptDebugEngine*)pv;

		if (pTHIS)
		{
			if (IsEqualGUID(riid,IID_IActiveScript))
			{
				return GetScriptEngine(pTHIS,ppv);
			}
			else
			{
				ATLASSERT(pTHIS->m_pEngine);
				return pTHIS->m_pEngine->QueryInterface(riid,ppv);
			}
		}

		return E_NOINTERFACE;
	}
	catch(...)
	{
		ATLTRACE(L"Unexpected exception in CScriptDebugEngine::fnDelegate\n");
	}

	return E_NOINTERFACE;
}



By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions