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/languages/cpp/backgroundparser.cpp

553 lines
14 KiB

/***************************************************************************
* 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;