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

550 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 <tqmutex.h>
#include <tdeparts/part.h>
#include <tdetexteditor/editinterface.h>
#include <tdetexteditor/document.h>
#include <tdetexteditor/view.h>
#include <kdevpartcontroller.h>
#include <kdevproject.h>
#include <kurl.h>
#include <kdebug.h>
#include <tdeapplication.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqtextstream.h>
#include <list>
#include <tqdatastream.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 TQString& 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 TQApplication is locked(that way a deadlock where the thread that holds the TQApplication-mutex and tries to lock the given mutex, while the thread that calls contents(..) and holds the given mutex and tries to lock the TQApplication-mutex, cannot happen)
KDevSourceProvider( CppSupportPart* cppSupport, TQMutex& deadlock )
: m_cppSupport( cppSupport ),
m_readFromDisk( false ),
m_deadlock(deadlock)
{}
void setReadFromDisk( bool b )
{
m_readFromDisk = b;
}
bool readFromDisk() const
{
return m_readFromDisk;
}
virtual TQString contents( const TQString& fileName )
{
TQString contents = TQString();
if ( !m_readFromDisk )
{
m_deadlock.unlock();
// GET LOCK
kapp->lock ();
//kdDebug(9007) << "-------> kapp locked" << endl;
TQPtrList<KParts::Part> parts( *m_cppSupport->partController() ->parts() );
TQPtrListIterator<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 = TQString( 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 == TQString() )
{
TQFile f( fileName );
if ( f.open( IO_ReadOnly ) )
{
TQTextStream stream( &f );
contents = stream.read();
f.close();
}
}
return contents;
}
virtual bool isModified( const TQString& 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;
TQMutex& m_deadlock;
private:
KDevSourceProvider( const KDevSourceProvider& source );
void operator = ( const KDevSourceProvider& source );
};
typedef std::string SafeString;
class SynchronizedFileList
{
typedef std::list< TQPair<SafeString, bool> > ListType;
public:
SynchronizedFileList()
{}
bool isEmpty() const
{
TQMutexLocker locker( &m_mutex );
return m_fileList.empty();
}
uint count() const
{
TQMutexLocker locker( &m_mutex );
return m_fileList.size();
}
TQPair<SafeString, bool> front() const
{
TQMutexLocker locker( &m_mutex );
return m_fileList.front();
}
void clear()
{
TQMutexLocker locker( &m_mutex );
m_fileList.clear();
}
void push_front( const TQString& fileName, bool readFromDisk = false )
{
SafeString s( fileName.ascii() );
TQMutexLocker locker( &m_mutex );
m_fileList.push_front( qMakePair( s, readFromDisk ) );
}
void push_back( const TQString& fileName, bool readFromDisk = false )
{
SafeString s( fileName.ascii() );
TQMutexLocker locker( &m_mutex );
m_fileList.push_back( qMakePair( s, readFromDisk ) );
}
void pop_front()
{
TQMutexLocker locker( &m_mutex );
m_fileList.pop_front();
}
int count( const TQString& fileName ) const {
int c = 0;
TQMutexLocker 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;
}
TQPair<SafeString, bool> takeFront()
{
TQMutexLocker locker( &m_mutex );
TQPair<SafeString, bool> ret = m_fileList.front();
m_fileList.pop_front();
return ret;
}
bool contains( const TQString& fileName ) const
{
TQMutexLocker 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 TQString& fileName )
{
TQMutexLocker 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 TQMutex m_mutex;
ListType m_fileList;
};
BackgroundParser::BackgroundParser( CppSupportPart* part, TQWaitCondition* 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 ) );
TQString conf_file_name = m_cppSupport->specialHeaderName();
m_mutex.lock();
if ( TQFile::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 TQString& fileName, bool readFromDisk )
{
TQString 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 TQString& fileName, bool readFromDisk )
{
TQString 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;
TQMutexLocker locker( &m_mutex );
TQMap<TQString, 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 TQString& fileName )
{
TQMutexLocker 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 TQString& 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 TQString& 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();
}
TQValueList<Problem> cloneProblemList( const TQValueList<Problem>& list ) {
TQValueList<Problem> ret;
for( TQValueList<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 TQStrings 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)
TQMemArray<char> data;
{
TQDataStream stream( TQByteArray(data), IO_WriteOnly );
translationUnitUnsafe->write( stream );
}
{
TQDataStream stream( TQByteArray(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 );
TDEApplication::postEvent( m_cppSupport, new FileParsedEvent( file.fileName(), unit->problems, m_readFromDisk ) );
m_currentFile = TQString();
if ( m_fileList->isEmpty() )
m_isEmpty.wakeAll();
}
Unit* BackgroundParser::findUnit( const TQString& fileName )
{
TQMap<TQString, Unit*>::Iterator it = m_unitDict.find( fileName );
return it != m_unitDict.end() ? *it : 0;
}
bool BackgroundParser::hasTranslationUnit( const TQString& fileName ) {
TQMap<TQString, Unit*>::Iterator it = m_unitDict.find( fileName );
return it != m_unitDict.end();
}
ParsedFilePointer BackgroundParser::translationUnit( const TQString& fileName )
{
Unit * u = findUnit( fileName );
if ( u == 0 )
{
return 0;
/*m_fileList->remove
( fileName );
u = parseFile( fileName, false );*/
}
return u->translationUnit;
}
TQValueList<Problem> BackgroundParser::problems( const TQString& 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 : TQValueList<Problem>();
}
void BackgroundParser::close()
{
{
TQMutexLocker locker( &m_mutex );
m_close = true;
m_canParse.wakeAll();
}
kapp->unlock();
while ( running() )
sleep( 1 );
kapp->lock();
}
bool BackgroundParser::filesInQueue()
{
TQMutexLocker locker( &m_mutex );
return m_fileList->count() || !m_currentFile.isEmpty();
}
int BackgroundParser::countInQueue( const TQString& file ) const {
return m_fileList->count( file );
}
void BackgroundParser::updateParserConfiguration()
{
TQMutexLocker locker( &m_mutex );
m_driver->setup();
TQString 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( TQStringList(), 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;
TQPair<SafeString, bool> entry = m_fileList->takeFront();
TQString fileName = entry.first.c_str();
bool readFromDisk = entry.second;
m_currentFile = deepCopy(fileName);
( void ) parseFile( fileName, readFromDisk, true );
m_currentFile = TQString();
}
kdDebug( 9007 ) << "!!!!!!!!!!!!!!!!!! BG PARSER DESTROYED !!!!!!!!!!!!" << endl;
// adymo: commented to fix #88091
// TQThread::exit();
}
void BackgroundParser::saveMemory() {
m_saveMemory = true; //Delay the operation
m_canParse.wakeAll();
}