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