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.
250 lines
10 KiB
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.
|
|
}
|