diff options
Diffstat (limited to 'lib/cppparser/lexercache.cpp')
-rw-r--r-- | lib/cppparser/lexercache.cpp | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/lib/cppparser/lexercache.cpp b/lib/cppparser/lexercache.cpp new file mode 100644 index 00000000..a7e40dd6 --- /dev/null +++ b/lib/cppparser/lexercache.cpp @@ -0,0 +1,249 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : [email protected] +***************************************************************************/ + +/*************************************************************************** + * * + * 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. +} |