diff --git a/language/duchain/duchainlock.cpp b/language/duchain/duchainlock.cpp index 2f73ddd295..c4ee10352b 100644 --- a/language/duchain/duchainlock.cpp +++ b/language/duchain/duchainlock.cpp @@ -1,407 +1,407 @@ /* This file is part of KDevelop Copyright 2007 Kris Wong Copyright 2007 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "duchainlock.h" #include #include #include #include #include #include #include #include // Uncomment the following to turn on verbose locking information //#define DUCHAIN_LOCK_VERBOSE_OUTPUT // Uncomment this to enable checking for long lock times. It adds significant performance cost though. //#define DEBUG_LOG_TIMING // When uncommented, a backtrace will be printed whenever a too long lock-time is discovered //#define DEBUG_LOG_BACKTRACE -//If DEBUG_LOG_TIMING is uncommented, and the duchain is locked for more then this count of milliseconds, a message is printed +//If DEBUG_LOG_TIMING is uncommented, and the duchain is locked for more than this count of milliseconds, a message is printed #define LOCK_LOG_MILLISECONDS 1000 //If this is uncommented, backtraces are produced whenever the duchain is read-locked, and shows it when the same thread tries to get a write-lock, triggering an assertion. //Very expensive! //#define DEBUG_ASSERTION_BACKTRACES //Uncomment this to search for Deadlocks. DEBUG_LOG_TIMING must be enabled too, and DEBUG_LOG_BACKTRACE is recommended //Warning: this may result in a total spamming of the command-line. //#define SEARCH_DEADLOCKS #include namespace std { #if defined(Q_OS_WIN) using namespace stdext; #else using namespace __gnu_cxx; #endif } namespace KDevelop { class DUChainLockPrivate { public: DUChainLockPrivate() { m_writer = 0; m_writerRecursion = 0; m_totalReaderRecursion = 0; m_readers.set_empty_key(0); //Assuming that no thread can ever have handle zero m_readersEnd = m_readers.end(); //This is used to speedup currentThreadHasReadLock() } /** * Returns true if there is no reader that is not this thread. * */ bool haveOtherReaders() const { ///Since m_totalReaderRecursion is the sum of all reader-recursions, it will be same if either there is no reader at all, or if this thread is the only reader. return m_totalReaderRecursion != ownReaderRecursion(); } int ownReaderRecursion() const { int ownReaderRecursion = 0; ReaderMap::const_iterator it = m_readers.find( QThread::currentThreadId() ); if( it != m_readersEnd ) ownReaderRecursion = (*it).second; return ownReaderRecursion; } #ifdef DEBUG_LOG_TIMING inline void auditTime() const { int ms = m_lockTime.elapsed(); if (ms > LOCK_LOG_MILLISECONDS) { #ifndef DEBUG_LOG_BACKTRACE kWarning(9512) << "Long lock time:" << ms << "milliseconds." ; #else kWarning(9512) << "Long lock time:" << ms << "milliseconds, locked from:\n" << m_lockBacktrace ; #endif } } inline void startLockTiming() { m_lockTime.start(); #ifdef DEBUG_LOG_BACKTRACE m_lockBacktrace = kBacktrace(); #endif } #endif QMutex m_mutex; Qt::HANDLE m_writer; int m_writerRecursion; ///How often is the chain write-locked by the writer? int m_totalReaderRecursion; ///How often is the chain read-locked recursively by all readers? Should be sum of all m_reader values QWaitCondition m_waitForWriter; ///Map readers to the count of recursive read-locks they hold(0 if they do not hold a lock) typedef google::dense_hash_map ReaderMap; ReaderMap m_readers; DUChainLockPrivate::ReaderMap::const_iterator m_readersEnd; //Must always be updated when a new reader was added #ifdef DEBUG_LOG_TIMING QTime m_lockTime; #ifdef DEBUG_LOG_BACKTRACE QString m_lockBacktrace; #endif #endif #ifdef DEBUG_ASSERTION_BACKTRACES QHash > m_readerBacktraces; #endif }; class DUChainWriteLockerPrivate { public: DUChainWriteLockerPrivate() : m_locked(false) { } DUChainLock* m_lock; bool m_locked; int m_timeout; }; DUChainLock::DUChainLock() : d(new DUChainLockPrivate) { } DUChainLock::~DUChainLock() { delete d; } bool DUChainLock::lockForRead(unsigned int timeout) { #ifdef DUCHAIN_LOCK_VERBOSE_OUTPUT kDebug(9505) << "DUChain read lock requested by thread:" << QThread::currentThreadId(); #endif if(timeout == 0) timeout = 60000; d->m_mutex.lock(); bool locked = false; QTime startTime = QTime::currentTime(); while((d->m_writer && d->m_writer != QThread::currentThreadId()) && (uint)startTime.msecsTo(QTime::currentTime()) < timeout) d->m_waitForWriter.wait(&d->m_mutex, timeout); if (d->m_writer == 0 || d->m_writer == QThread::currentThreadId()) { DUChainLockPrivate::ReaderMap::iterator it = d->m_readers.find( QThread::currentThreadId() ); if ( it != d->m_readers.end() ) { ++(*it).second; } else { d->m_readers.insert( DUChainLockPrivate::ReaderMap::value_type(QThread::currentThreadId(), 1) ); d->m_readersEnd = d->m_readers.end(); } locked = true; } if(locked) { ++d->m_totalReaderRecursion; #ifdef DEBUG_LOCK_TIMING d->startLockTiming(); #endif #ifdef DEBUG_ASSERTION_BACKTRACES d->m_readerBacktraces[QThread::currentThreadId()].push(kBacktrace()); Q_ASSERT(d->m_readerBacktraces[QThread::currentThreadId()].size() == d->m_readers[QThread::currentThreadId()]); #endif } d->m_mutex.unlock(); return locked; } bool DUChainLock::lockForRead() { bool ret = lockForRead(100000); Q_ASSERT(currentThreadHasReadLock()); return ret; } void DUChainLock::releaseReadLock() { #ifdef DUCHAIN_LOCK_VERBOSE_OUTPUT kDebug(9505) << "DUChain read lock released by thread:" << QThread::currentThreadId(); #endif QMutexLocker lock(&d->m_mutex); DUChainLockPrivate::ReaderMap::iterator it = d->m_readers.find( QThread::currentThreadId() ); Q_ASSERT(it != d->m_readers.end()); --(*it).second; Q_ASSERT((*it).second>=0); --d->m_totalReaderRecursion; ///@todo Remove the all readers that do not exist any more at some point(leave other readers there with recursion 0 /// because it is very probable that they will lock again, and not having to re-allocate the bucket might speed up the locking. /* if( *it == 0 ) d->m_readers.erase(it); //Maybe it would even be wise simply leaving it there*/ #ifdef DEBUG_ASSERTION_BACKTRACES d->m_readerBacktraces[QThread::currentThreadId()].pop(); #endif #ifdef DEBUG_LOCK_TIMING d->auditTime(); #endif } bool DUChainLock::currentThreadHasReadLock() { d->m_mutex.lock(); DUChainLockPrivate::ReaderMap::const_iterator it = d->m_readers.find( QThread::currentThreadId() ); bool ret = false; if( it != d->m_readersEnd ) ret = ((*it).second != 0); d->m_mutex.unlock(); return ret; } bool DUChainLock::lockForWrite(uint timeout) { #ifdef DUCHAIN_LOCK_VERBOSE_OUTPUT kDebug(9505) << "DUChain write lock requested by thread:" << QThread::currentThreadId(); kDebug(9505) << "Current backtrace:" << kBacktrace(); #endif if(timeout == 0) timeout = 10000; QMutexLocker lock(&d->m_mutex); //It is not allowed to acquire a write-lock while holding read-lock #ifdef DEBUG_ASSERTION_BACKTRACES if(d->ownReaderRecursion()) kWarning(9505) << "Tried to lock the duchain for writing, but it was already locked for reading here:\n" << d->m_readerBacktraces[QThread::currentThreadId()].top(); #endif Q_ASSERT(d->ownReaderRecursion() == 0); bool locked = false; uint currentTime = 0; //We cannot use m_waitForWriterForWriter here, because we also have to wait for other readers to finish while ( ( (d->m_writer && d->m_writer != QThread::currentThreadId()) || d->haveOtherReaders()) && currentTime < timeout) { lock.unlock(); usleep(10000); currentTime++; lock.relock(); #ifdef DEBUG_LOG_BACKTRACE d->auditTime(); //Search for deadlocks #endif } if ( (d->m_writer == 0 || d->m_writer == QThread::currentThreadId()) && !d->haveOtherReaders()) { d->m_writer = QThread::currentThreadId(); ++d->m_writerRecursion; locked = true; #ifdef DEBUG_LOCK_TIMING d->startLockTiming(); #endif } return locked; } void DUChainLock::releaseWriteLock() { #ifdef DUCHAIN_LOCK_VERBOSE_OUTPUT kDebug(9505) << "DUChain write lock released by thread:" << QThread::currentThreadId(); #endif Q_ASSERT(currentThreadHasWriteLock()); QMutexLocker lock(&d->m_mutex); --d->m_writerRecursion; if( !d->m_writerRecursion ) d->m_writer = 0; d->m_waitForWriter.wakeAll(); #ifdef DEBUG_LOCK_TIMING d->auditTime(); #endif } bool DUChainLock::currentThreadHasWriteLock() { d->m_mutex.lock(); bool ret = d->m_writer == QThread::currentThreadId(); d->m_mutex.unlock(); return ret; } DUChainReadLocker::DUChainReadLocker(DUChainLock* duChainLock, uint timeout) : m_locked(false), m_timeout(timeout) { m_lock = duChainLock; m_timeout = timeout; lock(); } DUChainReadLocker::~DUChainReadLocker() { unlock(); } bool DUChainReadLocker::locked() const { return m_locked; } bool DUChainReadLocker::lock() { if( m_locked ) return true; bool l = false; if (m_lock) { l = m_lock->lockForRead(m_timeout); Q_ASSERT(m_timeout || l); }; m_locked = l; return l; } void DUChainReadLocker::unlock() { if (m_locked && m_lock) { m_lock->releaseReadLock(); m_locked = false; } } DUChainWriteLocker::DUChainWriteLocker(DUChainLock* duChainLock, uint timeout) : d(new DUChainWriteLockerPrivate) { d->m_timeout = timeout; d->m_lock = duChainLock; lock(); } DUChainWriteLocker::~DUChainWriteLocker() { unlock(); delete d; } bool DUChainWriteLocker::lock() { if( d->m_locked ) return true; bool l = false; if (d->m_lock) { l = d->m_lock->lockForWrite(d->m_timeout); Q_ASSERT(d->m_timeout || l); }; d->m_locked = l; return l; } bool DUChainWriteLocker::locked() const { return d->m_locked; } void DUChainWriteLocker::unlock() { if (d->m_locked && d->m_lock) { d->m_lock->releaseWriteLock(); d->m_locked = false; } } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/topducontext.cpp b/language/duchain/topducontext.cpp index cd6f5a537c..fb9e85e440 100644 --- a/language/duchain/topducontext.cpp +++ b/language/duchain/topducontext.cpp @@ -1,1438 +1,1438 @@ /* This is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "topducontext.h" #include #if defined(Q_OS_WIN) #include #else #include #endif #include #include "../editor/hashedstring.h" #include "../interfaces/iproblem.h" #include #include "persistentsymboltable.h" #include "declaration.h" #include "duchain.h" #include "duchainlock.h" #include "parsingenvironment.h" #include "duchainpointer.h" #include "declarationid.h" #include "namespacealiasdeclaration.h" #include "aliasdeclaration.h" #include "abstractfunctiondeclaration.h" #include "uses.h" #include "arrayhelpers.h" #include "topducontextdata.h" #include "duchainregister.h" #include "topducontextdynamicdata.h" using namespace KTextEditor; -//Do visibility-caching when more then X items are found. +//Do visibility-caching when more than X items are found. const uint visibilityCachingMargin = 10; namespace std { #if defined(Q_OS_WIN) using namespace stdext; #else using namespace __gnu_cxx; #endif } namespace KDevelop { ReferencedTopDUContext::ReferencedTopDUContext(TopDUContext* context) : m_topContext(context) { if(m_topContext) DUChain::self()->refCountUp(m_topContext); } ReferencedTopDUContext::ReferencedTopDUContext(IndexedTopDUContext context){ DUChainReadLocker lock(DUChain::lock()); m_topContext = context.data(); if(m_topContext) DUChain::self()->refCountUp(m_topContext); } ReferencedTopDUContext::ReferencedTopDUContext(const ReferencedTopDUContext& rhs) : m_topContext(rhs.m_topContext) { if(m_topContext) DUChain::self()->refCountUp(m_topContext); } ReferencedTopDUContext::~ReferencedTopDUContext() { if(m_topContext) DUChain::self()->refCountDown(m_topContext); } ReferencedTopDUContext& ReferencedTopDUContext::operator=(const ReferencedTopDUContext& rhs) { if(m_topContext == rhs.m_topContext) return *this; if(m_topContext) DUChain::self()->refCountDown(m_topContext); m_topContext = rhs.m_topContext; if(m_topContext) DUChain::self()->refCountUp(m_topContext); return *this; } IndexedTopDUContext::IndexedTopDUContext(TopDUContext* context) { if(context) m_index = context->ownIndex(); else m_index = 0; } bool IndexedTopDUContext::isLoaded() const { if(m_index) return DUChain::self()->isInMemory(m_index); else return false; } IndexedString IndexedTopDUContext::url() const { if(m_index) return DUChain::self()->urlForIndex(m_index); else return IndexedString(); } TopDUContext* IndexedTopDUContext::data() const { if(m_index) return DUChain::self()->chainForIndex(m_index); else return 0; } DEFINE_LIST_MEMBER_HASH(TopDUContextData, m_usedDeclarationIds, DeclarationId) REGISTER_DUCHAIN_ITEM(TopDUContext); class TopDUContext::CacheData { public: CacheData(TopDUContextPointer _context) : context(_context) { } typedef std::hash_map > HashType; HashType visibleDeclarations; //Contains cached visible declarations. Visible means that they are imported, and does not respect include-positions or similar TopDUContextPointer context; }; struct TopDUContext::AliasChainElement { AliasChainElement() { //Creates invalid entries, but we need it fast because it's used to intialize all items in KDevVarLengthArray } AliasChainElement(const AliasChainElement* _prev, Identifier id) : previous(_prev), ownsPrevious(false), identifier(id), hash(0), length(0) { if(previous) { length = previous->length + 1; hash = QualifiedIdentifier::combineHash(previous->hash, previous->length, identifier); } else{ length = 1; hash = QualifiedIdentifier::combineHash(0, 0, identifier); } } //Computes the identifier represented by this chain element(generally the identifiers across the "previous" chain reversed //Returns an invalid identifier if the to be constructed identifier doesn't exist in the identifier repository QualifiedIdentifier qualifiedIdentifier() const { KDevVarLengthArray identifiers; ///@todo find faster ways of doing this //Use the known hash to find a matching QualifiedIdentifier from the repository, without //having to construct it. This should be efficient, since in most cases there should only be one //identifier with this hash QualifiedIdentifier::findByHash(hash, identifiers); for(int a = 0; a < identifiers.size(); ++a) { //Check whether there is an identifier that is equal const QualifiedIdentifier& current(identifiers[a]); if(current.explicitlyGlobal()) continue; //Skip explicitly global identifiers, since those will not be matched correctly const AliasChainElement* checkElement = this; bool mismatch = false; for(int scope = current.count()-1; scope >= 0; --scope) { if(!checkElement || current.at(scope) != checkElement->identifier) { mismatch = true; break; } checkElement = checkElement->previous; } if(!mismatch) return current; } //We do this so we don't create crap items in the qualified-identifier repository while searching return QualifiedIdentifier(); } const AliasChainElement* previous; bool ownsPrevious; Identifier identifier; uint hash; uint length; }; template void removeFromVector(QVector& vec, const T& t) { for(int a =0; a < vec.size(); ++a) { if(vec[a] == t) { vec.remove(a); removeFromVector(vec, t); return; } } } QMutex importStructureMutex(QMutex::Recursive); template bool removeOneImport(Container& container, const DUContext* value) { for(int a = 0; a < container.size(); ++a) { if(container[a].context() == value) { removeFromArray(container, a); return true; } } return false; } //Contains data that is not shared among top-contexts that share their duchain entries class TopDUContextLocalPrivate { public: TopDUContextLocalPrivate (TopDUContext* ctxt, TopDUContext* sharedDataOwner, uint index) : m_ctxt(ctxt), m_sharedDataOwner(sharedDataOwner), m_ownIndex(index), m_inDuChain(false) { if(sharedDataOwner) { Q_ASSERT(!sharedDataOwner->m_local->m_sharedDataOwner); sharedDataOwner->m_local->m_dataUsers.insert(m_ctxt); foreach(const DUContext::Import& import, m_sharedDataOwner->m_local->m_importedContexts) if(dynamic_cast(import.context())) dynamic_cast(import.context())->m_local->m_directImporters.insert(m_ctxt); } } mutable QHash m_threadCaches; TopDUContext::CacheData* currentCache() const { QHash::iterator it = m_threadCaches.find(QThread::currentThreadId()); if(it != m_threadCaches.end()) return *it; else return 0; } ~TopDUContextLocalPrivate() { //Either we use some other contexts data and have no users, or we own the data and have users that share it. QMutexLocker lock(&importStructureMutex); Q_ASSERT(!m_sharedDataOwner || m_dataUsers.isEmpty()); if(m_sharedDataOwner) { Q_ASSERT(m_sharedDataOwner->m_local->m_dataUsers.contains(m_ctxt)); m_sharedDataOwner->m_local->m_dataUsers.remove(m_ctxt); if(!m_sharedDataOwner->m_local->m_dataUsers.isEmpty()) { //this should not happen, because the users should always be deleted before the owner itself is deleted. Q_ASSERT(0); } foreach(const DUContext::Import& import, m_sharedDataOwner->m_local->m_importedContexts) if(DUChain::self()->isInMemory(import.topContextIndex()) && dynamic_cast(import.context())) dynamic_cast(import.context())->m_local->m_directImporters.remove(m_ctxt); } foreach(const DUContext::Import& import, m_importedContexts) if(DUChain::self()->isInMemory(import.topContextIndex()) && dynamic_cast(import.context())) dynamic_cast(import.context())->m_local->m_directImporters.remove(m_ctxt); } //After loading, should rebuild the links void rebuildDynamicImportStructure() { //Currently we do not store the whole data in TopDUContextLocalPrivate, so we reconstruct it from what was stored by DUContext. ///@todo sharing Q_ASSERT(m_importedContexts.isEmpty()); FOREACH_FUNCTION(const DUContext::Import& import, m_ctxt->d_func()->m_importedContexts) { if(DUChain::self()->isInMemory(import.topContextIndex())) { TopDUContext* top = dynamic_cast(import.context()); Q_ASSERT(top); addImportedContextRecursively(top, false, true); } } FOREACH_FUNCTION(IndexedDUContext importer, m_ctxt->d_func()->m_importers) { if(DUChain::self()->isInMemory(importer.topContextIndex())) { TopDUContext* top = dynamic_cast(importer.context()); Q_ASSERT(top); top->m_local->addImportedContextRecursively(m_ctxt, false, true); } } } //Index of this top-context within the duchain //Since the data of top-contexts can be shared among multiple, this can be used to add imports that are local to this top-context. QVector m_importedContexts; // mutable bool m_haveImportStructure : 1; TopDUContext* m_ctxt; TopDUContext* m_sharedDataOwner; //Either the owner of the shared data, or zero if this context owns the data QSet m_dataUsers; //Set of all context that use the data of this context. QSet m_directImporters; ParsingEnvironmentFilePointer m_file; QList m_problems; uint m_ownIndex; bool m_inDuChain; void clearImportedContextsRecursively() { QMutexLocker lock(&importStructureMutex); QSet > rebuild; foreach(DUContext::Import import, m_importedContexts) { TopDUContext* top = dynamic_cast(import.context()); if(top) { top->m_local->m_directImporters.remove(m_ctxt); foreach(TopDUContext* user, m_dataUsers) user->m_local->removeImportedContextRecursively(top, false); removeImportedContextRecursion(top, top, 1, rebuild); QHash > b = top->m_local->m_recursiveImports; for(RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { if(m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == top) removeImportedContextRecursion(top, it.key(), it->first+1, rebuild); //Remove all contexts that are imported through the context } } } m_importedContexts.clear(); rebuildImportStructureRecursion(rebuild); } //Adds the context to this and all contexts that import this, and manages m_recursiveImports void addImportedContextRecursively(TopDUContext* context, bool temporary, bool local) { QMutexLocker lock(&importStructureMutex); context->m_local->m_directImporters.insert(m_ctxt); if(local) m_importedContexts << DUContext::Import(context); // if(!m_haveImportStructure) // return; foreach(TopDUContext* user, m_dataUsers) user->m_local->addImportedContextRecursively(context, temporary, false); // context->m_local->needImportStructure(); addImportedContextRecursion(context, context, 1, temporary); QHash > b = context->m_local->m_recursiveImports; for(RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) addImportedContextRecursion(context, it.key(), (*it).first+1, temporary); //Add contexts that were imported earlier into the given one } //Removes the context from this and all contexts that import this, and manages m_recursiveImports void removeImportedContextRecursively(TopDUContext* context, bool local) { QMutexLocker lock(&importStructureMutex); context->m_local->m_directImporters.remove(m_ctxt); if(local) removeFromVector(m_importedContexts, DUContext::Import(context)); // if(!m_haveImportStructure) // return; foreach(TopDUContext* user, m_dataUsers) user->m_local->removeImportedContextRecursively(context, false); QSet > rebuild; removeImportedContextRecursion(context, context, 1, rebuild); QHash > b = context->m_local->m_recursiveImports; for(RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { if(m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == context) removeImportedContextRecursion(context, it.key(), it->first+1, rebuild); //Remove all contexts that are imported through the context } rebuildImportStructureRecursion(rebuild); } void removeImportedContextsRecursively(const QList& contexts, bool local) { QMutexLocker lock(&importStructureMutex); // if(!m_haveImportStructure) // return; foreach(TopDUContext* user, m_dataUsers) user->m_local->removeImportedContextsRecursively(contexts, false); QSet > rebuild; foreach(TopDUContext* context, contexts) { context->m_local->m_directImporters.remove(m_ctxt); if(local) removeFromVector(m_importedContexts, DUContext::Import(context)); removeImportedContextRecursion(context, context, 1, rebuild); QHash > b = context->m_local->m_recursiveImports; for(RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { if(m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == context) removeImportedContextRecursion(context, it.key(), it->first+1, rebuild); //Remove all contexts that are imported through the context } } rebuildImportStructureRecursion(rebuild); } /* void needImportStructure() const { return; //We always have an import-structure now*/ // if(m_haveImportStructure) // return; /* for(QVector::const_iterator parentIt = m_importedParentContexts.constBegin(); parentIt != m_importedParentContexts.constEnd(); ++parentIt) { TopDUContext* top = dynamic_cast(const_cast(parentIt->data())); //To avoid detaching, use const iterator if(top) { RecursiveImports::iterator it = m_recursiveImports.find(top); if(it == m_recursiveImports.end() || it->first != 1) { if(it == m_recursiveImports.end()) m_recursiveImports.insert(top, qMakePair(1, const_cast(top))); else *it = qMakePair(1, const_cast(top)); top->m_local->needImportStructure(); for(RecursiveImports::const_iterator importIt = top->m_local->m_recursiveImports.constBegin(); importIt != top->m_local->m_recursiveImports.constEnd(); ++importIt) { it = m_recursiveImports.find(importIt.key()); if(it == m_recursiveImports.end()) m_recursiveImports.insert(importIt.key(), qMakePair(importIt->first+1, const_cast(top))); else if(it->first > importIt->first+1) *it = qMakePair(importIt->first+1, const_cast(top)); //Found a shorter path } } } }*/ // m_haveImportStructure = true; // } //Has an entry for every single recursively imported file, that contains the shortest path, and the next context on that path to the imported context. //This does not need to be stored to disk, because it is defined implicitly. //What makes this most complicated is the fact that loops are allowed in the import structure. typedef QHash > RecursiveImports; mutable RecursiveImports m_recursiveImports; mutable QSet m_recursiveImportIndices; private: // void childClosure(QSet& children) { // if(children.contains(m_ctxt)) // return; // children.insert(m_ctxt); // for(QVector::const_iterator it = m_importedChildContexts.constBegin(); it != m_importedChildContexts.constEnd(); ++it) { // TopDUContext* top = dynamic_cast(const_cast(*it)); //We need to do const cast, to avoid senseless detaching // if(top) // top->m_local->childClosure(children); // } // } void addImportedContextRecursion(const TopDUContext* traceNext, const TopDUContext* imported, int depth, bool temporary = false) { // if(!m_haveImportStructure) // return; if(imported == m_ctxt) return; const bool computeShortestPaths = false; ///@todo We do not compute the shortest path. Think what's right. // traceNext->m_local->needImportStructure(); // imported->m_local->needImportStructure(); RecursiveImports::iterator it = m_recursiveImports.find(imported); if(it == m_recursiveImports.end()) { //Insert new path to "imported" m_recursiveImports[imported] = qMakePair(depth, traceNext); m_recursiveImportIndices.insert(imported->ownIndex()); Q_ASSERT(m_recursiveImportIndices.size() == m_recursiveImports.size()); Q_ASSERT(traceNext != m_ctxt); }else{ if(!computeShortestPaths) return; if(temporary) //For temporary imports, we don't record the best path. return; //It would be better if we would use the following code, but it creates too much cost in updateImportedContextRecursion when imports are removed again. //Check whether the new way to "imported" is shorter than the stored one if((*it).first > depth) { //Add a shorter path (*it).first = depth; Q_ASSERT(traceNext); (*it).second = traceNext; Q_ASSERT(traceNext == imported || (traceNext->m_local->m_recursiveImports.contains(imported) && traceNext->m_local->m_recursiveImports[imported].first < (*it).first)); }else{ //The imported context is already imported through a same/better path, so we can just stop processing. This saves us from endless recursion. return; } } if(temporary) return; for(QSet::const_iterator it = m_directImporters.constBegin(); it != m_directImporters.constEnd(); ++it) { TopDUContext* top = dynamic_cast(const_cast(*it)); //Avoid detaching, so use const_cast if(top) ///@todo also record this for local imports top->m_local->addImportedContextRecursion(m_ctxt, imported, depth+1); } } void removeImportedContextRecursion(const TopDUContext* traceNext, const TopDUContext* imported, int distance, QSet >& rebuild) { if(imported == m_ctxt) return; // if(!m_haveImportStructure) // return; RecursiveImports::iterator it = m_recursiveImports.find(imported); if(it == m_recursiveImports.end()) { //We don't import. Just return, this saves us from endless recursion. return; }else{ //Check whether we have imported "imported" through "traceNext". If not, return. Else find a new trace. if((*it).second == traceNext && (*it).first == distance) { //We need to remove the import through traceNext. Check whether there is another imported context that imports it. m_recursiveImports.erase(it); //In order to prevent problems, we completely remove everything, and re-add it. //Just updating these complex structures is very hard. m_recursiveImportIndices.remove(imported->ownIndex()); Q_ASSERT(m_recursiveImportIndices.size() == m_recursiveImports.size()); rebuild.insert(qMakePair(m_ctxt, imported)); //We MUST do this before finding another trace, because else we would create loops for(QSet::const_iterator childIt = m_directImporters.constBegin(); childIt != m_directImporters.constEnd(); ++childIt) { TopDUContext* top = dynamic_cast(const_cast(*childIt)); //Avoid detaching, so use const iterator if(top) top->m_local->removeImportedContextRecursion(m_ctxt, imported, distance+1, rebuild); //Don't use 'it' from here on, it may be invalid } } } } //Updates the trace to 'imported' void rebuildStructure(const TopDUContext* imported); void rebuildImportStructureRecursion(const QSet >& rebuild) { for(QSet >::const_iterator it = rebuild.constBegin(); it != rebuild.constEnd(); ++it) { //for(int a = rebuild.size()-1; a >= 0; --a) { //Find the best imported parent it->first->m_local->rebuildStructure(it->second); } } }; struct TopDUContext::ContextChecker { ContextChecker(const TopDUContext* _top, const SimpleCursor& _position, ContextType _contextType, bool _dontCheckImport) : top(_top), position(_position), contextType(_contextType), dontCheckImport(_dontCheckImport) { } bool operator()(IndexedDUContext context) const { // const TopDUContext* otherTop = context->topContext(); if (top->m_local->m_ownIndex != context.topContextIndex()) { // Make sure that this declaration is accessible { QMutexLocker lock(&importStructureMutex); if (!dontCheckImport && !top->recursiveImportIndices().contains(context.topContextIndex())) return false; } DUContext* ctx = context.data(); if(!ctx) return false; if (ctx->type() != contextType) return false; } else { DUContext* ctx = context.data(); if(!ctx) return false; if (ctx->type() != contextType) return false; if (ctx->range().start > position) if(!ctx->parentContext() || ctx->parentContext()->type() != Class) return false; } //Success return true; } const TopDUContext* top; const SimpleCursor& position; ContextType contextType; bool dontCheckImport; }; ///Takes a set of conditions in the constructors, and checks with each call to operator() whether these conditions are fulfilled on the given declaration. ///The import-structure needs to be constructed and locked when this is used struct TopDUContext::DeclarationChecker { DeclarationChecker(const TopDUContext* _top, const SimpleCursor& _position, const AbstractType::Ptr& _dataType, DUContext::SearchFlags _flags, KDevVarLengthArray* _createVisibleCache = 0) : createVisibleCache(_createVisibleCache), top(_top), topDFunc(_top->d_func()), position(_position), dataType(_dataType), flags(_flags) { } bool operator()(IndexedDeclaration dec) const { if (top->m_local->m_ownIndex != dec.topContextIndex()) { bool visible; { QMutexLocker lock(&importStructureMutex); visible = top->m_local->m_recursiveImportIndices.contains(dec.topContextIndex()); } if(createVisibleCache && visible) createVisibleCache->append(dec); // Make sure that this declaration is accessible if (!(flags & DUContext::NoImportsCheck) && !visible) return false; Declaration* decl = dec.data(); if(!decl) return false; if((flags & DUContext::OnlyFunctions) && !dynamic_cast(decl)) return false; if (dataType && decl->abstractType() != dataType) // The declaration doesn't match the type filter we are applying return false; } else { if(createVisibleCache) createVisibleCache->append(dec); Declaration* decl = dec.data(); if(!decl) return false; if((flags & DUContext::OnlyFunctions) && !dynamic_cast(decl)) return false; if (dataType && decl->abstractType() != dataType) // The declaration doesn't match the type filter we are applying return false; if (decl->range().start >= position) if(!decl->context() || decl->context()->type() != DUContext::Class) return false; // The declaration is behind the position we're searching from, therefore not accessible } // Success, this declaration is accessible return true; } mutable KDevVarLengthArray* createVisibleCache; const TopDUContext* top; const TopDUContextData* topDFunc; const SimpleCursor& position; const AbstractType::Ptr& dataType; DUContext::SearchFlags flags; }; ImportTrace TopDUContext::importTrace(const TopDUContext* target) const { ImportTrace ret; importTrace(target, ret); return ret; } void TopDUContext::importTrace(const TopDUContext* target, ImportTrace& store) const { QMutexLocker lock(&importStructureMutex); const TopDUContext* current = this; while(current != target) { // current->d_func()->needImportStructure(); TopDUContextLocalPrivate::RecursiveImports::const_iterator it = current->m_local->m_recursiveImports.constFind(target); if(it == current->m_local->m_recursiveImports.constEnd()) return; const TopDUContext* nextContext = (*it).second; if(nextContext) { store.append(ImportTraceItem(current, current->importPosition(nextContext))); current = nextContext; }else{ kWarning() << "inconsistent import-structure"; return; } } } const QSet& TopDUContext::recursiveImportIndices() const { ENSURE_CAN_READ QMutexLocker lock(&importStructureMutex); return m_local->m_recursiveImportIndices; } RecursiveImports TopDUContext::recursiveImports() const { ENSURE_CAN_READ QMutexLocker lock(&importStructureMutex); return m_local->m_recursiveImports; } SimpleCursor TopDUContext::importPosition(const DUContext* target) const { ENSURE_CAN_READ DUCHAIN_D(DUContext); for(unsigned int a = 0; a < d->m_importedContextsSize(); ++a) if(d->m_importedContexts()[a].context() == target) return d->m_importedContexts()[a].position; return DUContext::importPosition(target); } void TopDUContextLocalPrivate::rebuildStructure(const TopDUContext* imported) { if(m_ctxt == imported) return; for(QVector::const_iterator parentIt = m_importedContexts.constBegin(); parentIt != m_importedContexts.constEnd(); ++parentIt) { TopDUContext* top = dynamic_cast(const_cast(parentIt->context())); //To avoid detaching, use const iterator if(top) { // top->m_local->needImportStructure(); if(top == imported) { addImportedContextRecursion(top, imported, 1); }else{ RecursiveImports::const_iterator it2 = top->m_local->m_recursiveImports.find(imported); if(it2 != top->m_local->m_recursiveImports.end()) { addImportedContextRecursion(top, imported, (*it2).first + 1); } } } } for(unsigned int a = 0; a < m_ctxt->d_func()->m_importedContextsSize(); ++a) { TopDUContext* top = dynamic_cast(const_cast(m_ctxt->d_func()->m_importedContexts()[a].context())); //To avoid detaching, use const iterator if(top) { // top->m_local->needImportStructure(); if(top == imported) { addImportedContextRecursion(top, imported, 1); }else{ RecursiveImports::const_iterator it2 = top->m_local->m_recursiveImports.find(imported); if(it2 != top->m_local->m_recursiveImports.end()) { addImportedContextRecursion(top, imported, (*it2).first + 1); } } } } } void TopDUContext::rebuildDynamicImportStructure() { m_local->rebuildDynamicImportStructure(); } void TopDUContext::rebuildDynamicData(DUContext* parent, uint ownIndex) { Q_ASSERT(parent == 0 && ownIndex != 0); m_local->m_ownIndex = ownIndex; DUContext::rebuildDynamicData(parent, 0); } uint TopDUContext::ownIndex() const { return m_local->m_ownIndex; } TopDUContext::TopDUContext(TopDUContextData& data) : DUContext(data), m_local(new TopDUContextLocalPrivate(this, 0, DUChain::newTopContextIndex())), m_dynamicData(new TopDUContextDynamicData(this)) { } TopDUContext::TopDUContext(const IndexedString& url, const SimpleRange& range, ParsingEnvironmentFile* file) : DUContext(*new TopDUContextData(url), range), m_local(new TopDUContextLocalPrivate(this, 0, DUChain::newTopContextIndex())), m_dynamicData(new TopDUContextDynamicData(this)) { d_func_dynamic()->setClassId(this); DUCHAIN_D_DYNAMIC(TopDUContext); d_func_dynamic()->setClassId(this); d->m_features = VisibleDeclarationsAndContexts; d->m_deleting = false; m_local->m_file = ParsingEnvironmentFilePointer(file); setInSymbolTable(true); } TopDUContext::TopDUContext(TopDUContext* sharedDataOwner, ParsingEnvironmentFile* file) : DUContext(*sharedDataOwner), m_local(new TopDUContextLocalPrivate(this, sharedDataOwner, DUChain::newTopContextIndex())), m_dynamicData(sharedDataOwner->m_dynamicData) { m_local->m_file = ParsingEnvironmentFilePointer(file); } KSharedPtr TopDUContext::parsingEnvironmentFile() const { return m_local->m_file; } TopDUContext::~TopDUContext( ) { if(!m_local->m_sharedDataOwner) { d_func_dynamic()->m_deleting = true; if(!isOnDisk()) clearUsedDeclarationIndices(); } } void TopDUContext::deleteSelf() { //We've got to make sure that m_dynamicData and m_local are still valid while all the sub-contexts are destroyed TopDUContextLocalPrivate* local = m_local; TopDUContextDynamicData* dynamicData = m_dynamicData; if(!m_local->m_sharedDataOwner) d_func_dynamic()->m_deleting = true; delete this; delete local; delete dynamicData; } TopDUContext::Features TopDUContext::features() const { return d_func()->m_features; } void TopDUContext::setFeatures(Features features) { d_func_dynamic()->m_features = features; //Replicate features to ParsingEnvironmentFile if(parsingEnvironmentFile()) parsingEnvironmentFile()->setFeatures(features); } void TopDUContext::setParsingEnvironmentFile(ParsingEnvironmentFile* file) { m_local->m_file = KSharedPtr(file); //Replicate features to ParsingEnvironmentFile if(file) file->setFeatures(d_func()->m_features); } ///Decides whether the cache contains a valid list of visible declarations for the given hash. ///@param hash The hash-value, @param data The cache @param items Will be filled with the cached declarations. Will be left alone if none were found. void eventuallyUseCache(uint hash, TopDUContext::CacheData* cache, const IndexedDeclaration*& items, uint& itemCount) { //Check whether we have all visible global items cached TopDUContext::CacheData::HashType::iterator it = cache->visibleDeclarations.find( hash ); if( it != cache->visibleDeclarations.end() ) { itemCount = (uint)(*it).second.size(); items = (*it).second.constData(); } } struct TopDUContext::FindDeclarationsAcceptor { FindDeclarationsAcceptor(const TopDUContext* _top, DeclarationList& _target, const DeclarationChecker& _check) : top(_top), target(_target), check(_check) { cache = _top->m_local->currentCache(); } void operator() (const AliasChainElement& element) { #ifdef DEBUG_SEARCH kDebug() << "accepting" << element.qualifiedIdentifier().toString(); #endif const IndexedDeclaration* decls; uint declCount = 0; if(cache) eventuallyUseCache(element.hash, cache, decls, declCount); if(!declCount) { QualifiedIdentifier id = element.qualifiedIdentifier(); if(!id.isEmpty()) PersistentSymbolTable::self().declarations(id, declCount, decls); if(declCount > visibilityCachingMargin && cache) check.createVisibleCache = &(*cache->visibleDeclarations.insert(std::make_pair( element.hash, KDevVarLengthArray())).first).second; } for(uint a = 0; a < declCount; ++a) { if(!check(decls[a])) continue; Declaration* decl = decls[a].data(); if(!decl) continue; if(decl->identifier() != element.identifier) ///@todo eventually do more extensive checking continue; if( decl->kind() == Declaration::Alias ) { //Apply alias declarations AliasDeclaration* alias = static_cast(decl); if(alias->aliasedDeclaration().isValid()) { decl = alias->aliasedDeclaration().declaration(); } else { kDebug() << "lost aliased declaration"; } } target.append(decl); } check.createVisibleCache = 0; } const TopDUContext* top; CacheData* cache; DeclarationList& target; const DeclarationChecker& check; }; bool TopDUContext::findDeclarationsInternal(const SearchItem::PtrList& identifiers, const SimpleCursor& position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* /*source*/, SearchFlags flags) const { ENSURE_CAN_READ #ifdef DEBUG_SEARCH FOREACH_ARRAY(SearchItem::Ptr idTree, identifiers) foreach(QualifiedIdentifier id, idTree->toList()) kDebug() << "findDeclarationsInternal" << id.toString(); #endif DeclarationChecker check(this, position, dataType, flags); FindDeclarationsAcceptor storer(this, ret, check); ///The actual scopes are found within applyAliases, and each complete qualified identifier is given to FindDeclarationsAcceptor. ///That stores the found declaration to the output. applyAliases(identifiers, storer, position, false); return true; } ///@todo Implement a cache so at least the global import checks don't need to be done repeatedly. The cache should be thread-local, using DUChainPointer for the hashed items, and when an item was deleted, it should be discarded template void TopDUContext::applyAliases( const AliasChainElement* backPointer, const SearchItem::Ptr& identifier, Acceptor& accept, const SimpleCursor& position, bool canBeNamespace ) const { ///@todo explicitlyGlobal if the first identifier los global bool foundAlias = false; AliasChainElement newElement(backPointer, identifier->identifier); //Back-pointer for following elements. Also contains current hash and length. #ifdef DEBUG_SEARCH kDebug() << "checking" << newElement.qualifiedIdentifier().toString(); #endif if( !identifier->next.isEmpty() || canBeNamespace ) { //If it cannot be a namespace, the last part of the scope will be ignored //Find namespace aliases const IndexedDeclaration* aliases; uint aliasesCount = 0; KDevVarLengthArray* createVisibleCache = 0; ///Eventually take a reduced list of declarations from the cache, instead of asking the symbol-store. if(accept.cache) eventuallyUseCache(newElement.hash, accept.cache, aliases, aliasesCount); if(!aliasesCount) { QualifiedIdentifier id = newElement.qualifiedIdentifier(); if(!id.isEmpty()) PersistentSymbolTable::self().declarations(id, aliasesCount, aliases); if(aliasesCount > visibilityCachingMargin && accept.cache) createVisibleCache = &(*accept.cache->visibleDeclarations.insert(std::make_pair( newElement.hash, KDevVarLengthArray())).first).second; } if(aliasesCount) { #ifdef DEBUG_SEARCH kDebug() << "found" << aliasesCount << "aliases"; #endif DeclarationChecker check(this, position, AbstractType::Ptr(), NoSearchFlags, createVisibleCache); //The first part of the identifier has been found as a namespace-alias. //In c++, we only need the first alias. However, just to be correct, follow them all for now. for(uint a = 0; a < aliasesCount; ++a) { if(!check(aliases[a])) continue; Declaration* aliasDecl = aliases[a].data(); if(!aliasDecl) continue; if(aliasDecl->kind() != Declaration::NamespaceAlias) continue; if(foundAlias) { if(createVisibleCache) //We've got to walk through all declarations so we create a correct visible-cache continue; else break; } if(aliasDecl->identifier() != newElement.identifier) //Since we have retrieved the aliases by hash only, we still need to compare the name continue; Q_ASSERT(dynamic_cast(aliasDecl)); NamespaceAliasDeclaration* alias = static_cast(aliasDecl); foundAlias = true; QualifiedIdentifier importIdentifier = alias->importIdentifier(); if(importIdentifier.isEmpty()) { kDebug() << "found empty import"; continue; } //Create a chain of AliasChainElements that represent the identifier uint count = importIdentifier.count(); KDevVarLengthArray newChain; newChain.resize(count); for(uint a = 0; a < count; ++a) newChain[a] = AliasChainElement(a == 0 ? 0 : &newChain[a-1], importIdentifier.at(a)); AliasChainElement* newAliasedElement = &newChain[importIdentifier.count()-1]; if(identifier->next.isEmpty()) { //Just insert the aliased namespace identifier accept(*newAliasedElement); }else{ //Create an identifiers where namespace-alias part is replaced with the alias target FOREACH_ARRAY(SearchItem::Ptr item, identifier->next) applyAliases(newAliasedElement, item, accept, position, canBeNamespace); } } } } if(!foundAlias) { //If we haven't found an alias, put the current versions into the result list. Additionally we will compute the identifiers transformed through "using". if(identifier->next.isEmpty()) { accept(newElement); //We're at the end of a qualified identifier, accept it } else { FOREACH_ARRAY(SearchItem::Ptr next, identifier->next) applyAliases(&newElement, next, accept, position, canBeNamespace); } } /*if( !prefix.explicitlyGlobal() || !prefix.isEmpty() ) {*/ ///@todo check iso c++ if using-directives should be respected on top-level when explicitly global ///@todo this is bad for a very big repository(the chains should be walked for the top-context instead) //Find all namespace-imports at given scope #ifdef DEBUG_SEARCH kDebug() << "checking imports in" << (backPointer ? backPointer->qualifiedIdentifier().toString() : QString("global")); #endif { AliasChainElement importChainItem(backPointer, globalImportIdentifier); const IndexedDeclaration* imports; uint importsCount = 0; KDevVarLengthArray* createVisibleCache = 0; ///Eventually take a reduced list of declarations from the cache, instead of asking the symbol-store. if(accept.cache) eventuallyUseCache(importChainItem.hash, accept.cache, imports, importsCount); if(!importsCount) { QualifiedIdentifier id = importChainItem.qualifiedIdentifier(); if(!id.isEmpty()) PersistentSymbolTable::self().declarations(id, importsCount, imports); if(importsCount > visibilityCachingMargin && accept.cache) createVisibleCache = &(*accept.cache->visibleDeclarations.insert(std::make_pair( importChainItem.hash, KDevVarLengthArray())).first).second; } if(importsCount) { DeclarationChecker check(this, position, AbstractType::Ptr(), NoSearchFlags, createVisibleCache); for(uint a = 0; a < importsCount; ++a) { //We must never break or return from this loop, because else we might be creating a bad cache #ifdef DEBUG_SEARCH kDebug() << "found" << importsCount << "imports"; #endif if(!check(imports[a])) continue; Declaration* importDecl = imports[a].data(); if(!importDecl) continue; if(importDecl->identifier() != globalImportIdentifier) //We need to check, since we've only searched by hash continue; //Search for the identifier with the import-identifier prepended Q_ASSERT(dynamic_cast(importDecl)); NamespaceAliasDeclaration* alias = static_cast(importDecl); #ifdef DEBUG_SEARCH kDebug() << "found import of" << alias->importIdentifier().toString(); #endif QualifiedIdentifier importIdentifier = alias->importIdentifier(); if(importIdentifier.isEmpty()) { kDebug() << "found empty import"; continue; } int count = importIdentifier.count(); KDevVarLengthArray newChain; newChain.resize(importIdentifier.count()); for(int a = 0; a < count; ++a) newChain[a] = AliasChainElement(a == 0 ? 0 : &newChain[a-1], importIdentifier.at(a)); AliasChainElement* newAliasedElement = &newChain[count-1]; #ifdef DEBUG_SEARCH kDebug() << "imported" << newAliasedElement->qualifiedIdentifier().toString(); #endif //Prevent endless recursion by checking whether we're actually doing a change if(!backPointer || newAliasedElement->hash != backPointer->hash || newAliasedElement->qualifiedIdentifier() != backPointer->qualifiedIdentifier()) applyAliases(newAliasedElement, identifier, accept, position, canBeNamespace); } } } } template void TopDUContext::applyAliases( const SearchItem::PtrList& identifiers, Acceptor& acceptor, const SimpleCursor& position, bool canBeNamespace ) const { FOREACH_ARRAY(SearchItem::Ptr item, identifiers) applyAliases(0, item, acceptor, position, canBeNamespace); } struct TopDUContext::FindContextsAcceptor { FindContextsAcceptor(const TopDUContext* _top, QList& _target, const ContextChecker& _check) : top(_top), target(_target), check(_check) { cache = _top->m_local->currentCache(); } void operator() (const AliasChainElement& element) { #ifdef DEBUG_SEARCH kDebug() << "accepting" << element.qualifiedIdentifier().toString(); #endif const IndexedDUContext* decls; uint declsCount = 0; QualifiedIdentifier id = element.qualifiedIdentifier(); PersistentSymbolTable::self().contexts(id, declsCount, decls); for(uint a = 0; a < declsCount; ++a) { if(!check(decls[a])) continue; DUContext* ctx = decls[a].data(); if(!ctx) continue; if(ctx->localScopeIdentifier().last() != element.identifier) ///@todo eventually do more extensive checking continue; target << ctx; } } const TopDUContext* top; CacheData* cache; QList& target; const ContextChecker& check; }; void TopDUContext::findContextsInternal(ContextType contextType, const SearchItem::PtrList& baseIdentifiers, const SimpleCursor& position, QList& ret, SearchFlags flags) const { ENSURE_CAN_READ ContextChecker check(this, position, contextType, flags & DUContext::NoImportsCheck); FindContextsAcceptor storer(this, ret, check); ///The actual scopes are found within applyAliases, and each complete qualified identifier is given to FindContextsAcceptor. ///That stores the found declaration to the output. applyAliases(baseIdentifiers, storer, position, contextType == Namespace); } TopDUContext* TopDUContext::sharedDataOwner() const { return m_local->m_sharedDataOwner; } TopDUContext * TopDUContext::topContext() const { return const_cast(this); } bool TopDUContext::deleting() const { return d_func()->m_deleting; } QList TopDUContext::problems() const { ENSURE_CAN_READ if(m_local->m_sharedDataOwner) return m_local->m_problems + m_local->m_sharedDataOwner->m_local->m_problems; else return m_local->m_problems; } void TopDUContext::addProblem(const ProblemPointer& problem) { ENSURE_CAN_WRITE m_local->m_problems << problem; } void TopDUContext::clearProblems() { ENSURE_CAN_WRITE m_local->m_problems.clear(); } QVector TopDUContext::importers() const { ENSURE_CAN_READ return QVector::fromList( m_local->m_directImporters.toList() ); } QList TopDUContext::loadedImporters() const { ENSURE_CAN_READ return m_local->m_directImporters.toList(); } QVector TopDUContext::importedParentContexts() const { ENSURE_CAN_READ if(m_local->m_sharedDataOwner) return m_local->m_importedContexts + m_local->m_sharedDataOwner->m_local->m_importedContexts; else return m_local->m_importedContexts; } bool TopDUContext::imports(const DUContext * origin, const SimpleCursor& position) const { ENSURE_CAN_READ return importsPrivate(origin, position); } bool TopDUContext::importsPrivate(const DUContext * origin, const SimpleCursor& position) const { Q_UNUSED(position); if( dynamic_cast(origin) ) { QMutexLocker lock(&importStructureMutex); // d_func()->needImportStructure(); return m_local->m_recursiveImports.contains(static_cast(origin)); } else { return DUContext::imports(origin, position); } } void TopDUContext::clearImportedParentContexts() { m_local->clearImportedContextsRecursively(); if(!m_local->m_sharedDataOwner) DUContext::clearImportedParentContexts(); else { foreach (Import parent, m_local->m_importedContexts) if( parent.context() ) removeImportedParentContext(parent.context()); Q_ASSERT(m_local->m_importedContexts.isEmpty()); } } void TopDUContext::addImportedParentContext(DUContext* context, const SimpleCursor& position, bool /*anonymous*/, bool temporary) { if(context == this) return; if(!m_local->m_sharedDataOwner) //Always make the contexts anonymous, because we care about importers in TopDUContextLocalPrivate DUContext::addImportedParentContext(context, position, true, temporary); m_local->addImportedContextRecursively(static_cast(context), temporary, true); } void TopDUContext::removeImportedParentContext(DUContext* context) { if(!m_local->m_sharedDataOwner) DUContext::removeImportedParentContext(context); m_local->removeImportedContextRecursively(static_cast(context), true); } void TopDUContext::addImportedParentContexts(const QList >& contexts, bool temporary) { typedef QPair Pair; foreach(Pair pair, contexts) addImportedParentContext(pair.first, pair.second, false, temporary); } void TopDUContext::removeImportedParentContexts(const QList& contexts) { foreach(TopDUContext* context, contexts) DUContext::removeImportedParentContext(context); if(!m_local->m_sharedDataOwner) m_local->removeImportedContextsRecursively(contexts, true); } /// Returns true if this object is registered in the du-chain. If it is not, all sub-objects(context, declarations, etc.) bool TopDUContext::inDuChain() const { return m_local->m_inDuChain; } /// This flag is only used by DUChain, never change it from outside. void TopDUContext::setInDuChain(bool b) { m_local->m_inDuChain = b; } TopDUContext::Flags TopDUContext::flags() const { return d_func()->m_flags; } void TopDUContext::setFlags(Flags f) { d_func_dynamic()->m_flags = f; } bool TopDUContext::isOnDisk() const { ///@todo Change this to releasingToDisk, and only enable it while saving a top-context to disk. return m_dynamicData->isOnDisk(); } IndexedString TopDUContext::language() const { return d_func()->m_language; } void TopDUContext::setLanguage(IndexedString language) { d_func_dynamic()->m_language = language; } void TopDUContext::clearUsedDeclarationIndices() { ENSURE_CAN_WRITE for(unsigned int a = 0; a < d_func()->m_usedDeclarationIdsSize(); ++a) DUChain::uses()->removeUse(d_func()->m_usedDeclarationIds()[a], this); d_func_dynamic()->m_usedDeclarationIdsList().clear(); } Declaration* TopDUContext::usedDeclarationForIndex(unsigned int declarationIndex) const { ENSURE_CAN_READ if(declarationIndex & (1<<31)) { //We use the highest bit to mark direct indices into the local declarations declarationIndex &= (0xffffffff - (1<<31)); //unset the highest bit return m_dynamicData->getDeclarationForIndex(declarationIndex); }else if(declarationIndex < d_func()->m_usedDeclarationIdsSize()) return d_func()->m_usedDeclarationIds()[declarationIndex].getDeclaration(this); else return 0; } int TopDUContext::indexForUsedDeclaration(Declaration* declaration, bool create) { if(create) { ENSURE_CAN_WRITE }else{ ENSURE_CAN_READ } if(declaration->topContext() == this) { uint index = declaration->ownIndex(); Q_ASSERT(!(index & (1<<31))); return (int)(index | (1<<31)); //We don't put context-local declarations into the list, that's a waste. We just use the mark them with the highest bit. } DeclarationId id(declaration->id()); int index = -1; uint size = d_func()->m_usedDeclarationIdsSize(); const DeclarationId* ids = d_func()->m_usedDeclarationIds(); for(unsigned int a = 0; a < size; ++a) if(ids[a] == id) { index = a; break; } if(index != -1) return index; if(!create) return std::numeric_limits::max(); d_func_dynamic()->m_usedDeclarationIdsList().append(id); if(declaration->topContext() != this) DUChain::uses()->addUse(id, this); return d_func()->m_usedDeclarationIdsSize()-1; } QList allSmartUses(TopDUContext* context, Declaration* declaration) { QList ret; int declarationIndex = context->indexForUsedDeclaration(declaration, false); if(declarationIndex == std::numeric_limits::max()) return ret; return allSmartUses(context, declarationIndex); } QList allUses(TopDUContext* context, Declaration* declaration, bool noEmptyRanges) { QList ret; int declarationIndex = context->indexForUsedDeclaration(declaration, false); if(declarationIndex == std::numeric_limits::max()) return ret; return allUses(context, declarationIndex, noEmptyRanges); } ///@todo move this kind of caching into the symbol-table TopDUContext::Cache::Cache(TopDUContextPointer context) : d(new CacheData(context)) { DUChainWriteLocker lock(DUChain::lock()); if(d->context) d->context->m_local->m_threadCaches.insert(QThread::currentThreadId(), d); } TopDUContext::Cache::~Cache() { DUChainWriteLocker lock(DUChain::lock()); if(d->context && d->context->m_local->m_threadCaches[QThread::currentThreadId()] == d) d->context->m_local->m_threadCaches.remove(QThread::currentThreadId()); delete d; } IndexedString TopDUContext::url() const { return d_func()->m_url; } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on