File indexing completed on 2024-05-12 04:38:03

0001 /*
0002     SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "parsingenvironment.h"
0008 #include "topducontext.h"
0009 #include "duchainregister.h"
0010 #include "topducontextdynamicdata.h"
0011 #include "duchain.h"
0012 #include "duchainlock.h"
0013 #include "topducontextdata.h"
0014 #include <debug.h>
0015 #include <language/backgroundparser/parsejob.h>
0016 
0017 #define ENSURE_READ_LOCKED   if (indexedTopContext().isValid()) { ENSURE_CHAIN_READ_LOCKED }
0018 #define ENSURE_WRITE_LOCKED   if (indexedTopContext().isValid()) { ENSURE_CHAIN_READ_LOCKED }
0019 
0020 namespace KDevelop {
0021 StaticParsingEnvironmentData* ParsingEnvironmentFile::m_staticData = nullptr;
0022 
0023  #if 0
0024 ///Wrapper class around objects that are managed through the DUChain, and may contain arbitrary objects
0025 ///that support duchain-like store (IndexedString, StorableSet, and the likes). The object must not contain pointers
0026 ///or other non-persistent data.
0027 ///
0028 ///The object is stored during the normal duchain storage/cleanup cycles.
0029 
0030 template <class T>
0031 struct PersistentDUChainObject
0032 {
0033     ///@param fileName File-name that will be used to store the data of the object in the duchain directory
0034     PersistentDUChainObject(QString fileName)
0035     {
0036         object = ( T* ) new char[sizeof(T)];
0037         if (!DUChain::self()->addPersistentObject(object, fileName, sizeof(T))) {
0038             //The constructor is called only if the object did not exist yet
0039             new (object) T();
0040         }
0041     }
0042     ~PersistentDUChainObject()
0043     {
0044         DUChain::self()->unregisterPersistentObject(object);
0045         delete[] object;
0046     }
0047 
0048     T* object;
0049 };
0050 #endif
0051 
0052 REGISTER_DUCHAIN_ITEM(ParsingEnvironmentFile);
0053 
0054 TopDUContext::Features ParsingEnvironmentFile::features() const
0055 {
0056     ENSURE_READ_LOCKED
0057 
0058     return d_func()->m_features;
0059 }
0060 
0061 ParsingEnvironment::ParsingEnvironment()
0062 {
0063 }
0064 
0065 ParsingEnvironment::~ParsingEnvironment()
0066 {
0067 }
0068 
0069 IndexedString ParsingEnvironmentFile::url() const
0070 {
0071     ENSURE_READ_LOCKED
0072     return d_func()->m_url;
0073 }
0074 
0075 bool ParsingEnvironmentFile::needsUpdate(const ParsingEnvironment* /*environment*/) const
0076 {
0077     ENSURE_READ_LOCKED
0078     return d_func()->m_allModificationRevisions.needsUpdate();
0079 }
0080 
0081 bool ParsingEnvironmentFile::matchEnvironment(const ParsingEnvironment* /*environment*/) const
0082 {
0083     ENSURE_READ_LOCKED
0084     return true;
0085 }
0086 
0087 void ParsingEnvironmentFile::setTopContext(KDevelop::IndexedTopDUContext context)
0088 {
0089     if (d_func()->m_topContext == context)
0090         return;
0091     ENSURE_WRITE_LOCKED
0092         d_func_dynamic()->m_topContext = context;
0093 
0094     //Enforce an update of the 'features satisfied' caches
0095     TopDUContext::Features oldFeatures = features();
0096     setFeatures(TopDUContext::Empty);
0097     setFeatures(oldFeatures);
0098 }
0099 
0100 KDevelop::IndexedTopDUContext ParsingEnvironmentFile::indexedTopContext() const
0101 {
0102     return d_func()->m_topContext;
0103 }
0104 
0105 const ModificationRevisionSet& ParsingEnvironmentFile::allModificationRevisions() const
0106 {
0107     ENSURE_READ_LOCKED
0108     return d_func()->m_allModificationRevisions;
0109 }
0110 
0111 void ParsingEnvironmentFile::addModificationRevisions(const ModificationRevisionSet& revisions)
0112 {
0113     ENSURE_WRITE_LOCKED
0114         d_func_dynamic()->m_allModificationRevisions += revisions;
0115 }
0116 
0117 ParsingEnvironmentFile::ParsingEnvironmentFile(ParsingEnvironmentFileData& data,
0118                                                const IndexedString& url) : DUChainBase(data)
0119 {
0120     d_func_dynamic()->m_url = url;
0121     d_func_dynamic()->m_modificationTime = ModificationRevision::revisionForFile(url);
0122 
0123     addModificationRevision(url, d_func_dynamic()->m_modificationTime);
0124     Q_ASSERT(d_func()->m_allModificationRevisions.index());
0125 }
0126 
0127 ParsingEnvironmentFile::ParsingEnvironmentFile(const IndexedString& url) : DUChainBase(*new ParsingEnvironmentFileData())
0128 {
0129     d_func_dynamic()->setClassId(this);
0130 
0131     d_func_dynamic()->m_url = url;
0132     d_func_dynamic()->m_modificationTime = ModificationRevision::revisionForFile(url);
0133 
0134     addModificationRevision(url, d_func_dynamic()->m_modificationTime);
0135     Q_ASSERT(d_func()->m_allModificationRevisions.index());
0136 }
0137 
0138 TopDUContext* ParsingEnvironmentFile::topContext() const
0139 {
0140     ENSURE_READ_LOCKED
0141     return indexedTopContext().data();
0142 }
0143 
0144 ParsingEnvironmentFile::~ParsingEnvironmentFile()
0145 {
0146 }
0147 
0148 ParsingEnvironmentFile::ParsingEnvironmentFile(ParsingEnvironmentFileData& data) : DUChainBase(data)
0149 {
0150     //If this triggers, the item has most probably not been initialized with the correct constructor that takes an IndexedString.
0151     Q_ASSERT(d_func()->m_allModificationRevisions.index());
0152 }
0153 
0154 int ParsingEnvironment::type() const
0155 {
0156     return StandardParsingEnvironment;
0157 }
0158 
0159 int ParsingEnvironmentFile::type() const
0160 {
0161     ENSURE_READ_LOCKED
0162     return StandardParsingEnvironment;
0163 }
0164 
0165 bool ParsingEnvironmentFile::isProxyContext() const
0166 {
0167     ENSURE_READ_LOCKED
0168     return d_func()->m_isProxyContext;
0169 }
0170 
0171 void ParsingEnvironmentFile::setIsProxyContext(bool is)
0172 {
0173     ENSURE_WRITE_LOCKED
0174         d_func_dynamic()->m_isProxyContext = is;
0175 }
0176 
0177 QList<QExplicitlySharedDataPointer<ParsingEnvironmentFile>> ParsingEnvironmentFile::imports() const
0178 {
0179     ENSURE_READ_LOCKED
0180 
0181     QList<IndexedDUContext> imp;
0182     IndexedTopDUContext top = indexedTopContext();
0183     if (top.isLoaded()) {
0184         TopDUContext* topCtx = top.data();
0185         imp.reserve(topCtx->d_func()->m_importedContextsSize());
0186         FOREACH_FUNCTION(const DUContext::Import& import, topCtx->d_func()->m_importedContexts)
0187         imp << import.indexedContext();
0188     } else {
0189         imp = TopDUContextDynamicData::loadImports(top.index());
0190     }
0191 
0192     QList<QExplicitlySharedDataPointer<ParsingEnvironmentFile>> ret;
0193     for (const IndexedDUContext ctx : qAsConst(imp)) {
0194         QExplicitlySharedDataPointer<ParsingEnvironmentFile> item = DUChain::self()->environmentFileForDocument(
0195             ctx.topContextIndex());
0196         if (item) {
0197             ret << item;
0198         } else {
0199             qCDebug(LANGUAGE) << url().str() << indexedTopContext().index() << ": invalid import" <<
0200                 ctx.topContextIndex();
0201         }
0202     }
0203 
0204     return ret;
0205 }
0206 
0207 QList<QExplicitlySharedDataPointer<ParsingEnvironmentFile>> ParsingEnvironmentFile::importers() const
0208 {
0209     ENSURE_READ_LOCKED
0210 
0211     QList<IndexedDUContext> imp;
0212     IndexedTopDUContext top = indexedTopContext();
0213     if (top.isLoaded()) {
0214         TopDUContext* topCtx = top.data();
0215         FOREACH_FUNCTION(const IndexedDUContext &ctx, topCtx->d_func()->m_importers)
0216         imp << ctx;
0217     } else {
0218         imp = TopDUContextDynamicData::loadImporters(top.index());
0219     }
0220 
0221     QList<QExplicitlySharedDataPointer<ParsingEnvironmentFile>> ret;
0222     for (const IndexedDUContext ctx : qAsConst(imp)) {
0223         QExplicitlySharedDataPointer<ParsingEnvironmentFile> f = DUChain::self()->environmentFileForDocument(
0224             ctx.topContextIndex());
0225         if (f)
0226             ret << f;
0227         else
0228             qCDebug(LANGUAGE) << url().str() << indexedTopContext().index() << ": invalid importer context" <<
0229                 ctx.topContextIndex();
0230     }
0231 
0232     return ret;
0233 }
0234 
0235 QMutex featureSatisfactionMutex;
0236 
0237 inline bool satisfied(TopDUContext::Features features, TopDUContext::Features required)
0238 {
0239     return (features & required) == required;
0240 }
0241 
0242 ///Makes sure the file has the correct features attached, and if minimumFeatures contains AllDeclarationsContextsAndUsesForRecursive, then also checks all imports.
0243 bool ParsingEnvironmentFile::featuresMatch(TopDUContext::Features minimumFeatures,
0244                                            QSet<const ParsingEnvironmentFile*>& checked) const
0245 {
0246     if (checked.contains(this))
0247         return true;
0248 
0249     checked.insert(this);
0250 
0251     auto localRequired = minimumFeatures | ParseJob::staticMinimumFeatures(url());
0252 
0253     //Check other 'local' requirements
0254     localRequired &= (TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST);
0255 
0256     if (!satisfied(features(), localRequired))
0257         return false;
0258 
0259     if (ParseJob::hasStaticMinimumFeatures()) {
0260         //Do a manual recursion to check whether any of the relevant contexts has static minimum features set
0261         ///@todo Only do this if one of the imports actually has static features attached (by RecursiveImports set intersection)
0262         const auto imports = this->imports();
0263         for (const ParsingEnvironmentFilePointer& import : imports) {
0264             if (!import->featuresMatch(minimumFeatures &
0265                                        TopDUContext::Recursive ? minimumFeatures : TopDUContext::Features{},
0266                                        checked))
0267                 return false;
0268         }
0269     } else if (minimumFeatures & TopDUContext::Recursive) {
0270         QMutexLocker lock(&featureSatisfactionMutex);
0271 
0272         TopDUContext::IndexedRecursiveImports recursiveImportIndices = d_func()->m_importsCache;
0273         if (recursiveImportIndices.isEmpty()) {
0274             //Unfortunately, we have to load the top-context
0275             TopDUContext* top = topContext();
0276             if (top)
0277                 recursiveImportIndices = top->recursiveImportIndices();
0278         }
0279 
0280         ///@todo Do not create temporary intersected sets
0281 
0282         //Use the features-cache to efficiently check the recursive satisfaction of the features
0283         if (satisfied(minimumFeatures,
0284                       TopDUContext::AST) &&
0285             !((m_staticData->ASTSatisfied & recursiveImportIndices) == recursiveImportIndices))
0286             return false;
0287 
0288         if (satisfied(minimumFeatures, TopDUContext::AllDeclarationsContextsAndUses))
0289             return (m_staticData->allDeclarationsAndUsesSatisfied & recursiveImportIndices) == recursiveImportIndices;
0290         else if (satisfied(minimumFeatures, TopDUContext::AllDeclarationsAndContexts))
0291             return (m_staticData->allDeclarationsSatisfied & recursiveImportIndices) == recursiveImportIndices;
0292         else if (satisfied(minimumFeatures, TopDUContext::VisibleDeclarationsAndContexts))
0293             return (m_staticData->visibleDeclarationsSatisfied & recursiveImportIndices) == recursiveImportIndices;
0294         else if (satisfied(minimumFeatures, TopDUContext::SimplifiedVisibleDeclarationsAndContexts))
0295             return (m_staticData->simplifiedVisibleDeclarationsSatisfied & recursiveImportIndices) ==
0296                    recursiveImportIndices;
0297     }
0298 
0299     return true;
0300 }
0301 
0302 void ParsingEnvironmentFile::setFeatures(TopDUContext::Features features)
0303 {
0304     if (d_func()->m_features == features)
0305         return;
0306     ENSURE_WRITE_LOCKED
0307         d_func_dynamic()->m_features = features;
0308 
0309     if (indexedTopContext().isValid()) {
0310         QMutexLocker lock(&featureSatisfactionMutex);
0311 
0312         if (!satisfied(features, TopDUContext::SimplifiedVisibleDeclarationsAndContexts))
0313             m_staticData->simplifiedVisibleDeclarationsSatisfied.remove(indexedTopContext());
0314         else
0315             m_staticData->simplifiedVisibleDeclarationsSatisfied.insert(indexedTopContext());
0316 
0317         if (!satisfied(features, TopDUContext::VisibleDeclarationsAndContexts))
0318             m_staticData->visibleDeclarationsSatisfied.remove(indexedTopContext());
0319         else
0320             m_staticData->visibleDeclarationsSatisfied.insert(indexedTopContext());
0321 
0322         if (!satisfied(features, TopDUContext::AllDeclarationsAndContexts))
0323             m_staticData->allDeclarationsSatisfied.remove(indexedTopContext());
0324         else
0325             m_staticData->allDeclarationsSatisfied.insert(indexedTopContext());
0326 
0327         if (!satisfied(features, TopDUContext::AllDeclarationsContextsAndUses))
0328             m_staticData->allDeclarationsAndUsesSatisfied.remove(indexedTopContext());
0329         else
0330             m_staticData->allDeclarationsAndUsesSatisfied.insert(indexedTopContext());
0331 
0332         if (!satisfied(features, TopDUContext::AST))
0333             m_staticData->ASTSatisfied.remove(indexedTopContext());
0334         else
0335             m_staticData->ASTSatisfied.insert(indexedTopContext());
0336     }
0337 }
0338 
0339 bool ParsingEnvironmentFile::featuresSatisfied(KDevelop::TopDUContext::Features minimumFeatures) const
0340 {
0341     ENSURE_READ_LOCKED
0342     QSet<const ParsingEnvironmentFile*> checked;
0343     if (minimumFeatures & TopDUContext::ForceUpdate)
0344         return false;
0345     return featuresMatch(minimumFeatures, checked);
0346 }
0347 
0348 void ParsingEnvironmentFile::clearModificationRevisions()
0349 {
0350     ENSURE_WRITE_LOCKED
0351         d_func_dynamic()->m_allModificationRevisions.clear();
0352     d_func_dynamic()->m_allModificationRevisions.addModificationRevision(d_func()->m_url, d_func()->m_modificationTime);
0353 }
0354 
0355 void ParsingEnvironmentFile::addModificationRevision(const IndexedString& url, const ModificationRevision& revision)
0356 {
0357     ENSURE_WRITE_LOCKED
0358         d_func_dynamic()->m_allModificationRevisions.addModificationRevision(url, revision);
0359     {
0360         //Test
0361         Q_ASSERT(d_func_dynamic()->m_allModificationRevisions.index());
0362         bool result = d_func_dynamic()->m_allModificationRevisions.removeModificationRevision(url, revision);
0363         Q_UNUSED(result);
0364         Q_ASSERT(result);
0365         d_func_dynamic()->m_allModificationRevisions.addModificationRevision(url, revision);
0366     }
0367 }
0368 
0369 void ParsingEnvironmentFile::setModificationRevision(const KDevelop::ModificationRevision& rev)
0370 {
0371     ENSURE_WRITE_LOCKED
0372 
0373         Q_ASSERT(d_func_dynamic()->m_allModificationRevisions.index());
0374     bool result = d_func_dynamic()->m_allModificationRevisions.removeModificationRevision(d_func()->m_url,
0375                                                                                           d_func()->m_modificationTime);
0376     Q_ASSERT(result);
0377     Q_UNUSED(result);
0378 
0379   #ifdef LEXERCACHE_DEBUG
0380     if (debugging()) {
0381         qCDebug(LANGUAGE) <<  id(this) << "setting modification-revision" << rev.toString();
0382     }
0383 #endif
0384     d_func_dynamic()->m_modificationTime = rev;
0385 #ifdef LEXERCACHE_DEBUG
0386     if (debugging()) {
0387         qCDebug(LANGUAGE) <<  id(this) << "new modification-revision" << m_modificationTime;
0388     }
0389 #endif
0390     d_func_dynamic()->m_allModificationRevisions.addModificationRevision(d_func()->m_url, d_func()->m_modificationTime);
0391 }
0392 
0393 KDevelop::ModificationRevision ParsingEnvironmentFile::modificationRevision() const
0394 {
0395     ENSURE_READ_LOCKED
0396     return d_func()->m_modificationTime;
0397 }
0398 
0399 IndexedString ParsingEnvironmentFile::language() const
0400 {
0401     return d_func()->m_language;
0402 }
0403 
0404 void ParsingEnvironmentFile::setLanguage(const IndexedString& language)
0405 {
0406     d_func_dynamic()->m_language = language;
0407 }
0408 
0409 const KDevelop::TopDUContext::IndexedRecursiveImports& ParsingEnvironmentFile::importsCache() const
0410 {
0411     return d_func()->m_importsCache;
0412 }
0413 
0414 void ParsingEnvironmentFile::setImportsCache(const KDevelop::TopDUContext::IndexedRecursiveImports& importsCache)
0415 {
0416     d_func_dynamic()->m_importsCache = importsCache;
0417 }
0418 } //KDevelop