508 lines
11 KiB
C++
508 lines
11 KiB
C++
|
#include "pch.h"
|
||
|
#pragma hdrstop
|
||
|
#include "modset.h"
|
||
|
#include "modtree.h"
|
||
|
#include "ncstl.h"
|
||
|
|
||
|
struct GMBCONTEXT
|
||
|
{
|
||
|
// The tree to reference for generating the set.
|
||
|
//
|
||
|
IN const CModuleTree* pTree;
|
||
|
|
||
|
// The module to start with when generating the set.
|
||
|
//
|
||
|
IN const CModule* pSourceMod;
|
||
|
|
||
|
// INS_FLAGS to use when adding DepChain to the set.
|
||
|
//
|
||
|
IN DWORD dwFlags;
|
||
|
|
||
|
// The module list set to generate based on pSourceMod.
|
||
|
//
|
||
|
IN OUT CModuleListSet* pSet;
|
||
|
|
||
|
// The result of the operation.
|
||
|
//
|
||
|
OUT HRESULT hr;
|
||
|
|
||
|
// This module list is built up via recursion. It is
|
||
|
// temporary. It represents a depenedency chain sourced
|
||
|
// at pSourceMod. It is added to the set when the depth
|
||
|
// of the chain (or a circular reference) is detected.
|
||
|
//
|
||
|
CModuleList DepChain;
|
||
|
};
|
||
|
|
||
|
VOID
|
||
|
GetModuleBindings (
|
||
|
IN const CModule* pMod,
|
||
|
IN OUT GMBCONTEXT* pCtx)
|
||
|
{
|
||
|
BOOL fFoundOne = FALSE;
|
||
|
const CModuleTreeEntry* pScan;
|
||
|
|
||
|
Assert (pCtx);
|
||
|
Assert (pCtx->pSourceMod);
|
||
|
Assert (pCtx->pSet);
|
||
|
Assert (pCtx->pTree);
|
||
|
|
||
|
// Append this module to te end of the context's working
|
||
|
// dependency chain.
|
||
|
//
|
||
|
pCtx->hr = pCtx->DepChain.HrInsertModule (pMod,
|
||
|
INS_ASSERT_IF_DUP | INS_APPEND);
|
||
|
if (S_OK != pCtx->hr)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// For all rows in the tree where the module is the one passed in...
|
||
|
//
|
||
|
for (pScan = pCtx->pTree->PFindFirstEntryWithModule (pMod);
|
||
|
(pScan != pCtx->pTree->end()) && (pScan->m_pModule == pMod);
|
||
|
pScan++)
|
||
|
{
|
||
|
fFoundOne = TRUE;
|
||
|
|
||
|
// Detect circular import chains.
|
||
|
//
|
||
|
if (pCtx->DepChain.FLinearFindModuleByPointer (pScan->m_pImportModule))
|
||
|
{
|
||
|
pCtx->DepChain.m_fCircular = TRUE;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
GetModuleBindings (pScan->m_pImportModule, pCtx);
|
||
|
if (S_OK != pCtx->hr)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If we didnt find any rows with pMod as a module, it means we
|
||
|
// hit the depth of the dependency chain. Time to add it to the set
|
||
|
// unless this is the original module we were asked to find the
|
||
|
// set for.
|
||
|
//
|
||
|
if (!fFoundOne && (pMod != pCtx->pSourceMod))
|
||
|
{
|
||
|
|
||
|
CHAR pszBuf [4096];
|
||
|
ULONG cch = celems(pszBuf);
|
||
|
pCtx->DepChain.FDumpToString (pszBuf, &cch);
|
||
|
strcat(pszBuf, "\n");
|
||
|
printf(pszBuf);
|
||
|
|
||
|
pCtx->hr = pCtx->pSet->HrAddModuleList (&pCtx->DepChain,
|
||
|
INS_APPEND | pCtx->dwFlags);
|
||
|
}
|
||
|
|
||
|
const CModule* pRemoved;
|
||
|
|
||
|
pRemoved = pCtx->DepChain.RemoveLastModule();
|
||
|
|
||
|
// This should be the component we appened above.
|
||
|
//
|
||
|
Assert (pRemoved == pMod);
|
||
|
}
|
||
|
|
||
|
|
||
|
CModuleTree::~CModuleTree ()
|
||
|
{
|
||
|
FreeCollectionAndItem (Modules);
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CModuleTree::HrAddEntry (
|
||
|
IN CModule* pMod,
|
||
|
IN CModule* pImport,
|
||
|
IN DWORD dwFlags)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
iterator InsertPosition = NULL;
|
||
|
CModuleTreeEntry* pEntry;
|
||
|
|
||
|
Assert (pMod);
|
||
|
Assert (pImport);
|
||
|
|
||
|
if (size() == capacity())
|
||
|
{
|
||
|
//fprintf(stderr, "growing module tree buffer\n");
|
||
|
|
||
|
__try
|
||
|
{
|
||
|
reserve (size() + 16384);
|
||
|
}
|
||
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hr = S_OK;
|
||
|
//pEntry = PFindFirstEntryAfterModuleGroup (pMod);
|
||
|
|
||
|
pEntry = PBinarySearchEntryByModule (pMod, &InsertPosition);
|
||
|
|
||
|
if (pEntry != end())
|
||
|
{
|
||
|
Assert (pEntry);
|
||
|
|
||
|
CModuleTreeEntry* pScan;
|
||
|
|
||
|
// Found an entry with a matching module. Need to scan backwards
|
||
|
// in the module group looking for a duplicate. If not found,
|
||
|
// Scan to the end looking for a duplicate and if we reach the
|
||
|
// end of the group, we can insert this entry there.
|
||
|
//
|
||
|
pScan = pEntry;
|
||
|
|
||
|
while (pScan != begin())
|
||
|
{
|
||
|
pScan--;
|
||
|
|
||
|
if (pScan->m_pModule != pMod)
|
||
|
{
|
||
|
// Left the group without finding a dupliate.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pScan->m_pImportModule == pImport)
|
||
|
{
|
||
|
// Don't insert duplicate entries.
|
||
|
//
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Assert (pMod == pEntry->m_pModule);
|
||
|
while (pEntry != end() && pEntry->m_pModule == pMod)
|
||
|
{
|
||
|
// Don't insert duplicate entries.
|
||
|
//
|
||
|
if (pEntry->m_pImportModule == pImport)
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
pEntry++;
|
||
|
}
|
||
|
|
||
|
// Looks like we'll be inserting it.
|
||
|
//
|
||
|
InsertPosition = pEntry;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// InsertPosition is the correct insertion point.
|
||
|
//
|
||
|
Assert (InsertPosition);
|
||
|
}
|
||
|
|
||
|
__try
|
||
|
{
|
||
|
CModuleTreeEntry Entry;
|
||
|
|
||
|
Entry.m_pModule = pMod;
|
||
|
Entry.m_pImportModule = pImport;
|
||
|
Entry.m_dwFlags = dwFlags;
|
||
|
|
||
|
Assert (InsertPosition);
|
||
|
insert (InsertPosition, Entry);
|
||
|
Assert (S_OK == hr);
|
||
|
|
||
|
DbgVerifySorted();
|
||
|
}
|
||
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CModuleTree::HrGetModuleBindings (
|
||
|
IN const CModule* pMod,
|
||
|
IN DWORD dwFlags /* GMB_FLAGS */,
|
||
|
OUT CModuleListSet* pSet) const
|
||
|
{
|
||
|
GMBCONTEXT Ctx;
|
||
|
|
||
|
Assert (pMod);
|
||
|
Assert (dwFlags);
|
||
|
Assert (pSet);
|
||
|
|
||
|
// Initialize the output parameter.
|
||
|
//
|
||
|
if (!(dwFlags & GMBF_ADD_TO_MLSET))
|
||
|
{
|
||
|
pSet->clear();
|
||
|
}
|
||
|
|
||
|
// Initialize members of the context structure for recursion.
|
||
|
//
|
||
|
ZeroMemory (&Ctx, sizeof(Ctx));
|
||
|
Ctx.pTree = this;
|
||
|
Ctx.pSourceMod = pMod;
|
||
|
Ctx.dwFlags = (dwFlags & GMBF_ADD_TO_MLSET)
|
||
|
? INS_IGNORE_IF_DUP
|
||
|
: INS_ASSERT_IF_DUP;
|
||
|
Ctx.pSet = pSet;
|
||
|
|
||
|
GetModuleBindings (pMod, &Ctx);
|
||
|
|
||
|
return Ctx.hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
CModuleTreeEntry*
|
||
|
CModuleTree::PBinarySearchEntryByModule (
|
||
|
IN const CModule* pMod,
|
||
|
OUT CModuleTreeEntry** pInsertPosition OPTIONAL) const
|
||
|
{
|
||
|
Assert (pMod);
|
||
|
|
||
|
// Find the module using a binary search.
|
||
|
//
|
||
|
if (size())
|
||
|
{
|
||
|
LONG Lo;
|
||
|
LONG Hi;
|
||
|
LONG Mid;
|
||
|
INT Result;
|
||
|
const CModuleTreeEntry* pScan;
|
||
|
PCSTR pszFileName = pMod->m_pszFileName;
|
||
|
|
||
|
Lo = 0;
|
||
|
Hi = size() - 1;
|
||
|
|
||
|
while (Hi >= Lo)
|
||
|
{
|
||
|
Mid = (Lo + Hi) / 2;
|
||
|
|
||
|
Assert ((UINT)Mid < size());
|
||
|
pScan = (begin() + Mid);
|
||
|
|
||
|
Result = strcmp (pszFileName, pScan->m_pModule->m_pszFileName);
|
||
|
|
||
|
if (Result < 0)
|
||
|
{
|
||
|
Hi = Mid - 1;
|
||
|
}
|
||
|
else if (Result > 0)
|
||
|
{
|
||
|
Lo = Mid + 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Assert (pMod == pScan->m_pModule);
|
||
|
return const_cast<CModuleTreeEntry*>(pScan);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If we make it to here, the module was not found.
|
||
|
//
|
||
|
if (pInsertPosition)
|
||
|
{
|
||
|
CModule* pGroupMod;
|
||
|
const CModuleTreeEntry* pPrev;
|
||
|
|
||
|
// Seek to the beginning of this group. We need to insert
|
||
|
// before the entire group, not just the one item we last found.
|
||
|
//
|
||
|
pScan = begin() + Lo;
|
||
|
|
||
|
if (pScan != begin())
|
||
|
{
|
||
|
pGroupMod = pScan->m_pModule;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
pPrev = pScan - 1;
|
||
|
|
||
|
if (pPrev->m_pModule == pGroupMod)
|
||
|
{
|
||
|
pScan = pPrev;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
} while (pPrev != begin());
|
||
|
}
|
||
|
|
||
|
*pInsertPosition = const_cast<CModuleTreeEntry*>(pScan);
|
||
|
Assert (*pInsertPosition >= begin());
|
||
|
Assert (*pInsertPosition <= end());
|
||
|
}
|
||
|
}
|
||
|
else if (pInsertPosition)
|
||
|
{
|
||
|
// Empty collection. Insert position is at the beginning.
|
||
|
//
|
||
|
*pInsertPosition = const_cast<CModuleTreeEntry*>(begin());
|
||
|
}
|
||
|
|
||
|
return const_cast<CModuleTreeEntry*>(end());
|
||
|
}
|
||
|
|
||
|
CModuleTreeEntry*
|
||
|
CModuleTree::PFindFirstEntryWithModule (
|
||
|
IN const CModule* pMod) const
|
||
|
{
|
||
|
CModuleTreeEntry* pEntry;
|
||
|
|
||
|
Assert (pMod);
|
||
|
|
||
|
pEntry = PBinarySearchEntryByModule (pMod, NULL);
|
||
|
|
||
|
if (pEntry != end())
|
||
|
{
|
||
|
Assert (pEntry);
|
||
|
|
||
|
if (pEntry != begin())
|
||
|
{
|
||
|
CModuleTreeEntry* pPrev;
|
||
|
|
||
|
Assert (pMod == pEntry->m_pModule);
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
pPrev = pEntry - 1;
|
||
|
|
||
|
if (pPrev->m_pModule == pMod)
|
||
|
{
|
||
|
pEntry = pPrev;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pPrev == begin())
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pEntry;
|
||
|
}
|
||
|
|
||
|
CModuleTreeEntry*
|
||
|
CModuleTree::PFindFirstEntryAfterModuleGroup (
|
||
|
IN const CModule* pMod) const
|
||
|
{
|
||
|
CModuleTreeEntry* pEntry;
|
||
|
|
||
|
Assert (pMod);
|
||
|
|
||
|
pEntry = PBinarySearchEntryByModule (pMod, NULL);
|
||
|
|
||
|
if (pEntry != end())
|
||
|
{
|
||
|
Assert (pEntry);
|
||
|
Assert (pMod == pEntry->m_pModule);
|
||
|
|
||
|
while (pEntry != end() && pEntry->m_pModule == pMod)
|
||
|
{
|
||
|
pEntry++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pEntry;
|
||
|
}
|
||
|
|
||
|
CModuleTreeEntry*
|
||
|
CModuleTree::PBinarySearchEntry (
|
||
|
IN const CModule* pMod,
|
||
|
IN const CModule* pImport,
|
||
|
OUT CModuleTreeEntry** pInsertPosition OPTIONAL) const
|
||
|
{
|
||
|
CModuleTreeEntry* pEntry;
|
||
|
|
||
|
Assert (this);
|
||
|
Assert (pMod);
|
||
|
Assert (pImport);
|
||
|
|
||
|
pEntry = PBinarySearchEntryByModule (pMod, pInsertPosition);
|
||
|
|
||
|
if (pEntry != end())
|
||
|
{
|
||
|
Assert (pEntry);
|
||
|
|
||
|
const CModuleTreeEntry* pScan;
|
||
|
|
||
|
// Found an entry with a matching module. Need to scan backwards
|
||
|
// in the module group looking for a match. If not found,
|
||
|
// Scan to the end looking for a match and if we reach the
|
||
|
// end of the group, that will be the insert position (if specified).
|
||
|
//
|
||
|
pScan = pEntry;
|
||
|
while (pScan != begin())
|
||
|
{
|
||
|
pScan--;
|
||
|
|
||
|
if (pScan->m_pModule != pMod)
|
||
|
{
|
||
|
// Left the group without finding a dupliate.
|
||
|
//
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pScan->m_pImportModule == pImport)
|
||
|
{
|
||
|
Assert (pScan->m_pModule == pMod);
|
||
|
return const_cast<CModuleTreeEntry*>(pScan);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pScan = pEntry;
|
||
|
Assert (pMod == pScan->m_pModule);
|
||
|
while (pScan != end() && pScan->m_pModule == pMod)
|
||
|
{
|
||
|
if (pScan->m_pImportModule == pImport)
|
||
|
{
|
||
|
Assert (pScan->m_pModule == pMod);
|
||
|
return const_cast<CModuleTreeEntry*>(pScan);
|
||
|
}
|
||
|
|
||
|
pScan++;
|
||
|
}
|
||
|
|
||
|
if (pInsertPosition)
|
||
|
{
|
||
|
*pInsertPosition = const_cast<CModuleTreeEntry*>(pScan);
|
||
|
}
|
||
|
|
||
|
// No match.
|
||
|
pEntry = const_cast<CModuleTreeEntry*>(end());
|
||
|
}
|
||
|
|
||
|
return pEntry;
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
VOID
|
||
|
CModuleTree::DbgVerifySorted ()
|
||
|
{
|
||
|
CModuleTreeEntry* pScan;
|
||
|
CModuleTreeEntry* pPrev = NULL;
|
||
|
|
||
|
if (size() > 1)
|
||
|
{
|
||
|
for (pPrev = begin(), pScan = begin() + 1; pScan != end(); pScan++)
|
||
|
{
|
||
|
Assert (strcmp(pPrev->m_pModule->m_pszFileName,
|
||
|
pScan->m_pModule->m_pszFileName) <= 0);
|
||
|
|
||
|
pPrev = pScan;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|