diff options
Diffstat (limited to 'languages/cpp/backgroundparser.cpp')
-rw-r--r-- | languages/cpp/backgroundparser.cpp | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/languages/cpp/backgroundparser.cpp b/languages/cpp/backgroundparser.cpp new file mode 100644 index 00000000..ed51d0c6 --- /dev/null +++ b/languages/cpp/backgroundparser.cpp @@ -0,0 +1,552 @@ +/*************************************************************************** +* Copyright (C) 2002 by Roberto Raggi * +* roberto@kdevelop.org * +* * +* 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 "backgroundparser.h" +#include "cppsupportpart.h" +#include "cppsupport_events.h" +#include "codeinformationrepository.h" +#include "cppcodecompletion.h" +#include "ast_utils.h" +#include "kdevdeepcopy.h" +#include "kdevdriver.h" + +#include <qmutex.h> + +#include <kparts/part.h> +#include <ktexteditor/editinterface.h> +#include <ktexteditor/document.h> +#include <ktexteditor/view.h> + +#include <kdevpartcontroller.h> +#include <kdevproject.h> + +#include <kurl.h> +#include <kdebug.h> +#include <kapplication.h> + +#include <qfile.h> +#include <qfileinfo.h> +#include <qtextstream.h> +#include <list> +#include <qdatastream.h> + + +class BackgroundKDevDriver : public KDevDriver { +public: + BackgroundKDevDriver( CppSupportPart* cppSupport, BackgroundParser* bp ) : KDevDriver( cppSupport, false ), m_backgroundParser(bp) { + } + virtual void fileParsed( ParsedFile& fileName ); + virtual void addDependence( const QString& fileName, const Dependence& dep ); +private: + BackgroundParser* m_backgroundParser; +}; + + +class KDevSourceProvider: public SourceProvider +{ +public: + //Deadlock is a mutex that is locked when KDevSourceProvider::contents(..) is used, and that should be unlocked before QApplication is locked(that way a deadlock where the thread that holds the QApplication-mutex and tries to lock the given mutex, while the thread that calls contents(..) and holds the given mutex and tries to lock the QApplication-mutex, cannot happen) + KDevSourceProvider( CppSupportPart* cppSupport, QMutex& deadlock ) + : m_cppSupport( cppSupport ), + m_readFromDisk( false ), + m_deadlock(deadlock) + {} + + void setReadFromDisk( bool b ) + { + m_readFromDisk = b; + } + bool readFromDisk() const + { + return m_readFromDisk; + } + + virtual QString contents( const QString& fileName ) + { + QString contents = QString::null; + + if ( !m_readFromDisk ) + { + m_deadlock.unlock(); + // GET LOCK + kapp->lock (); + + //kdDebug(9007) << "-------> kapp locked" << endl; + + QPtrList<KParts::Part> parts( *m_cppSupport->partController() ->parts() ); + QPtrListIterator<KParts::Part> it( parts ); + while ( it.current() ) + { + KTextEditor::Document * doc = dynamic_cast<KTextEditor::Document*>( it.current() ); + ++it; + + KTextEditor::EditInterface* editIface = dynamic_cast<KTextEditor::EditInterface*>( doc ); + if ( !doc || !editIface || doc->url().path() != fileName ) + continue; + + contents = QString( editIface->text().ascii() ); // deep copy + + //kdDebug(9007) << "-------> kapp unlocked" << endl; + + break; + } + + // RELEASE LOCK + kapp->unlock(); + m_deadlock.lock(); + //kdDebug(9007) << "-------> kapp unlocked" << endl; + } + + if( m_readFromDisk || contents == QString::null ) + { + QFile f( fileName ); + if ( f.open( IO_ReadOnly ) ) + { + QTextStream stream( &f ); + contents = stream.read(); + f.close(); + } + } + + return contents; + } + + virtual bool isModified( const QString& fileName ) + { + bool ret = false; + m_deadlock.unlock(); + kapp->lock (); + + KParts::ReadOnlyPart *part = m_cppSupport->partController()->partForURL( KURL(fileName) ); + KTextEditor::Document * doc = dynamic_cast<KTextEditor::Document*>( part ); + + if ( doc ) + ret = doc->isModified(); + + kapp->unlock(); + m_deadlock.lock(); + return ret; + } + +private: + CppSupportPart* m_cppSupport; + bool m_readFromDisk; + QMutex& m_deadlock; +private: + KDevSourceProvider( const KDevSourceProvider& source ); + void operator = ( const KDevSourceProvider& source ); +}; + +typedef std::string SafeString; + +class SynchronizedFileList +{ + typedef std::list< QPair<SafeString, bool> > ListType; +public: + SynchronizedFileList() + {} + + bool isEmpty() const + { + QMutexLocker locker( &m_mutex ); + return m_fileList.empty(); + } + + uint count() const + { + QMutexLocker locker( &m_mutex ); + return m_fileList.size(); + } + + QPair<SafeString, bool> front() const + { + QMutexLocker locker( &m_mutex ); + return m_fileList.front(); + } + + void clear() + { + QMutexLocker locker( &m_mutex ); + m_fileList.clear(); + } + + void push_front( const QString& fileName, bool readFromDisk = false ) + { + SafeString s( fileName.ascii() ); + QMutexLocker locker( &m_mutex ); + m_fileList.push_front( qMakePair( s, readFromDisk ) ); + } + + void push_back( const QString& fileName, bool readFromDisk = false ) + { + SafeString s( fileName.ascii() ); + QMutexLocker locker( &m_mutex ); + m_fileList.push_back( qMakePair( s, readFromDisk ) ); + } + + void pop_front() + { + QMutexLocker locker( &m_mutex ); + m_fileList.pop_front(); + } + + int count( const QString& fileName ) const { + int c = 0; + + QMutexLocker locker( &m_mutex ); + ListType::const_iterator it = m_fileList.begin(); + while ( it != m_fileList.end() ) + { + if ( ( *it ).first.compare( fileName.ascii() ) == 0 ) + ++c; + ++it; + } + return c; + } + + QPair<SafeString, bool> takeFront() + { + QMutexLocker locker( &m_mutex ); + QPair<SafeString, bool> ret = m_fileList.front(); + m_fileList.pop_front(); + return ret; + } + + bool contains( const QString& fileName ) const + { + QMutexLocker locker( &m_mutex ); + ListType::const_iterator it = m_fileList.begin(); + while ( it != m_fileList.end() ) + { + if ( ( *it ).first.compare( fileName.ascii() ) == 0 ) + return true; + ++it; + } + return false; + } + + void remove( const QString& fileName ) + { + QMutexLocker locker( &m_mutex ); + ListType::iterator it = m_fileList.begin(); + while ( it != m_fileList.end() ) + { + if ( ( *it ).first.compare(fileName.ascii() ) == 0 ) + m_fileList.erase( it++ ); + else + ++it; + } + } + +private: + mutable QMutex m_mutex; + ListType m_fileList; +}; + +BackgroundParser::BackgroundParser( CppSupportPart* part, QWaitCondition* consumed ) +: m_consumed( consumed ), m_cppSupport( part ), m_close( false ), m_saveMemory( false ) +{ + m_fileList = new SynchronizedFileList(); + m_driver = new BackgroundKDevDriver( m_cppSupport, this ); + m_driver->setSourceProvider( new KDevSourceProvider( m_cppSupport, m_mutex ) ); + + QString conf_file_name = m_cppSupport->specialHeaderName(); + m_mutex.lock(); + if ( QFile::exists( conf_file_name ) ) + m_driver->parseFile( conf_file_name, true, true, true ); + m_mutex.unlock(); + + //disabled for now m_driver->setResolveDependencesEnabled( true ); +} + +BackgroundParser::~BackgroundParser() +{ + removeAllFiles(); + + delete( m_driver ); + m_driver = 0; + + delete m_fileList; + m_fileList = 0; +} + +void BackgroundParser::addFile( const QString& fileName, bool readFromDisk ) +{ + QString fn = deepCopy( fileName ); + + //bool added = false; + /*if ( !m_fileList->contains( fn ) ) + { + m_fileList->push_back( fn, readFromDisk ); + added = true; + }*/ + m_fileList->push_back( fn, readFromDisk ); + + //if ( added ) + m_canParse.wakeAll(); +} + +void BackgroundParser::addFileFront( const QString& fileName, bool readFromDisk ) +{ + QString fn = deepCopy( fileName ); + + bool added = false; + /*if ( m_fileList->contains( fn ) ) + m_fileList->remove( fn );*/ + + m_fileList->push_front( fn, readFromDisk ); + added = true; + + if ( added ) + m_canParse.wakeAll(); +} + +void BackgroundParser::removeAllFiles() +{ + kdDebug( 9007 ) << "BackgroundParser::removeAllFiles()" << endl; + QMutexLocker locker( &m_mutex ); + + QMap<QString, Unit*>::Iterator it = m_unitDict.begin(); + while ( it != m_unitDict.end() ) + { + Unit * unit = it.data(); + ++it; + delete( unit ); + unit = 0; + } + m_unitDict.clear(); + m_driver->reset(); + m_fileList->clear(); + + m_isEmpty.wakeAll(); +} + +void BackgroundParser::removeFile( const QString& fileName ) +{ + QMutexLocker locker( &m_mutex ); + + Unit* unit = findUnit( fileName ); + if ( unit ) + { + m_driver->remove + ( fileName ); + m_unitDict.remove( fileName ); + delete( unit ); + unit = 0; + } + + if ( m_fileList->isEmpty() ) + m_isEmpty.wakeAll(); +} + +void BackgroundKDevDriver::addDependence( const QString& fileName, const Dependence& dep ) { + //give waiting threads a chance to perform their actions + m_backgroundParser->m_mutex.unlock(); + m_backgroundParser->m_mutex.lock(); + KDevDriver::addDependence( fileName, dep ); +} + +void BackgroundKDevDriver::fileParsed( ParsedFile& fileName ) { + m_backgroundParser->fileParsed( fileName ); +} + +void BackgroundParser::parseFile( const QString& fileName, bool readFromDisk, bool lock ) +{ + if( lock ) + m_mutex.lock(); + m_readFromDisk = readFromDisk; + static_cast<KDevSourceProvider*>( m_driver->sourceProvider() ) ->setReadFromDisk( readFromDisk ); + + m_driver->remove( fileName ); + m_driver->parseFile( fileName , false, true ); + if( !m_driver->isResolveDependencesEnabled() ) + m_driver->removeAllMacrosInFile( fileName ); // romove all macros defined by this + // translation unit. + if ( lock ) + m_mutex.unlock(); +} + +QValueList<Problem> cloneProblemList( const QValueList<Problem>& list ) { + QValueList<Problem> ret; + for( QValueList<Problem>::const_iterator it = list.begin(); it != list.end(); ++it ) { + ret << Problem( *it, true ); + } + return ret; +} + +void BackgroundParser::fileParsed( ParsedFile& file ) { + + ParsedFilePointer translationUnitUnsafe = m_driver->takeTranslationUnit( file.fileName() ); + //now file and translationUnitUnsafe are the same + ParsedFilePointer translationUnit; + //Since the lexer-cache keeps many QStrings like macro-names used in the background, everything must be copied here. The safest solution is just + //serializing and deserializing the whole thing(the serialization does not respect the AST, but that can be copied later because that's safe) + QMemArray<char> data; + { + QDataStream stream( data, IO_WriteOnly ); + translationUnitUnsafe->write( stream ); + } + { + QDataStream stream( data, IO_ReadOnly ); + translationUnit = new ParsedFile( stream ); + } + + translationUnit->setTranslationUnit( translationUnitUnsafe->operator TranslationUnitAST *() ); //Copy the AST, doing that is thread-safe + translationUnitUnsafe->setTranslationUnit( 0 ); //Move the AST completely out of this thread's scope. Else it might crash on dual-core machines + file.setTranslationUnit(0); //just to be sure, set to zero on both + + Unit* unit = new Unit; + unit->fileName = file.fileName(); + unit->translationUnit = translationUnit; + unit->problems = cloneProblemList( m_driver->problems( file.fileName() ) ); + + static_cast<KDevSourceProvider*>( m_driver->sourceProvider() ) ->setReadFromDisk( false ); + + if ( m_unitDict.find( file.fileName() ) != m_unitDict.end() ) + { + Unit * u = m_unitDict[ file.fileName() ]; + m_unitDict.remove( file.fileName() ); + delete( u ); + u = 0; + } + + m_unitDict.insert( file.fileName(), unit ); + + KApplication::postEvent( m_cppSupport, new FileParsedEvent( file.fileName(), unit->problems, m_readFromDisk ) ); + + m_currentFile = QString::null; + + if ( m_fileList->isEmpty() ) + m_isEmpty.wakeAll(); +} + +Unit* BackgroundParser::findUnit( const QString& fileName ) +{ + QMap<QString, Unit*>::Iterator it = m_unitDict.find( fileName ); + return it != m_unitDict.end() ? *it : 0; +} + +bool BackgroundParser::hasTranslationUnit( const QString& fileName ) { + QMap<QString, Unit*>::Iterator it = m_unitDict.find( fileName ); + return it != m_unitDict.end(); +} + +ParsedFilePointer BackgroundParser::translationUnit( const QString& fileName ) +{ + Unit * u = findUnit( fileName ); + if ( u == 0 ) + { + return 0; + /*m_fileList->remove + ( fileName ); + u = parseFile( fileName, false );*/ + } + + return u->translationUnit; +} + +QValueList<Problem> BackgroundParser::problems( const QString& fileName, bool readFromDisk, bool forceParse ) +{ + Q_UNUSED(readFromDisk); + Unit * u = findUnit( fileName ); + if ( u == 0 || forceParse ) + { + /* + m_fileList->remove + ( fileName ); + u = parseFile( fileName, readFromDisk ); */ + } + + return u ? u->problems : QValueList<Problem>(); +} + +void BackgroundParser::close() +{ + { + QMutexLocker locker( &m_mutex ); + m_close = true; + m_canParse.wakeAll(); + } + kapp->unlock(); + + while ( running() ) + sleep( 1 ); + kapp->lock(); +} + +bool BackgroundParser::filesInQueue() +{ + QMutexLocker locker( &m_mutex ); + + return m_fileList->count() || !m_currentFile.isEmpty(); +} + +int BackgroundParser::countInQueue( const QString& file ) const { + return m_fileList->count( file ); +} + +void BackgroundParser::updateParserConfiguration() +{ + QMutexLocker locker( &m_mutex ); + + m_driver->setup(); + QString conf_file_name = m_cppSupport->specialHeaderName(); + m_driver->removeAllMacrosInFile( conf_file_name ); + m_driver->parseFile( conf_file_name, true, true, true ); +} + +void BackgroundParser::run() +{ + // (void) m_cppSupport->codeCompletion()->repository()->getEntriesInScope( QStringList(), false ); + + while ( !m_close ) + { + + while ( m_fileList->isEmpty() ) + { + if( m_saveMemory ) { + m_saveMemory = false; + m_driver->lexerCache()->saveMemory(); + } + + m_canParse.wait(); + + if ( m_close ) + break; + } + + if ( m_close ) + break; + + QPair<SafeString, bool> entry = m_fileList->takeFront(); + QString fileName = entry.first.c_str(); + bool readFromDisk = entry.second; + m_currentFile = deepCopy(fileName); + + ( void ) parseFile( fileName, readFromDisk, true ); + + + m_currentFile = QString::null; + } + + kdDebug( 9007 ) << "!!!!!!!!!!!!!!!!!! BG PARSER DESTROYED !!!!!!!!!!!!" << endl; + +// adymo: commented to fix #88091 +// QThread::exit(); +} + +void BackgroundParser::saveMemory() { + m_saveMemory = true; //Delay the operation + m_canParse.wakeAll(); +} + + +//kate: indent-mode csands; tab-width 4; space-indent off; |