summaryrefslogtreecommitdiffstats
path: root/lib/cppparser/lexercache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cppparser/lexercache.cpp')
-rw-r--r--lib/cppparser/lexercache.cpp249
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 : 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.
+}