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.
tdevelop/lib/cppparser/lexercache.cpp

250 lines
10 KiB

/***************************************************************************
copyright : (C) 2006 by David Nolden
email : david.nolden.kdevelop@art-master.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "lexercache.h"
#include "driver.h"
#include <kdebug.h>
LexerCache::LexerCache( Driver* d ) : m_driver( d ) {}
void LexerCache::addLexedFile( const CachedLexedFilePointer& file ) {
//kdDebug( 9007 ) << "LexerCache: adding an instance of " << file->fileName().str() << endl;
std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( file->fileName() );
if ( files.first == files.second ) {
m_files.insert( std::make_pair( file->fileName(), file ) );
} else {
//Make sure newer files appear first
m_files.insert( files.first, std::make_pair( file->fileName(), file ) );
}
int cnt = 0;
while ( files.first != files.second ) {
if ( sourceChanged( *( *( files.first ) ).second ) ) {
m_files.erase( files.first++ );
} else {
cnt++;
files.first++;
}
}
//kdDebug( 9007 ) << "LexerCache: new count of cached instances for the file: " << cnt << endl;
}
CachedLexedFilePointer LexerCache::lexedFile( const HashedString& fileName ) {
initFileModificationCache();
std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( fileName );
///@todo optimize with standard-algorithms(by first computing the intersection)
/* if( files.first != files.second )
//kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is not empty" << endl;
else
//kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is empty" << endl;*/
while ( files.first != files.second ) {
const CachedLexedFile& file( *( *( files.first ) ).second );
if ( sourceChanged( file ) ) {
//kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is being discarded because the file was modified" << endl;
m_files.erase( files.first++ );
continue;
}
bool success = true;
//Make sure that none of the macros stored in the driver affect the file in a different way than the one before
Driver::MacroMap::const_iterator end = m_driver->macros().end();
for ( Driver::MacroMap::const_iterator rit = m_driver->macros().begin(); rit != end; ) {
Driver::MacroMap::const_iterator it = rit;
++rit;
if ( rit != end && ( *it ).first == ( *rit ).first ) continue; //Always only use the last macro of the same name for comparison, it is on top of the macro-stack
if (( *it ).second.isUndef() ) continue; //Undef-macros theoretically don't exist
if ( file.hasString(( *it ).first ) ) {
if ( file.m_usedMacros.hasMacro(( *it ).first ) ) {
Macro m( file.m_usedMacros.macro(( *it ).first.str() ) );
if ( !( m == ( *it ).second ) ) {
//kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " depends on the string \"" << ( *it ).first.str() << "\" and used a macro for it with the body \"" << m.body() << "\"(from " << m.fileName() << "), but the driver contains the same macro with body \"" << ( *it ).second.body() << "\"(from " << ( *it ).second.fileName() << "), cache is not used" << endl;
//Macro with the same name was used, but it is different
success = false;
break;
}
} else {
//There is a macro that affects the file, but was not used while the previous parse
//kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " depends on the string \"" << ( *it ).first.str() << "\" and the driver contains a macro of that name with body \"" << ( *it ).second.body() << "\"(from " << ( *it ).second.fileName() << "), the cached file is not used" << endl;
success = false;
break;
}
}
}
//Make sure that all external macros used by the file now exist too
MacroSet::Macros::const_iterator end2 = file.usedMacros().macros().end();
for ( MacroSet::Macros::const_iterator it = file.usedMacros().macros().begin(); it != end2; ++it ) {
if ( !m_driver->hasMacro( HashedString(( *it ).name() ) ) ) {
//kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " used a macro called \"" << it->name() << "\"(from " << it->fileName() << "), but the driver does not contain that macro, the cached file is not used" << endl;
success = false;
break;
}
}
if ( success ) {
//kdDebug( 9007 ) << "LexerCache: Using cached file " << fileName.str() << endl;
(*files.first).second->access();
return ( *files.first ).second;
}
++files.first;
}
return CachedLexedFilePointer();
}
QDateTime LexerCache::fileModificationTimeCached( const HashedString& fileName ) {
FileModificationMap::const_iterator it = m_fileModificationCache.find( fileName );
if( it != m_fileModificationCache.end() ) {
///Use the cache for 10 seconds
if( (*it).second.m_readTime.secsTo( m_currentDateTime ) < 10 ) {
return (*it).second.m_modificationTime;
}
}
QFileInfo fileInfo( fileName.str() );
m_fileModificationCache[fileName].m_readTime = QDateTime::currentDateTime();
m_fileModificationCache[fileName].m_modificationTime = fileInfo.lastModified();
return fileInfo.lastModified();
}
//Should be cached too!
bool LexerCache::sourceChanged( const CachedLexedFile& file ) {
//@todo Check if any of the dependencies changed
QDateTime modTime = fileModificationTimeCached( file.fileName() );
if ( modTime != file.modificationTime() )
return true;
for( QMap<HashedString, QDateTime>::const_iterator it = file.allModificationTimes().begin(); it != file.allModificationTimes().end(); ++it ) {
QDateTime modTime = fileModificationTimeCached( it.key() );
if( modTime != *it )
return true;
}
return false;
}
void LexerCache::clear() {
m_files.clear();
m_totalStringSet.clear();
m_fileModificationCache.clear();
}
void LexerCache::erase( const CacheNode* node ) {
std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( ((const CachedLexedFile*)(node))->fileName() );
while ( files.first != files.second ) {
if( (*files.first).second.data() == ((const CachedLexedFile*)(node)) ) {
m_files.erase( files.first );
return;
}
files.first++;
}
//kdDebug( 9007 ) << "Error: could not find a node in the list for file " << ((const CachedLexedFile*)(node))->fileName().str() << endl;
}
CachedLexedFile::CachedLexedFile( const HashedString& fileName, LexerCache* manager ) : CacheNode( manager ), m_fileName( fileName ) {
QFileInfo fileInfo( fileName.str() );
m_modificationTime = fileInfo.lastModified();
m_allModificationTimes[ fileName ] = m_modificationTime;
}
void CachedLexedFile::addDefinedMacro( const Macro& macro ) {
#ifdef LEXERCACHE_DEBUG
//kdDebug( 9007 ) << "defined macro " << macro.name() << endl;
#endif
m_definedMacros.addMacro( macro );
m_definedMacroNames.insert( HashedString( macro.name() ) );
}
void CachedLexedFile::addUsedMacro( const Macro& macro ) {
if ( !m_definedMacros.hasMacro( macro.name() ) ) {
#ifdef LEXERCACHE_DEBUG
//kdDebug( 9007 ) << "used macro " << macro.name() << endl;
#endif
m_usedMacros.addMacro( macro );
}
}
void CachedLexedFile::addIncludeFile( const HashedString& file, const QDateTime& modificationTime ) {
m_includeFiles.insert( file );
m_allModificationTimes[file] = modificationTime;
}
QDateTime CachedLexedFile::modificationTime() const {
return m_modificationTime;
}
void CachedLexedFile::addProblem( const Problem& p ) {
m_problems << p;
}
QValueList<Problem> CachedLexedFile::problems() const {
return m_problems;
}
//The parameter should be a CachedLexedFile that was lexed AFTER the content of this file
void CachedLexedFile::merge( const CachedLexedFile& file ) {
#ifdef LEXERCACHE_DEBUG
//kdDebug( 9007 ) << fileName().str() << ": merging " << file.fileName().str() << endl << "defined in this: " << m_definedMacroNames.print().c_str() << endl << "defined macros in other: " << file.m_definedMacroNames.print().c_str() << endl;;
#endif
HashedStringSet tempStrings = file.m_strings;
tempStrings -= m_definedMacroNames;
m_strings += tempStrings;
m_includeFiles += file.m_includeFiles;
//Only add macros to the usedMacros-list that were not defined locally
for ( MacroSet::Macros::const_iterator it = file.m_usedMacros.macros().begin(); it != file.m_usedMacros.macros().end(); ++it ) {
if ( !m_definedMacros.hasMacro(( *it ).name() ) ) {///If the macro was not defined locally, add it to the macros-list.
#ifdef LEXERCACHE_DEBUG
//kdDebug( 9007 ) << "inserting used macro " << ( *it ).name() << endl;
#endif
m_usedMacros.addMacro( *it );
}
}
m_definedMacros.merge( file.m_definedMacros );
m_definedMacroNames += file.m_definedMacroNames;
for( QMap<HashedString, QDateTime>::const_iterator it = file.m_allModificationTimes.begin(); it != file.m_allModificationTimes.end(); ++it )
m_allModificationTimes[it.key()] = *it;
#ifdef LEXERCACHE_DEBUG
//kdDebug( 9007 ) << fileName().str() << ": defined in this after merge: " << m_definedMacroNames.print().c_str() << endl;
#endif
m_problems += file.m_problems;
}
size_t CachedLexedFile::hash() const {
return m_usedMacros.valueHash() + m_usedMacros.idHash() + m_definedMacros.idHash() + m_definedMacros.valueHash() + m_strings.hash();
}
void LexerCache::initFileModificationCache() {
m_currentDateTime = QDateTime::currentDateTime();
}
void LexerCache::saveMemory() {
m_fileModificationCache.clear();
m_totalStringSet.clear(); ///it's unclear how often this should be emptied. It may happen that completely unused strings end up in this set, then deleting it will save us memory.
}