/*============================================================================== 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() { if (InterlockedDecrement(&m_cRefs) == 0) { delete this; return 0; } return m_cRefs; } /* ============================================================================ 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(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(&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(&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() { if (InterlockedDecrement(&m_cRefs) == 0) { delete this; return 0; } return m_cRefs; } /* ============================================================================ 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() { if (InterlockedDecrement(&m_cRefs) == 0) { delete this; return 0; } return m_cRefs; } /* ============================================================================ 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(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; }