1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00
Beef/IDEHelper/Clang/CDepChecker.cpp
2019-08-23 11:56:54 -07:00

538 lines
No EOL
12 KiB
C++

#include "CDepChecker.h"
#include "BeefySysLib/util/PerfTimer.h"
#include "ClangHelper.h"
#include "BeefySysLib/util/AllocDebug.h"
USING_NS_BF;
//////////////////////////////////////////////////////////////////////////
static CDepChecker* gCDepChecker = NULL;
static PerfManager gCDepPerfManager;
static PerfManager* gPerfManagerPtr = NULL;
CDepFile::~CDepFile()
{
delete mParser;
}
void CppParser::HandlePragma(const StringImpl& pragma, BfBlock* block)
{
}
void CppParser::HandleDefine(const StringImpl& defineString, BfAstNode* paramNode)
{
}
void CppParser::HandleUndefine(const StringImpl& name)
{
}
MaybeBool CppParser::HandleIfDef(const StringImpl& name)
{
return MaybeBool_None;
}
MaybeBool CppParser::HandleProcessorCondition(BfBlock* paramNode)
{
return MaybeBool_None;
}
void CppParser::HandleInclude(BfAstNode* paramNode)
{
IncludeHelper(paramNode, false, false);
}
void CppParser::HandleIncludeNext(BfAstNode* paramNode)
{
IncludeHelper(paramNode, true, false);
}
bool CppParser::IncludeHelper(BfAstNode* paramNode, bool includeNext, bool scanOnly)
{
String name;
bool isLocalInclude = false;
auto checkNode = paramNode;
if (auto identifier = BfNodeDynCast<BfIdentifierNode>(checkNode))
{
//EvaluatePreprocessor(identifier, NULL, &checkNode);
return false;
}
if (auto token = BfNodeDynCast<BfTokenNode>(checkNode))
{
//TODO: DO THIS
/*if (token->mToken == BfToken_LChevron)
{
auto curNode = checkNode->mNext;
while (curNode != NULL)
{
if ((token = BfNodeDynCast<BfTokenNode>(curNode)))
{
if (token->mToken == BfToken_RChevron)
break;
}
name += curNode->ToString();
curNode = curNode->mNext;
}
}*/
}
else if (auto literalExpr = BfNodeDynCast<BfLiteralExpression>(checkNode))
{
if (literalExpr->mValue.mTypeCode == BfTypeCode_CharPtr)
{
name = String(literalExpr->GetSourceData()->mSrc + literalExpr->GetSrcStart() + 1, literalExpr->GetSourceData()->mSrc + literalExpr->GetSrcEnd() - 1);
isLocalInclude = true;
}
}
if (name.empty())
return false;
for (int i = 0; i < (int)name.length(); i++)
if (name[i] == DIR_SEP_CHAR_ALT)
name[i] = DIR_SEP_CHAR;
String curDir = GetFileDir(mFileName);
bool found = false;
bool foundCurPath = false;
for (int i = isLocalInclude ? -1 : 0; i < (int)mCDepChecker->mIncludeDirs.size(); i++)
{
String checkPath;
if (i == -1)
checkPath = curDir;
else
checkPath = mCDepChecker->mIncludeDirs[i];
if (includeNext)
{
if (checkPath == curDir)
{
foundCurPath = true;
continue;
}
if (!foundCurPath)
continue;
}
checkPath = GetAbsPath(name, checkPath);
#ifdef _WIN32
checkPath = ToUpper(checkPath);
#endif
if (mCDepChecker->CachedFileExists(checkPath))
{
if (scanOnly)
return true;
bool isFromCache = false;
found = true;
CDepFile* cDepFile = mCDepChecker->LoadFile(checkPath, mCDepFile, &isFromCache);
if (cDepFile != NULL)
{
for (auto refFileName : cDepFile->mFilesReferenced)
mCDepFile->mFilesReferenced.insert(refFileName);
}
return true;
}
}
//BF_ASSERT(found);
return false;
}
//////////////////////////////////////////////////////////////////////////
CDepChecker::CDepChecker()
{
mAbort = false;
mCPPCount = 0;
mFindHeaderIdx = -1;
}
void CDepChecker::SetCArgs(const char* cArgs)
{
int strStart = 0;
for (int i = 0; cArgs[i] != (char)0; i++)
{
if (cArgs[i] == '\n')
{
String arg = String(cArgs + strStart, cArgs + i);
if (strncmp(arg.c_str(), "-I", 2) == 0)
{
String includeDir = arg.Substring(2);
includeDir = EnsureEndsInSlash(includeDir);
#ifdef _WIN32
includeDir = ToUpper(includeDir);
#endif
mIncludeDirs.push_back(includeDir);
}
else if (strncmp(arg.c_str(), "-D", 2) == 0)
{
}
strStart = i + 1;
}
}
}
/*String CDepChecker::DetermineFilesReferenced(const StringImpl& fileName)
{
LoadFile(fileName);
return "";
}*/
CDepFile* CDepChecker::LoadFile(const StringImpl& inFileName, CDepFile* fileFrom, bool* isFromCache, const char* contentOverride)
{
*isFromCache = false;
if (mAbort)
return NULL;
AutoPerf autoPerf("LoadFile", gPerfManagerPtr);
#ifdef _WIN32
String fileName = ToUpper(inFileName);
#else
String fileName = inFileName;
#endif
if (mFindHeaderFileName.length() > 0)
{
if (fileFrom != NULL)
{
if ((mFindHeaderIdx == -1) && (fileName == mFindHeaderFileName))
mFindHeaderIdx = fileFrom->mParser->mLineStart;
return NULL;
}
}
//Val128 val128 = cDepPreprocState->mBaseHash;
//HASH128_MIXIN_STR(val128, fileName);
CDepFile* dataCDep = NULL;
auto depFileItr = mDepFileMap.find(fileName);
if (depFileItr != mDepFileMap.end())
{
auto checkCDepFile = depFileItr->second;
depFileItr++;
dataCDep = checkCDepFile;
if (checkCDepFile->mProcessing)
{
BF_ASSERT(fileFrom != NULL);
// Propagate include files down the line once we finish processing cDepFile
checkCDepFile->mDeferredDepSet.insert(fileFrom);
for (auto childDep : fileFrom->mDeferredDepSet)
checkCDepFile->mDeferredDepSet.insert(childDep);
}
*isFromCache = true;
return checkCDepFile;
}
CDepFile* cDepFile = new CDepFile();
cDepFile->mFilePath = fileName;
cDepFile->mFilesReferenced.insert(fileName);
cDepFile->mProcessing = true;
bool fileAlreadyVisited = mFilesVisited.find(fileName) != mFilesVisited.end();
if (!fileAlreadyVisited)
mFilesVisited.insert(fileName);
mDepFileMap.insert(CDepFileMap::value_type(fileName, cDepFile));
BfPassInstance bfPassInstance(NULL);
CppParser* newParser = new CppParser();
cDepFile->mParser = newParser;
//int64 fileTime = GetFileTimeWrite(fileName);
//cDepFile->mFileTime = fileTime;
int fileSize = 0;
newParser->mAstAllocManager = &mAstAllocManager;
newParser->mFileName = fileName;
newParser->mFileAlreadyVisited = fileAlreadyVisited;
newParser->mAborted = false;
newParser->mCDepChecker = this;
newParser->mCDepFile = cDepFile;
if (dataCDep != NULL)
{
//OutputDebugStrF("CDep Reffing file: %s\n", fileName.c_str());
newParser->RefSource(dataCDep->mFileData.mData, dataCDep->mFileData.mLength);
cDepFile->mFileData = dataCDep->mFileData;
}
else
{
AutoPerf autoPerf("LoadFile LoadTextData", gPerfManagerPtr);
CDepFileData fileData;
fileData.mLength = 0;
#ifdef IDE_C_SUPPORT
if ((contentOverride == NULL) && (gClangUnsavedFiles != NULL))
{
AutoCrit autoCrit(gClangUnsavedFiles->mCritSect);
for (auto& unsavedFile : gClangUnsavedFiles->mUnsavedFiles)
{
if (_stricmp(unsavedFile.Filename, fileName.c_str()) == 0)
{
fileData.mData = new char[unsavedFile.Length + 1];
fileData.mData[unsavedFile.Length] = 0;
memcpy(fileData.mData, unsavedFile.Contents, unsavedFile.Length);
fileData.mLength = unsavedFile.Length;
break;
}
}
}
#endif
if (contentOverride != NULL)
{
newParser->RefSource(contentOverride, (int)strlen(contentOverride));
}
else
{
if (fileData.mData == NULL)
{
fileData.mData = Beefy::LoadTextData(fileName, &fileData.mLength);
//BF_ASSERT(fileData.mData != NULL);
}
if (fileData.mData != NULL)
newParser->RefSource(fileData.mData, fileData.mLength);
}
//OutputDebugStrF("CDep Loading file: %s\n", fileName.c_str());
cDepFile->mFileData = fileData;
if (fileData.mData != NULL)
mDepFileData.push_back(fileData);
}
int maxIncludeDepth = 256;
#ifdef _DEBUG
maxIncludeDepth = 64;
#endif
if ((int)gCDepChecker->mIncludeStack.size() >= maxIncludeDepth) // Include stack blown?
mAbort = true;
gCDepChecker->mIncludeStack.push_back(cDepFile);
{
AutoPerf autoPerf("LoadFile Parse", gPerfManagerPtr);
if (newParser->mSrc != NULL)
newParser->Parse(&bfPassInstance);
newParser->Close();
}
gCDepChecker->mIncludeStack.pop_back();
//bool wasAborted = newParser->mAborted;
bool wasAborted = false;
//cDepPreprocState->mParsers.push_back(newParser);
cDepFile->mProcessing = false;
// We need to remove this because it wasn't really processed and won't have mFilesReferenced and such filled in
if (wasAborted)
{
delete cDepFile;
//auto itr = mDepFileMap.find(val128);
mDepFileMap.erase(depFileItr);
return NULL;
}
else
{
for (auto deferredFile : cDepFile->mDeferredDepSet)
{
for (auto refFile : cDepFile->mFilesReferenced)
deferredFile->mFilesReferenced.insert(refFile);
}
}
return cDepFile;
}
void CDepChecker::ClearCache()
{
mCPPCount = 0;
for (auto pair : mDepFileMap)
{
delete pair.second;
}
mDepFileMap.clear();
for (auto& depFileData : mDepFileData)
{
delete depFileData.mData;
}
mDepFileData.clear();
mFileExistsCache.clear();
}
bool CDepChecker::CachedFileExists(const StringImpl& filePath)
{
AutoPerf autoPerf("CachedFileExists", gPerfManagerPtr);
auto itr = mFileExistsCache.find(filePath);
if (itr != mFileExistsCache.end())
return itr->second;
auto fileExists = FileExists(filePath);
mFileExistsCache[filePath] = fileExists;
return fileExists;
}
//////////////////////////////////////////////////////////////////////////
/// Get the value the ATOMIC_*_LOCK_FREE macro should have for a type with
/// the specified properties.
static int GetLockFreeValue(int typeWidth, int typeAlign, int inlineWidth)
{
// Fully-aligned, power-of-2 sizes no larger than the inline
// width will be inlined as lock-free operations.
if (typeWidth == typeAlign && (typeWidth & (typeWidth - 1)) == 0 &&
typeWidth <= inlineWidth)
return 2; // "always lock free"
// We cannot be certain what operations the lib calls might be
// able to implement as lock-free on future processors.
return 1; // "sometimes lock free"
}
static void FixFilePath(String& fileName)
{
for (int i = 0; i < (int)fileName.length(); i++)
if (fileName[i] == DIR_SEP_CHAR_ALT)
fileName[i] = DIR_SEP_CHAR;
#ifdef _WIN32
else
fileName[i] = (char)::toupper((uint8)fileName[i]);
#endif
}
static int gCheckCount = 0;
BF_EXPORT const char* BF_CALLTYPE CDep_DetermineFilesReferenced(const char* fileNamePtr, const char* cArgs)
{
String& outString = *gTLStrReturn.Get();
gCheckCount++;
if (gCDepChecker == NULL)
gCDepChecker = new CDepChecker();
gCDepChecker->mCPPCount++;
gPerfManagerPtr = &gCDepPerfManager;
gCDepChecker->SetCArgs(cArgs);
//gPerfManagerPtr->StartRecording();
String fileName = fileNamePtr;
FixFilePath(fileName);
bool isFromCache;
CDepFile* depFile = gCDepChecker->LoadFile(fileName, NULL, &isFromCache);
BF_ASSERT(gCDepChecker->mIncludeStack.size() == 0);
outString.clear();
for (auto fileRef : depFile->mFilesReferenced)
{
if (!outString.empty())
outString += "\n";
outString += fileRef;
}
//OutputDebugStrF("%s Files Referenced: %s\n", fileNamePtr, outString.c_str());
/*if ((_stricmp(fileNamePtr, "C:/Beef/IDE/mintest/cpp/test1.cpp") == 0) || (stricmp(fileNamePtr, "C:/Beef/IDE/mintest/cpp/test2.cpp") == 0))
{
//TODO: Temporary for testing
BF_ASSERT(depFile->mFilesReferenced.size() > 1);
}*/
if (gPerfManagerPtr->IsRecording())
{
gPerfManagerPtr->StopRecording();
gPerfManagerPtr->DbgPrint();
}
gCDepChecker->mAbort = false;
gCDepChecker->mIncludeDirs.clear();
gCDepChecker->mFilesVisited.clear();
return outString.c_str();
}
BF_EXPORT int BF_CALLTYPE CDep_GetIncludePosition(const char* sourceFileName, const char* sourceContent, const char* headerFileNamePtr, const char* cArgs)
{
if (gCDepChecker == NULL)
gCDepChecker = new CDepChecker();
gPerfManagerPtr = &gCDepPerfManager;
gCDepChecker->SetCArgs(cArgs);
String fileName = sourceFileName;
FixFilePath(fileName);
String headerFileName = headerFileNamePtr;
FixFilePath(headerFileName);
gCDepChecker->mFindHeaderFileName = headerFileName;
gCDepChecker->mFindHeaderIdx = -1;
bool isFromCache;
CDepFile* depFile = gCDepChecker->LoadFile(fileName, NULL, &isFromCache, sourceContent);
BF_ASSERT(gCDepChecker->mIncludeStack.size() == 0);
gCDepChecker->mAbort = false;
gCDepChecker->mIncludeDirs.clear();
gCDepChecker->mFilesVisited.clear();
gCDepChecker->mFindHeaderFileName.clear();
gCDepChecker->ClearCache();
return gCDepChecker->mFindHeaderIdx;
}
void BfFullReportMemory();
BF_EXPORT void BF_CALLTYPE CDep_ClearCache()
{
if (gCDepChecker != NULL)
gCDepChecker->ClearCache();
}
BF_EXPORT void BF_CALLTYPE CDep_Shutdown()
{
delete gCDepChecker;
gCDepChecker = NULL;
}
/*BF_EXPORT const char* BF_CALLTYPE CDep_DetermineFilesReferenced(const char* fileNamePtr, const char* cArgs)
{
for (int i = 0; i < 20; i++)
{
zCDep_DetermineFilesReferenced(fileNamePtr, cArgs);
CDep_ClearCache();
}
return zCDep_DetermineFilesReferenced(fileNamePtr, cArgs);
}
*/