You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
606 lines
16 KiB
606 lines
16 KiB
/*==============================================================================
|
|
Microsoft Denali
|
|
|
|
Microsoft Confidential.
|
|
Copyright 1996 Microsoft Corporation. All Rights Reserved.
|
|
|
|
File: dbgcxt.cpp
|
|
Maintained by: DGottner
|
|
Component: Implementation of IDebugDocumentContext for CTemplates
|
|
==============================================================================*/
|
|
#include "denpre.h"
|
|
#pragma hdrstop
|
|
|
|
#include "dbgcxt.h"
|
|
#include "perfdata.h"
|
|
#include "memchk.h"
|
|
|
|
// {5FA45A6C-AB8A-11d0-8EBA-00C04FC34DCC}
|
|
const GUID IID_IDenaliTemplateDocumentContext =
|
|
{ 0x5fa45a6c, 0xab8a, 0x11d0, { 0x8e, 0xba, 0x0, 0xc0, 0x4f, 0xc3, 0x4d, 0xcc } };
|
|
|
|
// {3AED94BE-ED79-11d0-8F34-00C04FC34DCC}
|
|
static const GUID IID_IDenaliIncFileDocumentContext =
|
|
{ 0x3aed94be, 0xed79, 0x11d0, { 0x8f, 0x34, 0x0, 0xc0, 0x4f, 0xc3, 0x4d, 0xcc } };
|
|
|
|
|
|
/*
|
|
*
|
|
* C T e m p l a t e D o c u m e n t C o n t e x t
|
|
*
|
|
*/
|
|
|
|
/* ============================================================================
|
|
CTemplateDocumentContext::CTemplateDocumentContext
|
|
Constructor
|
|
*/
|
|
CTemplateDocumentContext::CTemplateDocumentContext
|
|
(
|
|
CTemplate *pTemplate,
|
|
ULONG cchSourceOffset,
|
|
ULONG cchText,
|
|
IActiveScriptDebug *pDebugScript,
|
|
ULONG idEngine,
|
|
ULONG cchTargetOffset
|
|
)
|
|
{
|
|
Assert (pTemplate != NULL);
|
|
|
|
m_pTemplate = pTemplate;
|
|
m_idEngine = idEngine;
|
|
m_pDebugScript = pDebugScript;
|
|
m_cchSourceOffset = cchSourceOffset;
|
|
m_cchTargetOffset = cchTargetOffset;
|
|
m_cchText = cchText;
|
|
m_cRefs = 1;
|
|
|
|
m_pTemplate->AddRef();
|
|
|
|
if (m_pDebugScript)
|
|
{
|
|
m_pDebugScript->AddRef();
|
|
|
|
// If they passed in a script, then they must also pass in target offset & engine ID
|
|
Assert (m_idEngine != -1);
|
|
Assert (m_cchTargetOffset != -1);
|
|
}
|
|
}
|
|
|
|
/* ============================================================================
|
|
CTemplateDocumentContext::~CTemplateDocumentContext
|
|
Destructor
|
|
*/
|
|
CTemplateDocumentContext::~CTemplateDocumentContext
|
|
(
|
|
)
|
|
{
|
|
m_pTemplate->Release();
|
|
|
|
if (m_pDebugScript)
|
|
m_pDebugScript->Release();
|
|
}
|
|
|
|
/* ============================================================================
|
|
CTemplateDocumentContext::QueryInterface
|
|
|
|
NOTE: QueryInterface here is also used by CTemplate to determine if an
|
|
arbitrary document context is ours.
|
|
*/
|
|
HRESULT CTemplateDocumentContext::QueryInterface
|
|
(
|
|
const GUID & guid,
|
|
void ** ppvObj
|
|
)
|
|
{
|
|
if (guid == IID_IUnknown ||
|
|
guid == IID_IDebugDocumentContext ||
|
|
guid == IID_IDenaliTemplateDocumentContext)
|
|
{
|
|
*ppvObj = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
/* ============================================================================
|
|
CTemplateDocumentContext::AddRef
|
|
CTemplateDocumentContext::Release
|
|
|
|
NOTE: Don't know if these need to be protected with Interlocked(In|De)crement.
|
|
*/
|
|
ULONG CTemplateDocumentContext::AddRef()
|
|
{
|
|
InterlockedIncrement(&m_cRefs);
|
|
return m_cRefs;
|
|
}
|
|
|
|
ULONG CTemplateDocumentContext::Release()
|
|
{
|
|
LONG cRefs = InterlockedDecrement(&m_cRefs);
|
|
if (cRefs)
|
|
return cRefs;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
/* ============================================================================
|
|
CTemplateDocumentContext::GetDocument
|
|
Return the document.
|
|
*/
|
|
HRESULT CTemplateDocumentContext::GetDocument
|
|
(
|
|
/* [out] */ IDebugDocument **ppDebugDocument
|
|
)
|
|
{
|
|
#ifndef PERF_DISABLE
|
|
g_PerfData.Incr_DEBUGDOCREQ();
|
|
#endif
|
|
return m_pTemplate->QueryInterface(IID_IDebugDocument, reinterpret_cast<void **>(ppDebugDocument));
|
|
}
|
|
|
|
/* ============================================================================
|
|
CTemplateDocumentContext::EnumCodeContexts
|
|
Convert document offset to script offset and enumerate code contexts
|
|
*/
|
|
|
|
HRESULT CTemplateDocumentContext::EnumCodeContexts
|
|
(
|
|
/* [out] */ IEnumDebugCodeContexts **ppEnumerator
|
|
)
|
|
{
|
|
if (! m_pTemplate->FIsValid())
|
|
return E_FAIL;
|
|
|
|
if (m_pDebugScript == NULL)
|
|
{
|
|
// Convert offset
|
|
m_pTemplate->GetTargetOffset(m_pTemplate->GetSourceFileName(), m_cchSourceOffset, &m_idEngine, &m_cchTargetOffset);
|
|
|
|
// See if the script ran and template is holding onto it
|
|
CActiveScriptEngine *pScriptEngine = m_pTemplate->GetActiveScript(m_idEngine);
|
|
if (pScriptEngine)
|
|
{
|
|
if (FAILED(pScriptEngine->GetActiveScript()->QueryInterface(IID_IActiveScriptDebug, reinterpret_cast<void **>(&m_pDebugScript))))
|
|
{
|
|
pScriptEngine->Release();
|
|
return E_FAIL;
|
|
}
|
|
|
|
pScriptEngine->IsBeingDebugged();
|
|
pScriptEngine->Release();
|
|
}
|
|
|
|
// Script may be still running ("stop" statement case)
|
|
if (m_pDebugScript == NULL)
|
|
m_pDebugScript = g_ScriptManager.GetDebugScript(m_pTemplate, m_idEngine);
|
|
|
|
// This is probably a bug...
|
|
if (m_pDebugScript == NULL) // don't have a running script to match this
|
|
return E_FAIL;
|
|
|
|
// No need for AddRef(); m_pDebugScript called funtions that AddRef'ed
|
|
}
|
|
|
|
return m_pDebugScript->EnumCodeContextsOfPosition(
|
|
m_idEngine,
|
|
m_cchTargetOffset,
|
|
m_cchText,
|
|
ppEnumerator);
|
|
}
|
|
|
|
/*
|
|
*
|
|
* C I n c F i l e E n u m C o d e C o n t e x t s
|
|
*
|
|
*
|
|
* For an include file, the corresponding code contexts are the union
|
|
* of all appropriate code contexts in all template objects that are using
|
|
* the include file. This special enumerator implements the union.
|
|
*/
|
|
class CIncFileEnumCodeContexts : public IEnumDebugCodeContexts
|
|
{
|
|
private:
|
|
CIncFileDocumentContext * m_pContext; // context we are providing enumeration for
|
|
IEnumDebugCodeContexts * m_pEnumCodeContexts; // Current code context enumerator
|
|
LONG m_cRefs; // reference count
|
|
int m_iTemplate; // index of current template
|
|
|
|
IEnumDebugCodeContexts *GetEnumerator(int *piTemplate); // Get enumerator for a template
|
|
|
|
public:
|
|
CIncFileEnumCodeContexts(CIncFileDocumentContext *pIncFileDocumentContext);
|
|
~CIncFileEnumCodeContexts();
|
|
|
|
// IUnknown methods
|
|
|
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(const GUID &guid, void **ppvObj);
|
|
virtual ULONG STDMETHODCALLTYPE AddRef();
|
|
virtual ULONG STDMETHODCALLTYPE Release();
|
|
|
|
// IEnumDebugCodeContexts methods
|
|
|
|
virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, IDebugCodeContext **pscc, ULONG *pceltFetched);
|
|
virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt);
|
|
virtual HRESULT STDMETHODCALLTYPE Reset(void);
|
|
virtual HRESULT STDMETHODCALLTYPE Clone(IEnumDebugCodeContexts **ppescc);
|
|
};
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::CIncFileEnumCodeContexts
|
|
Constructor
|
|
*/
|
|
CIncFileEnumCodeContexts::CIncFileEnumCodeContexts
|
|
(
|
|
CIncFileDocumentContext *pDocumentContext
|
|
)
|
|
{
|
|
m_pContext = pDocumentContext;
|
|
m_pContext->AddRef();
|
|
m_cRefs = 1;
|
|
Reset();
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::~CIncFileEnumCodeContexts
|
|
Destructor
|
|
*/
|
|
CIncFileEnumCodeContexts::~CIncFileEnumCodeContexts()
|
|
{
|
|
m_pContext->Release();
|
|
if (m_pEnumCodeContexts)
|
|
m_pEnumCodeContexts->Release();
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::GetEnumerator
|
|
Get a code context enumerator for the current script engine
|
|
|
|
Side Effects:
|
|
piTemplate is incremented to point to the next available template
|
|
(piTemplate is really an "iteration cookie" -- don't think of it as an index)
|
|
*/
|
|
IEnumDebugCodeContexts *CIncFileEnumCodeContexts::GetEnumerator
|
|
(
|
|
int *piTemplate
|
|
)
|
|
{
|
|
// Get a template from the array - may need to retry if template contains compiler errors
|
|
CTemplate *pTemplate;
|
|
do
|
|
{
|
|
// GetTemplate returns NULL when array index is out of range (which is when iteration is exhaused)
|
|
pTemplate = m_pContext->m_pIncFile->GetTemplate((*piTemplate)++);
|
|
if (pTemplate == NULL)
|
|
return NULL;
|
|
}
|
|
while (! pTemplate->FIsValid());
|
|
|
|
// If we got this far, we got one of the users of this include file. Convert the offset
|
|
ULONG idEngine, cchTargetOffset;
|
|
pTemplate->GetTargetOffset(m_pContext->m_pIncFile->GetIncFileName(), m_pContext->m_cchSourceOffset, &idEngine, &cchTargetOffset);
|
|
|
|
// Now we have the engine ID, see if template is holding onto corresponding engine
|
|
IActiveScriptDebug *pDebugScriptEngine = NULL;
|
|
CActiveScriptEngine *pScriptEngine = pTemplate->GetActiveScript(idEngine);
|
|
if (pScriptEngine)
|
|
{
|
|
if (FAILED(pScriptEngine->GetActiveScript()->QueryInterface(IID_IActiveScriptDebug, reinterpret_cast<void **>(&pDebugScriptEngine))))
|
|
{
|
|
pScriptEngine->Release();
|
|
return NULL;
|
|
}
|
|
|
|
pScriptEngine->IsBeingDebugged();
|
|
pScriptEngine->Release();
|
|
}
|
|
|
|
// If we could not get the engine that way, the script is likely still in the running state
|
|
if (pDebugScriptEngine == NULL)
|
|
pDebugScriptEngine = g_ScriptManager.GetDebugScript(pTemplate, idEngine);
|
|
|
|
// This is probably a bug...
|
|
if (pDebugScriptEngine == NULL) // don't have a running script to match this
|
|
return NULL;
|
|
|
|
IEnumDebugCodeContexts *pEnumerator;
|
|
HRESULT hrGotEnum = pDebugScriptEngine->EnumCodeContextsOfPosition(
|
|
idEngine,
|
|
cchTargetOffset,
|
|
m_pContext->m_cchText,
|
|
&pEnumerator);
|
|
|
|
|
|
pDebugScriptEngine->Release();
|
|
return SUCCEEDED(hrGotEnum)? pEnumerator : NULL;
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::QueryInterface
|
|
*/
|
|
HRESULT CIncFileEnumCodeContexts::QueryInterface
|
|
(
|
|
const GUID & guid,
|
|
void ** ppvObj
|
|
)
|
|
{
|
|
if (guid == IID_IUnknown || guid == IID_IEnumDebugCodeContexts)
|
|
{
|
|
*ppvObj = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::AddRef
|
|
CIncFileEnumCodeContexts::Release
|
|
|
|
NOTE: Don't know if these need to be protected with Interlocked(In|De)crement.
|
|
*/
|
|
ULONG CIncFileEnumCodeContexts::AddRef()
|
|
{
|
|
InterlockedIncrement(&m_cRefs);
|
|
return m_cRefs;
|
|
}
|
|
|
|
ULONG CIncFileEnumCodeContexts::Release()
|
|
{
|
|
LONG cRefs = InterlockedDecrement(&m_cRefs);
|
|
if (cRefs)
|
|
return cRefs;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::Clone
|
|
|
|
Clone this iterator (standard method)
|
|
*/
|
|
HRESULT CIncFileEnumCodeContexts::Clone
|
|
(
|
|
IEnumDebugCodeContexts **ppEnumClone
|
|
)
|
|
{
|
|
CIncFileEnumCodeContexts *pClone = new CIncFileEnumCodeContexts(m_pContext);
|
|
if (pClone == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// new iterator should point to same location as this.
|
|
pClone->m_iTemplate = m_iTemplate;
|
|
pClone->m_pEnumCodeContexts = m_pEnumCodeContexts;
|
|
if (m_pEnumCodeContexts)
|
|
m_pEnumCodeContexts->AddRef();
|
|
|
|
*ppEnumClone = pClone;
|
|
return S_OK;
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::Next
|
|
|
|
Get next value (standard method)
|
|
|
|
To rehash standard OLE semantics:
|
|
|
|
We get the next "cElements" from the collection and store them
|
|
in "rgVariant" which holds at least "cElements" items. On
|
|
return "*pcElementsFetched" contains the actual number of elements
|
|
stored. Returns S_FALSE if less than "cElements" were stored, S_OK
|
|
otherwise.
|
|
*/
|
|
HRESULT CIncFileEnumCodeContexts::Next
|
|
(
|
|
unsigned long cElementsRequested,
|
|
IDebugCodeContext **ppCodeContexts,
|
|
unsigned long *pcElementsFetched
|
|
)
|
|
{
|
|
// give a valid pointer value to 'pcElementsFetched'
|
|
//
|
|
unsigned long cLocalElementsFetched;
|
|
if (pcElementsFetched == NULL)
|
|
pcElementsFetched = &cLocalElementsFetched;
|
|
|
|
// Initialize things
|
|
//
|
|
unsigned long cElements = cElementsRequested;
|
|
*pcElementsFetched = 0;
|
|
|
|
// Loop over all templates until we fill the ppCodeContext array or we've exhausted the collection
|
|
// (when m_pEnumCodeContexts is NULL that means we are done)
|
|
//
|
|
while (cElements > 0 && m_pEnumCodeContexts)
|
|
{
|
|
// Fetch as many contexts as we can from the current iterator
|
|
unsigned long cElementsFetched;
|
|
HRESULT hrEnum = m_pEnumCodeContexts->Next(cElements, ppCodeContexts, &cElementsFetched);
|
|
if (FAILED(hrEnum))
|
|
return hrEnum;
|
|
|
|
// If iterator did not fill entire array, advance to next one
|
|
if (cElementsFetched < cElements)
|
|
{
|
|
// Advance - first release the current iterator
|
|
m_pEnumCodeContexts->Release();
|
|
m_pEnumCodeContexts = GetEnumerator(&m_iTemplate);
|
|
}
|
|
|
|
*pcElementsFetched += cElementsFetched;
|
|
ppCodeContexts += cElementsFetched;
|
|
cElements -= cElementsFetched;
|
|
}
|
|
|
|
// initialize the remaining structures
|
|
while (cElements-- > 0)
|
|
*ppCodeContexts++ = NULL;
|
|
|
|
return (*pcElementsFetched == cElementsRequested)? S_OK : S_FALSE;
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::Skip
|
|
|
|
Skip items (standard method)
|
|
|
|
To rehash standard OLE semantics:
|
|
|
|
We skip over the next "cElements" from the collection.
|
|
Returns S_FALSE if less than "cElements" were skipped, S_OK
|
|
otherwise.
|
|
*/
|
|
HRESULT CIncFileEnumCodeContexts::Skip(unsigned long cElements)
|
|
{
|
|
/* Loop through the collection until either we reach the end or
|
|
* cElements becomes zero. Since the iteration logic is
|
|
* so complex, we do not repeat it here.
|
|
*/
|
|
HRESULT hrElementFetched = S_OK;
|
|
while (cElements > 0 && hrElementFetched == S_OK)
|
|
{
|
|
IDebugCodeContext *pCodeContext;
|
|
hrElementFetched = Next(1, &pCodeContext, NULL);
|
|
pCodeContext->Release();
|
|
--cElements;
|
|
}
|
|
|
|
return (cElements == 0)? S_OK : S_FALSE;
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileEnumCodeContexts::Reset
|
|
|
|
Reset the iterator (standard method)
|
|
*/
|
|
HRESULT CIncFileEnumCodeContexts::Reset()
|
|
{
|
|
m_iTemplate = 0;
|
|
m_pEnumCodeContexts = GetEnumerator(&m_iTemplate);
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* C I n c F i l e D o c u m e n t C o n t e x t
|
|
*
|
|
*/
|
|
|
|
/* ============================================================================
|
|
CIncFileDocumentContext::CIncFileDocumentContext
|
|
Constructor
|
|
*/
|
|
CIncFileDocumentContext::CIncFileDocumentContext
|
|
(
|
|
CIncFile *pIncFile,
|
|
ULONG cchSourceOffset,
|
|
ULONG cchText
|
|
)
|
|
{
|
|
Assert (pIncFile != NULL);
|
|
|
|
m_pIncFile = pIncFile;
|
|
m_cchSourceOffset = cchSourceOffset;
|
|
m_cchText = cchText;
|
|
m_cRefs = 1;
|
|
|
|
m_pIncFile->AddRef();
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileDocumentContext::~CIncFileDocumentContext
|
|
Destructor
|
|
*/
|
|
CIncFileDocumentContext::~CIncFileDocumentContext
|
|
(
|
|
)
|
|
{
|
|
m_pIncFile->Release();
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileDocumentContext::QueryInterface
|
|
|
|
NOTE: QueryInterface here is also used by CIncFile to determine if an
|
|
arbitrary document context is ours.
|
|
*/
|
|
HRESULT CIncFileDocumentContext::QueryInterface
|
|
(
|
|
const GUID & guid,
|
|
void ** ppvObj
|
|
)
|
|
{
|
|
if (guid == IID_IUnknown ||
|
|
guid == IID_IDebugDocumentContext ||
|
|
guid == IID_IDenaliIncFileDocumentContext)
|
|
{
|
|
*ppvObj = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileDocumentContext::AddRef
|
|
CIncFileDocumentContext::Release
|
|
|
|
NOTE: Don't know if these need to be protected with Interlocked(In|De)crement.
|
|
*/
|
|
ULONG CIncFileDocumentContext::AddRef()
|
|
{
|
|
InterlockedIncrement(&m_cRefs);
|
|
return m_cRefs;
|
|
}
|
|
|
|
ULONG CIncFileDocumentContext::Release()
|
|
{
|
|
LONG cRefs = InterlockedDecrement(&m_cRefs);
|
|
if (cRefs)
|
|
return cRefs;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileDocumentContext::GetDocument
|
|
Return the document.
|
|
*/
|
|
HRESULT CIncFileDocumentContext::GetDocument
|
|
(
|
|
/* [out] */ IDebugDocument **ppDebugDocument
|
|
)
|
|
{
|
|
#ifndef PERF_DISABLE
|
|
g_PerfData.Incr_DEBUGDOCREQ();
|
|
#endif
|
|
return m_pIncFile->QueryInterface(IID_IDebugDocument, reinterpret_cast<void **>(ppDebugDocument));
|
|
}
|
|
|
|
/* ============================================================================
|
|
CIncFileDocumentContext::EnumCodeContexts
|
|
Convert document offset to script offset and enumerate code contexts
|
|
*/
|
|
HRESULT CIncFileDocumentContext::EnumCodeContexts
|
|
(
|
|
/* [out] */ IEnumDebugCodeContexts **ppEnumerator
|
|
)
|
|
{
|
|
if ((*ppEnumerator = new CIncFileEnumCodeContexts(this)) == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return S_OK;
|
|
}
|