/* Copyright 2007 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 "parsingenvironment.h" #include "topducontext.h" #include "duchainregister.h" #include "topducontextdynamicdata.h" #include "duchain.h" #include "duchainlock.h" #include "topducontextdata.h" #include #include #define ENSURE_READ_LOCKED if(indexedTopContext().isValid()) { ENSURE_CHAIN_READ_LOCKED } #define ENSURE_WRITE_LOCKED if(indexedTopContext().isValid()) { ENSURE_CHAIN_READ_LOCKED } namespace KDevelop { StaticParsingEnvironmentData* ParsingEnvironmentFile::m_staticData = 0; #if 0 ///Wrapper class around objects that are managed through the DUChain, and may contain arbitrary objects ///that support duchain-like store (IndexedString, StorableSet, and the likes). The object must not contain pointers ///or other non-persistent data. /// ///The object is stored during the normal duchain storage/cleanup cycles. template struct PersistentDUChainObject { ///@param fileName File-name that will be used to store the data of the object in the duchain directory PersistentDUChainObject(QString fileName) { object = (T*) new char[sizeof(T)]; if(!DUChain::self()->addPersistentObject(object, fileName, sizeof(T))) { //The constructor is called only if the object did not exist yet new (object) T(); } } ~PersistentDUChainObject() { DUChain::self()->unregisterPersistentObject(object); delete[] object; } T* object; }; #endif REGISTER_DUCHAIN_ITEM(ParsingEnvironmentFile); TopDUContext::Features ParsingEnvironmentFile::features() const { ENSURE_READ_LOCKED return d_func()->m_features; } ParsingEnvironment::ParsingEnvironment() { } ParsingEnvironment::~ParsingEnvironment() { } IndexedString ParsingEnvironmentFile::url() const { ENSURE_READ_LOCKED return d_func()->m_url; } bool ParsingEnvironmentFile::needsUpdate(const ParsingEnvironment* /*environment*/) const { ENSURE_READ_LOCKED return d_func()->m_allModificationRevisions.needsUpdate(); } bool ParsingEnvironmentFile::matchEnvironment(const ParsingEnvironment* /*environment*/) const { ENSURE_READ_LOCKED return true; } void ParsingEnvironmentFile::setTopContext(KDevelop::IndexedTopDUContext context) { if(d_func()->m_topContext == context) return; ENSURE_WRITE_LOCKED d_func_dynamic()->m_topContext = context; //Enforce an update of the 'features satisfied' caches TopDUContext::Features oldFeatures = features(); setFeatures(TopDUContext::Empty); setFeatures(oldFeatures); } KDevelop::IndexedTopDUContext ParsingEnvironmentFile::indexedTopContext() const { return d_func()->m_topContext; } const ModificationRevisionSet& ParsingEnvironmentFile::allModificationRevisions() const { ENSURE_READ_LOCKED return d_func()->m_allModificationRevisions; } void ParsingEnvironmentFile::addModificationRevisions(const ModificationRevisionSet& revisions) { ENSURE_WRITE_LOCKED d_func_dynamic()->m_allModificationRevisions += revisions; } ParsingEnvironmentFile::ParsingEnvironmentFile(ParsingEnvironmentFileData& data, const IndexedString& url) : DUChainBase(data) { d_func_dynamic()->m_url = url; d_func_dynamic()->m_modificationTime = ModificationRevision::revisionForFile(url); addModificationRevision(url, d_func_dynamic()->m_modificationTime); Q_ASSERT(d_func()->m_allModificationRevisions.index()); } ParsingEnvironmentFile::ParsingEnvironmentFile(const IndexedString& url) : DUChainBase(*new ParsingEnvironmentFileData()) { d_func_dynamic()->setClassId(this); d_func_dynamic()->m_url = url; d_func_dynamic()->m_modificationTime = ModificationRevision::revisionForFile(url); addModificationRevision(url, d_func_dynamic()->m_modificationTime); Q_ASSERT(d_func()->m_allModificationRevisions.index()); } TopDUContext* ParsingEnvironmentFile::topContext() const { ENSURE_READ_LOCKED return indexedTopContext().data(); } ParsingEnvironmentFile::~ParsingEnvironmentFile() { } ParsingEnvironmentFile::ParsingEnvironmentFile(ParsingEnvironmentFileData& data) : DUChainBase(data) { //If this triggers, the item has most probably not been initialized with the correct constructor that takes an IndexedString. Q_ASSERT(d_func()->m_allModificationRevisions.index()); } int ParsingEnvironment::type() const { return StandardParsingEnvironment; } int ParsingEnvironmentFile::type() const { ENSURE_READ_LOCKED return StandardParsingEnvironment; } bool ParsingEnvironmentFile::isProxyContext() const { ENSURE_READ_LOCKED return d_func()->m_isProxyContext; } void ParsingEnvironmentFile::setIsProxyContext(bool is) { ENSURE_WRITE_LOCKED d_func_dynamic()->m_isProxyContext = is; } QList< KSharedPtr > ParsingEnvironmentFile::imports() const { ENSURE_READ_LOCKED QList imp; IndexedTopDUContext top = indexedTopContext(); if(top.isLoaded()) { TopDUContext* topCtx = top.data(); FOREACH_FUNCTION(const DUContext::Import& import, topCtx->d_func()->m_importedContexts) imp << import.indexedContext(); }else{ imp = TopDUContextDynamicData::loadImports(top.index()); } QList< KSharedPtr > ret; foreach(const IndexedDUContext &ctx, imp) { KSharedPtr item = DUChain::self()->environmentFileForDocument(ctx.topContextIndex()); if(item) { ret << item; }else{ kDebug() << url().str() << indexedTopContext().index() << ": invalid import" << ctx.topContextIndex(); } } return ret; } QList< KSharedPtr > ParsingEnvironmentFile::importers() const { ENSURE_READ_LOCKED QList imp; IndexedTopDUContext top = indexedTopContext(); if(top.isLoaded()) { TopDUContext* topCtx = top.data(); FOREACH_FUNCTION(const IndexedDUContext& ctx, topCtx->d_func()->m_importers) imp << ctx; }else{ imp = TopDUContextDynamicData::loadImporters(top.index()); } QList< KSharedPtr > ret; foreach(const IndexedDUContext &ctx, imp) { KSharedPtr f = DUChain::self()->environmentFileForDocument(ctx.topContextIndex()); if(f) ret << f; else kDebug() << url().str() << indexedTopContext().index() << ": invalid importer context" << ctx.topContextIndex(); } return ret; } QMutex featureSatisfactionMutex; inline bool satisfied(TopDUContext::Features features, TopDUContext::Features required) { return (features & required) == required; } ///Makes sure the file has the correct features attached, and if minimumFeatures contains AllDeclarationsContextsAndUsesForRecursive, then also checks all imports. bool ParsingEnvironmentFile::featuresMatch(TopDUContext::Features minimumFeatures, QSet& checked) const { if(checked.contains(this)) return true; checked.insert(this); TopDUContext::Features localRequired = (TopDUContext::Features) (minimumFeatures | ParseJob::staticMinimumFeatures(url())); //Check other 'local' requirements localRequired = (TopDUContext::Features)(localRequired & (TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST)); if(!satisfied(features(), localRequired)) return false; if(ParseJob::hasStaticMinimumFeatures()) { //Do a manual recursion to check whether any of the relevant contexts has static minimum features set ///@todo Only do this if one of the imports actually has static features attached (by RecursiveImports set intersection) foreach(const ParsingEnvironmentFilePointer &import, imports()) if(!import->featuresMatch(minimumFeatures & TopDUContext::Recursive ? minimumFeatures : ((TopDUContext::Features)0), checked)) return false; }else if(minimumFeatures & TopDUContext::Recursive) { QMutexLocker lock(&featureSatisfactionMutex); TopDUContext::IndexedRecursiveImports recursiveImportIndices = d_func()->m_importsCache; if(recursiveImportIndices.isEmpty()) { //Unfortunately, we have to load the top-context TopDUContext* top = topContext(); if(top) recursiveImportIndices = top->recursiveImportIndices(); } ///@todo Do not create temporary intersected sets //Use the features-cache to efficiently check the recursive satisfaction of the features if(satisfied(minimumFeatures, TopDUContext::AST) && !((m_staticData->ASTSatisfied & recursiveImportIndices) == recursiveImportIndices)) return false; if(satisfied(minimumFeatures, TopDUContext::AllDeclarationsContextsAndUses)) return (m_staticData->allDeclarationsAndUsesSatisfied & recursiveImportIndices) == recursiveImportIndices; else if(satisfied(minimumFeatures, TopDUContext::AllDeclarationsAndContexts)) return (m_staticData->allDeclarationsSatisfied & recursiveImportIndices) == recursiveImportIndices; else if(satisfied(minimumFeatures, TopDUContext::VisibleDeclarationsAndContexts)) return (m_staticData->visibleDeclarationsSatisfied & recursiveImportIndices) == recursiveImportIndices; else if(satisfied(minimumFeatures, TopDUContext::SimplifiedVisibleDeclarationsAndContexts)) return (m_staticData->simplifiedVisibleDeclarationsSatisfied & recursiveImportIndices) == recursiveImportIndices; } return true; } void ParsingEnvironmentFile::setFeatures(TopDUContext::Features features) { if(d_func()->m_features == features) return; ENSURE_WRITE_LOCKED d_func_dynamic()->m_features = features; if(indexedTopContext().isValid()) { QMutexLocker lock(&featureSatisfactionMutex); if(!satisfied(features, TopDUContext::SimplifiedVisibleDeclarationsAndContexts)) m_staticData->simplifiedVisibleDeclarationsSatisfied.remove(indexedTopContext()); else m_staticData->simplifiedVisibleDeclarationsSatisfied.insert(indexedTopContext()); if(!satisfied(features, TopDUContext::VisibleDeclarationsAndContexts)) m_staticData->visibleDeclarationsSatisfied.remove(indexedTopContext()); else m_staticData->visibleDeclarationsSatisfied.insert(indexedTopContext()); if(!satisfied(features, TopDUContext::AllDeclarationsAndContexts)) m_staticData->allDeclarationsSatisfied.remove(indexedTopContext()); else m_staticData->allDeclarationsSatisfied.insert(indexedTopContext()); if(!satisfied(features, TopDUContext::AllDeclarationsContextsAndUses)) m_staticData->allDeclarationsAndUsesSatisfied.remove(indexedTopContext()); else m_staticData->allDeclarationsAndUsesSatisfied.insert(indexedTopContext()); if(!satisfied(features, TopDUContext::AST)) m_staticData->ASTSatisfied.remove(indexedTopContext()); else m_staticData->ASTSatisfied.insert(indexedTopContext()); } } bool ParsingEnvironmentFile::featuresSatisfied(KDevelop::TopDUContext::Features minimumFeatures) const { ENSURE_READ_LOCKED QSet checked; if(minimumFeatures & TopDUContext::ForceUpdate) return false; return featuresMatch(minimumFeatures, checked); } void ParsingEnvironmentFile::clearModificationRevisions() { ENSURE_WRITE_LOCKED d_func_dynamic()->m_allModificationRevisions.clear(); d_func_dynamic()->m_allModificationRevisions.addModificationRevision(d_func()->m_url, d_func()->m_modificationTime); } void ParsingEnvironmentFile::addModificationRevision(const IndexedString& url, const ModificationRevision& revision) { ENSURE_WRITE_LOCKED d_func_dynamic()->m_allModificationRevisions.addModificationRevision(url, revision); { //Test Q_ASSERT(d_func_dynamic()->m_allModificationRevisions.index()); bool result = d_func_dynamic()->m_allModificationRevisions.removeModificationRevision(url, revision); Q_UNUSED( result ); Q_ASSERT( result ); d_func_dynamic()->m_allModificationRevisions.addModificationRevision(url, revision); } } void ParsingEnvironmentFile::setModificationRevision( const KDevelop::ModificationRevision& rev ) { ENSURE_WRITE_LOCKED Q_ASSERT(d_func_dynamic()->m_allModificationRevisions.index()); bool result = d_func_dynamic()->m_allModificationRevisions.removeModificationRevision(d_func()->m_url, d_func()->m_modificationTime); Q_ASSERT( result ); Q_UNUSED( result ); #ifdef LEXERCACHE_DEBUG if(debugging()) { kDebug() << id(this) << "setting modification-revision" << rev.toString(); } #endif d_func_dynamic()->m_modificationTime = rev; #ifdef LEXERCACHE_DEBUG if(debugging()) { kDebug() << id(this) << "new modification-revision" << m_modificationTime; } #endif d_func_dynamic()->m_allModificationRevisions.addModificationRevision(d_func()->m_url, d_func()->m_modificationTime); } KDevelop::ModificationRevision ParsingEnvironmentFile::modificationRevision() const { ENSURE_READ_LOCKED return d_func()->m_modificationTime; } IndexedString ParsingEnvironmentFile::language() const { return d_func()->m_language; } void ParsingEnvironmentFile::setLanguage(IndexedString language) { d_func_dynamic()->m_language = language; } const KDevelop::TopDUContext::IndexedRecursiveImports& ParsingEnvironmentFile::importsCache() const { return d_func()->m_importsCache; } void ParsingEnvironmentFile::setImportsCache(const KDevelop::TopDUContext::IndexedRecursiveImports& importsCache) { d_func_dynamic()->m_importsCache = importsCache; } } //KDevelop