• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdevelop API Reference
  • KDE Home
  • Contact Us
 

kdevplatform/language/duchain

  • sources
  • kfour-appscomplete
  • kdevelop
  • kdevplatform
  • language
  • duchain
duchain.cpp
Go to the documentation of this file.
1 /* This is part of KDevelop
2  Copyright 2006-2008 Hamish Rodda <[email protected]>
3  Copyright 2007-2008 David Nolden <[email protected]>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License version 2 as published by the Free Software Foundation.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18  */
19 
20 #include "duchain.h"
21 #include "duchainlock.h"
22 
23 #include <QCoreApplication>
24 #include <QHash>
25 #include <QMultiMap>
26 #include <QProcessEnvironment>
27 #include <QReadWriteLock>
28 #include <QAtomicInt>
29 #include <QThread>
30 #include <QStandardPaths>
31 #include <QMutex>
32 #include <QTimer>
33 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
34 #include <QRandomGenerator>
35 #endif
36 
37 #include <interfaces/idocumentcontroller.h>
38 #include <interfaces/icore.h>
39 #include <interfaces/ilanguagecontroller.h>
40 #include <interfaces/isession.h>
41 
42 #include "../interfaces/ilanguagesupport.h"
43 #include "../interfaces/icodehighlighting.h"
44 #include "../backgroundparser/backgroundparser.h"
45 #include <debug.h>
46 
47 #include "language-features.h"
48 #include "topducontext.h"
49 #include "topducontextdata.h"
50 #include "topducontextdynamicdata.h"
51 #include "parsingenvironment.h"
52 #include "declaration.h"
53 #include "definitions.h"
54 #include "duchainutils.h"
55 #include "use.h"
56 #include "uses.h"
57 #include "abstractfunctiondeclaration.h"
58 #include "duchainregister.h"
59 #include "persistentsymboltable.h"
60 #include "serialization/itemrepository.h"
61 #include "waitforupdate.h"
62 #include "importers.h"
63 
64 #if HAVE_MALLOC_TRIM
65 #include "malloc.h"
66 #endif
67 
68 namespace {
69 //Additional "soft" cleanup steps that are done before the actual cleanup.
70 //During "soft" cleanup, the consistency is not guaranteed. The repository is
71 //marked to be updating during soft cleanup, so if kdevelop crashes, it will be cleared.
72 //The big advantage of the soft cleanup steps is, that the duchain is always only locked for
73 //short times, which leads to no lockup in the UI.
74 const int SOFT_CLEANUP_STEPS = 1;
75 
76 // seconds to wait before trying to cleanup the DUChain
77 const uint cleanupEverySeconds = 200;
78 
80 const uint maxFinalCleanupCheckContexts = 2000;
81 const uint minimumFinalCleanupCheckContextsPercentage = 10; //Check at least n% of all top-contexts during cleanup
82 //Set to true as soon as the duchain is deleted
83 }
84 
85 namespace KDevelop {
86 bool DUChain::m_deleted = false;
87 
90 QMutex DUChain::chainsByIndexLock;
91 std::vector<TopDUContext*> DUChain::chainsByIndex;
92 
93 //This thing is not actually used, but it's needed for compiling
94 DEFINE_LIST_MEMBER_HASH(EnvironmentInformationListItem, items, uint)
95 
96 //An entry for the item-repository that holds some meta-data. Behind this entry, the actual ParsingEnvironmentFileData is stored.
97 class EnvironmentInformationItem
98 {
99 public:
100  EnvironmentInformationItem(uint topContext, uint size) : m_topContext(topContext)
101  , m_size(size)
102  {
103  }
104 
105  ~EnvironmentInformationItem()
106  {
107  }
108 
109  EnvironmentInformationItem& operator=(const EnvironmentInformationItem& rhs) = delete;
110 
111  unsigned int hash() const
112  {
113  return m_topContext;
114  }
115 
116  unsigned int itemSize() const
117  {
118  return sizeof(*this) + m_size;
119  }
120 
121  uint m_topContext;
122  uint m_size;//Size of the data behind, that holds the actual item
123 };
124 
125 struct ItemRepositoryIndexHash
126 {
127  uint
128  operator()(unsigned int __x) const
129  { return 173 * (__x >> 2) + 11 * (__x >> 16); }
130 };
131 
132 class EnvironmentInformationRequest
133 {
134 public:
135 
137  EnvironmentInformationRequest(uint topContextIndex) : m_file(nullptr)
138  , m_index(topContextIndex)
139  {
140  }
141 
142  EnvironmentInformationRequest(const ParsingEnvironmentFile* file) : m_file(file)
143  , m_index(file->indexedTopContext().index())
144  {
145  }
146 
147  enum {
148  AverageSize = 32 //This should be the approximate average size of an Item
149  };
150 
151  unsigned int hash() const
152  {
153  return m_index;
154  }
155 
156  uint itemSize() const
157  {
158  return sizeof(EnvironmentInformationItem) + DUChainItemSystem::self().dynamicSize(*m_file->d_func());
159  }
160 
161  void createItem(EnvironmentInformationItem* item) const
162  {
163  new (item) EnvironmentInformationItem(m_index, DUChainItemSystem::self().dynamicSize(*m_file->d_func()));
164  Q_ASSERT(m_file->d_func()->m_dynamic);
165  auto* data =
166  reinterpret_cast<DUChainBaseData*>(reinterpret_cast<char*>(item) + sizeof(EnvironmentInformationItem));
167  DUChainItemSystem::self().copy(*m_file->d_func(), *data, true);
168  Q_ASSERT(data->m_range == m_file->d_func()->m_range);
169  Q_ASSERT(data->classId == m_file->d_func()->classId);
170  Q_ASSERT(data->m_dynamic == false);
171  }
172 
173  static void destroy(EnvironmentInformationItem* item, KDevelop::AbstractItemRepository&)
174  {
175  item->~EnvironmentInformationItem();
176  //We don't need to call the destructor, because that's done in DUChainBase::makeDynamic()
177  //We just need to make sure that every environment-file is dynamic when it's deleted
178 // DUChainItemSystem::self().callDestructor((DUChainBaseData*)(((char*)item) + sizeof(EnvironmentInformationItem)));
179  }
180 
181  static bool persistent(const EnvironmentInformationItem*)
182  {
183  //Cleanup done separately
184  return true;
185  }
186 
187  bool equals(const EnvironmentInformationItem* item) const
188  {
189  return m_index == item->m_topContext;
190  }
191 
192  const ParsingEnvironmentFile* m_file;
193  uint m_index;
194 };
195 
197 class EnvironmentInformationListItem
198 {
199 public:
200  EnvironmentInformationListItem()
201  {
202  initializeAppendedLists(true);
203  }
204 
205  EnvironmentInformationListItem(const EnvironmentInformationListItem& rhs, bool dynamic = true)
206  {
207  initializeAppendedLists(dynamic);
208  m_file = rhs.m_file;
209  copyListsFrom(rhs);
210  }
211 
212  ~EnvironmentInformationListItem()
213  {
214  freeAppendedLists();
215  }
216 
217  EnvironmentInformationListItem& operator=(const EnvironmentInformationListItem& rhs) = delete;
218 
219  unsigned int hash() const
220  {
221  //We only compare the declaration. This allows us implementing a map, although the item-repository
222  //originally represents a set.
223  return m_file.hash();
224  }
225 
226  unsigned short int itemSize() const
227  {
228  return dynamicSize();
229  }
230 
231  IndexedString m_file;
232 
233  uint classSize() const
234  {
235  return sizeof(*this);
236  }
237 
238  START_APPENDED_LISTS(EnvironmentInformationListItem);
240  APPENDED_LIST_FIRST(EnvironmentInformationListItem, uint, items);
241  END_APPENDED_LISTS(EnvironmentInformationListItem, items);
242 };
243 
244 class EnvironmentInformationListRequest
245 {
246 public:
247 
249  EnvironmentInformationListRequest(const IndexedString& file) : m_file(file)
250  , m_item(nullptr)
251  {
252  }
254  EnvironmentInformationListRequest(const IndexedString& file, const EnvironmentInformationListItem& item) : m_file(
255  file)
256  , m_item(&item)
257  {
258  }
259 
260  enum {
261  AverageSize = 160 //This should be the approximate average size of an Item
262  };
263 
264  unsigned int hash() const
265  {
266  return m_file.hash();
267  }
268 
269  uint itemSize() const
270  {
271  return m_item->itemSize();
272  }
273 
274  void createItem(EnvironmentInformationListItem* item) const
275  {
276  Q_ASSERT(m_item->m_file == m_file);
277  new (item) EnvironmentInformationListItem(*m_item, false);
278  }
279 
280  static void destroy(EnvironmentInformationListItem* item, KDevelop::AbstractItemRepository&)
281  {
282  item->~EnvironmentInformationListItem();
283  }
284 
285  static bool persistent(const EnvironmentInformationListItem*)
286  {
287  //Cleanup is done separately
288  return true;
289  }
290 
291  bool equals(const EnvironmentInformationListItem* item) const
292  {
293  return m_file == item->m_file;
294  }
295 
296  IndexedString m_file;
297  const EnvironmentInformationListItem* m_item;
298 };
299 
300 class DUChainPrivate;
301 static DUChainPrivate* duChainPrivateSelf = nullptr;
302 class DUChainPrivate
303 {
304  class CleanupThread
305  : public QThread
306  {
307 public:
308  explicit CleanupThread(DUChainPrivate* data)
309  : m_data(data)
310  {
311  }
312 
313  void stopThread()
314  {
315  quit();
316  wait();
317  }
318 
319 private:
320  void run() override
321  {
322  QTimer timer;
323  connect(&timer, &QTimer::timeout, &timer, [this]() {
324  Q_ASSERT(QThread::currentThread() == this);
325  //Just to make sure the cache is cleared periodically
326  ModificationRevisionSet::clearCache();
327 
328  m_data->doMoreCleanup(SOFT_CLEANUP_STEPS, TryLock);
329  });
330  timer.start(cleanupEverySeconds * 1000);
331  exec();
332  }
333  DUChainPrivate* m_data;
334  };
335 
336 public:
337  DUChainPrivate() : m_chainsMutex(QMutex::Recursive)
338  , m_cleanupMutex(QMutex::Recursive)
339  , instance(nullptr)
340  , m_cleanupDisabled(false)
341  , m_destroyed(false)
342  , m_environmentListInfo(QStringLiteral("Environment Lists"))
343  , m_environmentInfo(QStringLiteral("Environment Information"))
344  {
345 #if defined(TEST_NO_CLEANUP)
346  m_cleanupDisabled = true;
347 #endif
348 
349  duChainPrivateSelf = this;
350  qRegisterMetaType<DUChainBasePointer>("KDevelop::DUChainBasePointer");
351  qRegisterMetaType<DUContextPointer>("KDevelop::DUContextPointer");
352  qRegisterMetaType<TopDUContextPointer>("KDevelop::TopDUContextPointer");
353  qRegisterMetaType<DeclarationPointer>("KDevelop::DeclarationPointer");
354  qRegisterMetaType<FunctionDeclarationPointer>("KDevelop::FunctionDeclarationPointer");
355  qRegisterMetaType<KDevelop::IndexedString>("KDevelop::IndexedString");
356  qRegisterMetaType<KDevelop::IndexedTopDUContext>("KDevelop::IndexedTopDUContext");
357  qRegisterMetaType<KDevelop::ReferencedTopDUContext>("KDevelop::ReferencedTopDUContext");
358 
359  instance = new DUChain();
360  m_cleanup = new CleanupThread(this);
361  m_cleanup->start();
362 
363  DUChain::m_deleted = false;
364 
366  {
368  QFile f(globalItemRepositoryRegistry().path() + QLatin1String("/parsing_environment_data"));
369  bool opened = f.open(QIODevice::ReadOnly);
371  ParsingEnvironmentFile::m_staticData =
372  reinterpret_cast<StaticParsingEnvironmentData*>(new char[sizeof(StaticParsingEnvironmentData)]);
373  if (opened) {
374  qCDebug(LANGUAGE) << "reading parsing-environment static data";
375  //Read
376  f.read(reinterpret_cast<char*>(ParsingEnvironmentFile::m_staticData), sizeof(StaticParsingEnvironmentData));
377  } else {
378  qCDebug(LANGUAGE) << "creating new parsing-environment static data";
379  //Initialize
380  new (ParsingEnvironmentFile::m_staticData) StaticParsingEnvironmentData();
381  }
382  }
383 
385  {
386  QFile f(globalItemRepositoryRegistry().path() + QLatin1String("/available_top_context_indices"));
387  bool opened = f.open(QIODevice::ReadOnly);
388  if (opened) {
389  Q_ASSERT((f.size() % sizeof(uint)) == 0);
390  m_availableTopContextIndices.resize(f.size() / ( int )sizeof(uint));
391  f.read(reinterpret_cast<char*>(m_availableTopContextIndices.data()), f.size());
392  }
393  }
394  }
395  ~DUChainPrivate()
396  {
397  qCDebug(LANGUAGE) << "Destroying";
398  DUChain::m_deleted = true;
399  m_cleanup->stopThread();
400  delete m_cleanup;
401  delete instance;
402  }
403 
404  void clear()
405  {
406  if (!m_cleanupDisabled)
407  doMoreCleanup();
408 
409  DUChainWriteLocker writeLock(DUChain::lock());
410 
411  QMutexLocker l(&m_chainsMutex);
412 
413  const auto currentChainsByUrl = m_chainsByUrl;
414  for (TopDUContext* top : currentChainsByUrl) {
415  removeDocumentChainFromMemory(top);
416  }
417 
418  m_indexEnvironmentInformations.clear();
419  m_fileEnvironmentInformations.clear();
420 
421  Q_ASSERT(m_fileEnvironmentInformations.isEmpty());
422  Q_ASSERT(m_chainsByUrl.isEmpty());
423  }
424 
427  void removeDocumentChainFromMemory(TopDUContext* context)
428  {
429  QMutexLocker l(&m_chainsMutex);
430 
431  {
432  QMutexLocker l(&m_referenceCountsMutex);
433 
434  auto countIt = m_referenceCounts.constFind(context);
435  if (countIt != m_referenceCounts.constEnd()) {
436  //This happens during shutdown, since everything is unloaded
437  qCDebug(LANGUAGE) << "removed a top-context that was reference-counted:" << context->url().str() <<
438  context->ownIndex();
439  m_referenceCounts.erase(countIt);
440  }
441  }
442 
443  uint index = context->ownIndex();
444 
445  // qCDebug(LANGUAGE) << "duchain: removing document" << context->url().str();
446  Q_ASSERT(hasChainForIndex(index));
447  Q_ASSERT(m_chainsByUrl.contains(context->url(), context));
448 
449  m_chainsByUrl.remove(context->url(), context);
450 
451  if (!context->isOnDisk())
452  instance->removeFromEnvironmentManager(context);
453 
454  l.unlock();
455  //DUChain is write-locked, so we can do whatever we want on the top-context, including deleting it
456  context->deleteSelf();
457  l.relock();
458 
459  Q_ASSERT(hasChainForIndex(index));
460 
461  QMutexLocker lock(&DUChain::chainsByIndexLock);
462  DUChain::chainsByIndex[index] = nullptr;
463  }
464 
467  QMutex m_chainsMutex;
468 
469  QMutex m_cleanupMutex;
470 
471  CleanupThread* m_cleanup;
472 
473  DUChain* instance;
474  DUChainLock lock;
475  QMultiMap<IndexedString, TopDUContext*> m_chainsByUrl;
476 
477  //Must be locked before accessing m_referenceCounts
478  QMutex m_referenceCountsMutex;
479  QHash<TopDUContext*, uint> m_referenceCounts;
480 
481  Definitions m_definitions;
482  Uses m_uses;
483  QSet<uint> m_loading;
484  bool m_cleanupDisabled;
485 
486  //List of available top-context indices, protected by m_chainsMutex
487  QVector<uint> m_availableTopContextIndices;
488 
490  QSet<ReferencedTopDUContext> m_openDocumentContexts;
491 
492  bool m_destroyed;
493 
496  void addEnvironmentInformation(ParsingEnvironmentFilePointer info)
497  {
498  Q_ASSERT(!findInformation(info->indexedTopContext().index()));
499  Q_ASSERT(m_environmentInfo.findIndex(info->indexedTopContext().index()) == 0);
500 
501  QMutexLocker lock(&m_chainsMutex);
502  m_fileEnvironmentInformations.insert(info->url(), info);
503 
504  m_indexEnvironmentInformations.insert(info->indexedTopContext().index(), info);
505 
506  Q_ASSERT(info->d_func()->classId);
507  }
508 
511  void removeEnvironmentInformation(ParsingEnvironmentFilePointer info)
512  {
513  info->makeDynamic(); //By doing this, we make sure the data is actually being destroyed in the destructor
514 
515  bool removed = false;
516  bool removed2 = false;
517  {
518  QMutexLocker lock(&m_chainsMutex);
519  removed = m_fileEnvironmentInformations.remove(info->url(), info);
520  removed2 = m_indexEnvironmentInformations.remove(info->indexedTopContext().index());
521  }
522 
523  {
524  //Remove it from the environment information lists if it was there
525  QMutexLocker lock(m_environmentListInfo.mutex());
526  uint index = m_environmentListInfo.findIndex(info->url());
527 
528  if (index) {
529  EnvironmentInformationListItem item(*m_environmentListInfo.itemFromIndex(index));
530  if (item.itemsList().removeOne(info->indexedTopContext().index())) {
531  m_environmentListInfo.deleteItem(index);
532  if (!item.itemsList().isEmpty())
533  m_environmentListInfo.index(EnvironmentInformationListRequest(info->url(), item));
534  }
535  }
536  }
537 
538  QMutexLocker lock(m_environmentInfo.mutex());
539  uint index = m_environmentInfo.findIndex(info->indexedTopContext().index());
540  if (index) {
541  m_environmentInfo.deleteItem(index);
542  }
543 
544  Q_UNUSED(removed);
545  Q_UNUSED(removed2);
546  Q_ASSERT(index || (removed && removed2));
547  Q_ASSERT(!findInformation(info->indexedTopContext().index()));
548  }
549 
551  QList<ParsingEnvironmentFilePointer> getEnvironmentInformation(const IndexedString& url)
552  {
553  QList<ParsingEnvironmentFilePointer> ret;
554  uint listIndex = m_environmentListInfo.findIndex(url);
555 
556  if (listIndex) {
557  KDevVarLengthArray<uint> topContextIndices;
558 
559  {
560  //First store all the possible indices into the KDevVarLengthArray, so we can unlock the mutex before processing them.
561 
562  QMutexLocker lock(m_environmentListInfo.mutex()); //Lock the mutex to make sure the item isn't changed while it's being iterated
563  const EnvironmentInformationListItem* item = m_environmentListInfo.itemFromIndex(listIndex);
564  FOREACH_FUNCTION(uint topContextIndex, item->items)
565  topContextIndices << topContextIndex;
566  }
567 
568  //Process the indices in a separate step after copying them from the array, so we don't need m_environmentListInfo.mutex locked,
569  //and can call loadInformation(..) safely, which else might lead to a deadlock.
570  for (uint topContextIndex : qAsConst(topContextIndices)) {
571  QExplicitlySharedDataPointer<ParsingEnvironmentFile> p =
572  ParsingEnvironmentFilePointer(loadInformation(topContextIndex));
573  if (p) {
574  ret << p;
575  } else {
576  qCDebug(LANGUAGE) << "Failed to load environment-information for" <<
577  TopDUContextDynamicData::loadUrl(topContextIndex).str();
578  }
579  }
580  }
581 
582  QMutexLocker l(&m_chainsMutex);
583 
584  //Add those information that have not been added to the stored lists yet
585  const auto files = m_fileEnvironmentInformations.values(url);
586  for (const ParsingEnvironmentFilePointer& file : files) {
587  if (!ret.contains(file))
588  ret << file;
589  }
590 
591  return ret;
592  }
593 
595  static inline bool hasChainForIndex(uint index)
596  {
597  QMutexLocker lock(&DUChain::chainsByIndexLock);
598  return (DUChain::chainsByIndex.size() > index) && DUChain::chainsByIndex[index];
599  }
600 
602  static inline TopDUContext* readChainForIndex(uint index)
603  {
604  QMutexLocker lock(&DUChain::chainsByIndexLock);
605  if (DUChain::chainsByIndex.size() > index)
606  return DUChain::chainsByIndex[index];
607  else
608  return nullptr;
609  }
610 
613  void loadChain(uint index, QSet<uint>& loaded)
614  {
615  QMutexLocker l(&m_chainsMutex);
616 
617  if (!hasChainForIndex(index)) {
618  if (m_loading.contains(index)) {
619  //It's probably being loaded by another thread. So wait until the load is ready
620  while (m_loading.contains(index)) {
621  l.unlock();
622  qCDebug(LANGUAGE) << "waiting for another thread to load index" << index;
623  QThread::usleep(50000);
624  l.relock();
625  }
626  loaded.insert(index);
627  return;
628  }
629  m_loading.insert(index);
630  loaded.insert(index);
631 
632  l.unlock();
633  qCDebug(LANGUAGE) << "loading top-context" << index;
634  TopDUContext* chain = TopDUContextDynamicData::load(index);
635  if (chain) {
636  chain->setParsingEnvironmentFile(loadInformation(chain->ownIndex()));
637 
638  if (!chain->usingImportsCache()) {
639  //Eventually also load all the imported chains, so the import-structure is built
640  const auto importedParentContexts = chain->DUContext::importedParentContexts();
641  for (const DUContext::Import& import : importedParentContexts) {
642  if (!loaded.contains(import.topContextIndex())) {
643  loadChain(import.topContextIndex(), loaded);
644  }
645  }
646  }
647  chain->rebuildDynamicImportStructure();
648 
649  chain->setInDuChain(true);
650  instance->addDocumentChain(chain);
651  }
652 
653  l.relock();
654  m_loading.remove(index);
655  }
656  }
657 
661  void storeAllInformation(bool atomic, DUChainWriteLocker& locker)
662  {
663  uint cnt = 0;
664 
665  QList<IndexedString> urls;
666  {
667  QMutexLocker lock(&m_chainsMutex);
668  urls += m_fileEnvironmentInformations.keys();
669  }
670 
671  for (const IndexedString& url : qAsConst(urls)) {
672  QList<ParsingEnvironmentFilePointer> check;
673  {
674  QMutexLocker lock(&m_chainsMutex);
675  check = m_fileEnvironmentInformations.values(url);
676  }
677 
678  for (const ParsingEnvironmentFilePointer& file : qAsConst(check)) {
679  EnvironmentInformationRequest req(file.data());
680  QMutexLocker lock(m_environmentInfo.mutex());
681  uint index = m_environmentInfo.findIndex(req);
682 
683  if (file->d_func()->isDynamic()) {
684  //This item has been changed, or isn't in the repository yet
685 
686  //Eventually remove an old entry
687  if (index)
688  m_environmentInfo.deleteItem(index);
689 
690  //Add the new entry to the item repository
691  index = m_environmentInfo.index(req);
692  Q_ASSERT(index);
693 
694  auto* item =
695  const_cast<EnvironmentInformationItem*>(m_environmentInfo.itemFromIndex(index));
696  auto* theData =
697  reinterpret_cast<DUChainBaseData*>(reinterpret_cast<char*>(item) +
698  sizeof(EnvironmentInformationItem));
699 
700  Q_ASSERT(theData->m_range == file->d_func()->m_range);
701  Q_ASSERT(theData->m_dynamic == false);
702  Q_ASSERT(theData->classId == file->d_func()->classId);
703 
704  file->setData(theData);
705 
706  ++cnt;
707  } else {
708  m_environmentInfo.itemFromIndex(index); //Prevent unloading of the data, by accessing the item
709  }
710  }
711 
714  if (!atomic && (cnt % 100 == 0)) {
715  //Release the lock on a regular basis
716  locker.unlock();
717  locker.lock();
718  }
719 
720  storeInformationList(url);
721 
722  //Access the data in the repository, so the bucket isn't unloaded
723  uint index = m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url));
724  if (index) {
725  m_environmentListInfo.itemFromIndex(index);
726  } else {
727  QMutexLocker lock(&m_chainsMutex);
728  qCDebug(LANGUAGE) << "Did not find stored item for" << url.str() << "count:" <<
729  m_fileEnvironmentInformations.values(url);
730  }
731  if (!atomic) {
732  locker.unlock();
733  locker.lock();
734  }
735  }
736  }
737 
738  QMutex& cleanupMutex()
739  {
740  return m_cleanupMutex;
741  }
742 
744  enum LockFlag {
746  NoLock = 0,
748  BlockingLock = 1,
750  TryLock = 2,
751  };
756  void doMoreCleanup(int retries = 0, LockFlag lockFlag = BlockingLock)
757  {
758  if (m_cleanupDisabled)
759  return;
760 
761  //This mutex makes sure that there's never 2 threads at he same time trying to clean up
762  QMutexLocker lockCleanupMutex(&cleanupMutex());
763 
764  if (m_destroyed || m_cleanupDisabled)
765  return;
766 
767  Q_ASSERT(!instance->lock()->currentThreadHasReadLock() && !instance->lock()->currentThreadHasWriteLock());
768  DUChainWriteLocker writeLock(instance->lock());
769 
770  //This is used to stop all parsing before starting to do the cleanup. This way less happens during the
771  //soft cleanups, and we have a good chance that during the "hard" cleanup only few data has to be written.
772  QList<QReadWriteLock*> locked;
773 
774  if (lockFlag != NoLock) {
775  QList<ILanguageSupport*> languages;
776  if (ICore* core = ICore::self())
777  if (ILanguageController* lc = core->languageController())
778  languages = lc->loadedLanguages();
779 
780  writeLock.unlock();
781 
782  //Here we wait for all parsing-threads to stop their processing
783  for (const auto language : qAsConst(languages)) {
784  if (lockFlag == TryLock) {
785  if (!language->parseLock()->tryLockForWrite()) {
786  qCDebug(LANGUAGE) << "Aborting cleanup because language plugin is still parsing:" <<
787  language->name();
788  // some language is still parsing, don't interfere with the cleanup
789  for (auto* lock : qAsConst(locked)) {
790  lock->unlock();
791  }
792 
793  return;
794  }
795  } else {
796  language->parseLock()->lockForWrite();
797  }
798  locked << language->parseLock();
799  }
800 
801  writeLock.lock();
802 
803  globalItemRepositoryRegistry().lockForWriting();
804  qCDebug(LANGUAGE) << "starting cleanup";
805  }
806 
807  QTime startTime = QTime::currentTime();
808  PersistentSymbolTable::self().clearCache();
809 
810  storeAllInformation(!retries, writeLock); //Puts environment-information into a repository
811 
812  //We don't need to increase the reference-count, since the cleanup-mutex is locked
813  QSet<TopDUContext*> workOnContexts;
814 
815  {
816  QMutexLocker l(&m_chainsMutex);
817 
818  workOnContexts.reserve(m_chainsByUrl.size());
819  for (TopDUContext* top : qAsConst(m_chainsByUrl)) {
820  workOnContexts << top;
821  Q_ASSERT(hasChainForIndex(top->ownIndex()));
822  }
823  }
824 
825  for (TopDUContext* context : qAsConst(workOnContexts)) {
826  context->m_dynamicData->store();
827 
828  if (retries) {
829  //Eventually give other threads a chance to access the duchain
830  writeLock.unlock();
831  //Sleep to give the other threads a realistic chance to get a read-lock in between
832  QThread::usleep(500);
833  writeLock.lock();
834  }
835  }
836 
837  //Unload all top-contexts that don't have a reference-count and that are not imported by a referenced one
838 
839  QSet<IndexedString> unloadedNames;
840  bool unloadedOne = true;
841 
842  bool unloadAllUnreferenced = !retries;
843 
844  //Now unload contexts, but only ones that are not imported by any other currently loaded context
845  //The complication: Since during the lock-break new references may be added, we must never keep
846  //the du-chain in an invalid state. Thus we can only unload contexts that are not imported by any
847  //currently loaded contexts. In case of loops, we have to unload everything at once.
848  while (unloadedOne) {
849  unloadedOne = false;
850  int hadUnloadable = 0;
851 
852 unloadContexts:
853 
854  const auto currentWorkOnContexts = workOnContexts;
855  for (TopDUContext * unload : currentWorkOnContexts) {
856  bool hasReference = false;
857 
858  {
859  QMutexLocker l(&m_referenceCountsMutex);
860  //Test if the context is imported by a referenced one
861  for (auto it = m_referenceCounts.constBegin(), end = m_referenceCounts.constEnd(); it != end;
862  ++it) {
863  auto* context = it.key();
864  if (context == unload || context->imports(unload, CursorInRevision())) {
865  workOnContexts.remove(unload);
866  hasReference = true;
867  }
868  }
869  }
870 
871  if (!hasReference)
872  ++hadUnloadable; //We have found a context that is not referenced
873  else
874  continue; //This context is referenced
875 
876  bool isImportedByLoaded = !unload->loadedImporters().isEmpty();
877 
878  //If we unload a context that is imported by other contexts, we create a bad loaded state
879  if (isImportedByLoaded && !unloadAllUnreferenced)
880  continue;
881 
882  unloadedNames.insert(unload->url());
883  //Since we've released the write-lock in between, we've got to call store() again to be sure that none of the data is dynamic
884  //If nothing has changed, it is only a low-cost call.
885  unload->m_dynamicData->store();
886  Q_ASSERT(!unload->d_func()->m_dynamic);
887  removeDocumentChainFromMemory(unload);
888  workOnContexts.remove(unload);
889  unloadedOne = true;
890 
891  if (!unloadAllUnreferenced) {
892  //Eventually give other threads a chance to access the duchain
893  writeLock.unlock();
894  //Sleep to give the other threads a realistic chance to get a read-lock in between
895  QThread::usleep(500);
896  writeLock.lock();
897  }
898  }
899 
900  if (hadUnloadable && !unloadedOne) {
901  Q_ASSERT(!unloadAllUnreferenced);
902  //This can happen in case of loops. We have o unload everything at one time.
903  qCDebug(LANGUAGE) << "found" << hadUnloadable <<
904  "unloadable contexts, but could not unload separately. Unloading atomically.";
905  unloadAllUnreferenced = true;
906  hadUnloadable = 0; //Reset to 0, so we cannot loop forever
907  goto unloadContexts;
908  }
909  }
910 
911  if (retries == 0) {
912  QMutexLocker lock(&m_chainsMutex);
913  //Do this atomically, since we must be sure that _everything_ is already saved
914  for (QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator it =
915  m_fileEnvironmentInformations.begin();
916  it != m_fileEnvironmentInformations.end();) {
917  ParsingEnvironmentFile* f = it->data();
918  Q_ASSERT(f->d_func()->classId);
919 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
920  if (f->ref.loadRelaxed() == 1) {
921 #else
922  if (f->ref.load() == 1) {
923 #endif
924  Q_ASSERT(!f->d_func()->isDynamic()); //It cannot be dynamic, since we have stored before
925  //The ParsingEnvironmentFilePointer is only referenced once. This means that it does not belong to any
926  //loaded top-context, so just remove it to save some memory and processing time.
928  it = m_fileEnvironmentInformations.erase(it);
929  } else {
930  ++it;
931  }
932  }
933  }
934 
935  if (retries)
936  writeLock.unlock();
937 
938  //This must be the last step, due to the on-disk reference counting
939  globalItemRepositoryRegistry().store(); //Stores all repositories
940 
941  {
942  //Store the static parsing-environment file data
944  Q_ASSERT(ParsingEnvironmentFile::m_staticData);
945  QFile f(globalItemRepositoryRegistry().path() + QLatin1String("/parsing_environment_data"));
946  bool opened = f.open(QIODevice::WriteOnly);
947  Q_ASSERT(opened);
948  Q_UNUSED(opened);
949  f.write(reinterpret_cast<const char*>(ParsingEnvironmentFile::m_staticData), sizeof(StaticParsingEnvironmentData));
950  }
951 
953  {
954  QMutexLocker lock(&m_chainsMutex);
955 
956  QFile f(globalItemRepositoryRegistry().path() + QLatin1String("/available_top_context_indices"));
957  bool opened = f.open(QIODevice::WriteOnly);
958  Q_ASSERT(opened);
959  Q_UNUSED(opened);
960 
961  f.write(reinterpret_cast<const char*>(m_availableTopContextIndices.data()), m_availableTopContextIndices.size() * sizeof(uint));
962  }
963 
964  if (retries) {
965  doMoreCleanup(retries - 1, NoLock);
966  writeLock.lock();
967  }
968 
969  if (lockFlag != NoLock) {
970  globalItemRepositoryRegistry().unlockForWriting();
971 
972  const auto elapsedMS = startTime.msecsTo(QTime::currentTime());
973  qCDebug(LANGUAGE) << "time spent doing cleanup:" << elapsedMS << "ms - top-contexts still open:" <<
974  m_chainsByUrl.size() << "- retries" << retries;
975  }
976 
977  for (QReadWriteLock* lock : qAsConst(locked)) {
978  lock->unlock();
979  }
980 
981 #if HAVE_MALLOC_TRIM
982  // trim unused memory but keep a pad buffer of about 50 MB
983  // this can greatly decrease the perceived memory consumption of kdevelop
984  // see: https://sourceware.org/bugzilla/show_bug.cgi?id=14827
985  malloc_trim(50 * 1024 * 1024);
986 #endif
987  }
988 
990  ParsingEnvironmentFile* findInformation(uint topContextIndex)
991  {
992  QMutexLocker lock(&m_chainsMutex);
993  QHash<uint, ParsingEnvironmentFilePointer>::iterator it = m_indexEnvironmentInformations.find(topContextIndex);
994  if (it != m_indexEnvironmentInformations.end())
995  return (*it).data();
996  return nullptr;
997  }
998 
1002  ParsingEnvironmentFile* loadInformation(uint topContextIndex)
1003  {
1004  ParsingEnvironmentFile* alreadyLoaded = findInformation(topContextIndex);
1005  if (alreadyLoaded)
1006  return alreadyLoaded;
1007 
1008  //Step two: Check if it is on disk, and if is, load it
1009  uint dataIndex = m_environmentInfo.findIndex(EnvironmentInformationRequest(topContextIndex));
1010  if (!dataIndex) {
1011  //No environment-information stored for this top-context
1012  return nullptr;
1013  }
1014 
1015  const EnvironmentInformationItem& item(*m_environmentInfo.itemFromIndex(dataIndex));
1016 
1017  QMutexLocker lock(&m_chainsMutex);
1018 
1019  //Due to multi-threading, we must do this check after locking the mutex, so we can be sure we don't create the same item twice at the same time
1020  alreadyLoaded = findInformation(topContextIndex);
1021  if (alreadyLoaded)
1022  return alreadyLoaded;
1023 
1025  auto* ret = dynamic_cast<ParsingEnvironmentFile*>(DUChainItemSystem::self().create(
1026  const_cast<DUChainBaseData*>(
1027  reinterpret_cast<const
1028  DUChainBaseData*>(
1029  reinterpret_cast<const char*>(&
1030  item)
1031  +
1032  sizeof(
1033  EnvironmentInformationItem)))
1034  ));
1035  if (ret) {
1036  Q_ASSERT(ret->d_func()->classId);
1037  Q_ASSERT(ret->indexedTopContext().index() == topContextIndex);
1038  ParsingEnvironmentFilePointer retPtr(ret);
1039 
1040  m_fileEnvironmentInformations.insert(ret->url(), retPtr);
1041 
1042  Q_ASSERT(!m_indexEnvironmentInformations.contains(ret->indexedTopContext().index()));
1043  m_indexEnvironmentInformations.insert(ret->indexedTopContext().index(), retPtr);
1044  }
1045  return ret;
1046  }
1047 
1048  struct CleanupListVisitor
1049  {
1050  QList<uint> checkContexts;
1051  bool operator()(const EnvironmentInformationItem* item)
1052  {
1053  checkContexts << item->m_topContext;
1054  return true;
1055  }
1056  };
1057 
1059  void cleanupTopContexts()
1060  {
1061  DUChainWriteLocker lock(DUChain::lock());
1062  qCDebug(LANGUAGE) << "cleaning top-contexts";
1063  CleanupListVisitor visitor;
1064  uint startPos = 0;
1065  m_environmentInfo.visitAllItems(visitor);
1066 
1067  int checkContextsCount = maxFinalCleanupCheckContexts;
1068  int percentageOfContexts = (visitor.checkContexts.size() * 100) / minimumFinalCleanupCheckContextsPercentage;
1069 
1070  if (checkContextsCount < percentageOfContexts)
1071  checkContextsCount = percentageOfContexts;
1072 
1073  if (visitor.checkContexts.size() > ( int )checkContextsCount)
1074 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
1075  startPos = QRandomGenerator::global()->bounded(visitor.checkContexts.size() - checkContextsCount);
1076 #else
1077  startPos = qrand() % (visitor.checkContexts.size() - checkContextsCount);
1078 #endif
1079 
1080  int endPos = startPos + maxFinalCleanupCheckContexts;
1081  if (endPos > visitor.checkContexts.size())
1082  endPos = visitor.checkContexts.size();
1083  QSet<uint> check;
1084  for (int a = startPos; a < endPos && check.size() < checkContextsCount; ++a)
1085  if (check.size() < checkContextsCount)
1086  addContextsForRemoval(check, IndexedTopDUContext(visitor.checkContexts[a]));
1087 
1088  for (uint topIndex : qAsConst(check)) {
1089  IndexedTopDUContext top(topIndex);
1090  if (top.data()) {
1091  qCDebug(LANGUAGE) << "removing top-context for" << top.data()->url().str() <<
1092  "because it is out of date";
1093  instance->removeDocumentChain(top.data());
1094  }
1095  }
1096 
1097  qCDebug(LANGUAGE) << "check ready";
1098  }
1099 
1100 private:
1101 
1102  void addContextsForRemoval(QSet<uint>& topContexts, IndexedTopDUContext top)
1103  {
1104  if (topContexts.contains(top.index()))
1105  return;
1106 
1107  QExplicitlySharedDataPointer<ParsingEnvironmentFile> info(instance->environmentFileForDocument(top));
1109  if (info && info->needsUpdate()) {
1110  //This context will be removed
1111  } else {
1112  return;
1113  }
1114 
1115  topContexts.insert(top.index());
1116 
1117  if (info) {
1118  //Check whether importers need to be removed as well
1119  const QList<QExplicitlySharedDataPointer<ParsingEnvironmentFile>> importers = info->importers();
1120 
1121  QSet<QExplicitlySharedDataPointer<ParsingEnvironmentFile>> checkNext;
1122 
1123  //Do breadth first search, so less imports/importers have to be loaded, and a lower depth is reached
1124 
1125  for (auto& importer : importers) {
1126  IndexedTopDUContext c = importer->indexedTopContext();
1127  if (!topContexts.contains(c.index())) {
1128  topContexts.insert(c.index()); //Prevent useless recursion
1129  checkNext.insert(importer);
1130  }
1131  }
1132 
1133  for (auto& parsingEnvFile : qAsConst(checkNext)) {
1134  topContexts.remove(parsingEnvFile->indexedTopContext().index()); // Enable full check again
1135  addContextsForRemoval(topContexts, parsingEnvFile->indexedTopContext());
1136  }
1137  }
1138  }
1139 
1141  void storeInformationList(const IndexedString& url)
1142  {
1143  QMutexLocker lock(m_environmentListInfo.mutex());
1144 
1145  EnvironmentInformationListItem newItem;
1146  newItem.m_file = url;
1147 
1148  QSet<uint> newItems;
1149 
1150  {
1151  QMutexLocker lock(&m_chainsMutex);
1152  QMultiMap<IndexedString,
1153  ParsingEnvironmentFilePointer>::iterator start = m_fileEnvironmentInformations.lowerBound(url);
1154  QMultiMap<IndexedString,
1155  ParsingEnvironmentFilePointer>::iterator end = m_fileEnvironmentInformations.upperBound(url);
1156 
1157  for (QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator it = start; it != end; ++it) {
1158  uint topContextIndex = (*it)->indexedTopContext().index();
1159  newItems.insert(topContextIndex);
1160  newItem.itemsList().append(topContextIndex);
1161  }
1162  }
1163 
1164  uint index = m_environmentListInfo.findIndex(url);
1165 
1166  if (index) {
1167  //We only handle adding items here, since we can never be sure whether everything is loaded
1168  //Removal is handled directly in removeEnvironmentInformation
1169 
1170  const EnvironmentInformationListItem* item = m_environmentListInfo.itemFromIndex(index);
1171  QSet<uint> oldItems;
1172  FOREACH_FUNCTION(uint topContextIndex, item->items) {
1173  oldItems.insert(topContextIndex);
1174  if (!newItems.contains(topContextIndex)) {
1175  newItems.insert(topContextIndex);
1176  newItem.itemsList().append(topContextIndex);
1177  }
1178  }
1179 
1180  if (oldItems == newItems)
1181  return;
1182 
1184  m_environmentListInfo.deleteItem(index); //Remove the previous item
1185  }
1186 
1187  Q_ASSERT(m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url)) == 0);
1188 
1189  //Insert the new item
1190  m_environmentListInfo.index(EnvironmentInformationListRequest(url, newItem));
1191 
1192  Q_ASSERT(m_environmentListInfo.findIndex(EnvironmentInformationListRequest(url)));
1193  }
1194 
1195  //Loaded environment information. Protected by m_chainsMutex
1196  QMultiMap<IndexedString, ParsingEnvironmentFilePointer> m_fileEnvironmentInformations;
1197  QHash<uint, ParsingEnvironmentFilePointer> m_indexEnvironmentInformations;
1198 
1202  ItemRepository<EnvironmentInformationListItem, EnvironmentInformationListRequest> m_environmentListInfo;
1204  ItemRepository<EnvironmentInformationItem, EnvironmentInformationRequest> m_environmentInfo;
1205 };
1206 
1207 Q_GLOBAL_STATIC(DUChainPrivate, sdDUChainPrivate)
1208 
1209 DUChain::DUChain()
1210 {
1211  Q_ASSERT(ICore::self());
1212 
1213  connect(
1214  ICore::self()->documentController(), &IDocumentController::documentLoadedPrepare, this,
1215  &DUChain::documentLoadedPrepare);
1216  connect(
1217  ICore::self()->documentController(), &IDocumentController::documentUrlChanged, this,
1218  &DUChain::documentRenamed);
1219  connect(
1220  ICore::self()->documentController(), &IDocumentController::documentActivated, this,
1221  &DUChain::documentActivated);
1222  connect(ICore::self()->documentController(), &IDocumentController::documentClosed, this, &DUChain::documentClosed);
1223 }
1224 
1225 DUChain::~DUChain()
1226 {
1227  DUChain::m_deleted = true;
1228 }
1229 
1230 DUChain* DUChain::self()
1231 {
1232  return sdDUChainPrivate->instance;
1233 }
1234 
1235 extern void initModificationRevisionSetRepository();
1236 extern void initDeclarationRepositories();
1237 extern void initIdentifierRepository();
1238 extern void initTypeRepository();
1239 extern void initInstantiationInformationRepository();
1240 
1241 QString DUChain::repositoryPathForSession(const KDevelop::ISessionLock::Ptr& session)
1242 {
1243  QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
1244  cacheDir += QLatin1String("/kdevduchain");
1245  QString baseDir = QProcessEnvironment::systemEnvironment().value(QStringLiteral("KDEV_DUCHAIN_DIR"), cacheDir);
1246  baseDir += QStringLiteral("/%1-%2").arg(QCoreApplication::applicationName(), session->id());
1247  return baseDir;
1248 }
1249 
1250 void DUChain::initialize()
1251 {
1252  // Initialize the global item repository as first thing after loading the session
1253  Q_ASSERT(ICore::self());
1254  Q_ASSERT(ICore::self()->activeSession());
1255 
1256  ItemRepositoryRegistry::initialize(repositoryPathForSession(ICore::self()->activeSessionLock()));
1257 
1258  initReferenceCounting();
1259 
1260  // This needs to be initialized here too as the function is not threadsafe, but can
1261  // sometimes be called from different threads. This results in the underlying QFile
1262  // being 0 and hence crashes at some point later when accessing the contents via
1263  // read. See https://bugs.kde.org/show_bug.cgi?id=250779
1264  RecursiveImportRepository::repository();
1265  RecursiveImportCacheRepository::repository();
1266 
1267  // similar to above, see https://bugs.kde.org/show_bug.cgi?id=255323
1268  initDeclarationRepositories();
1269 
1270  initModificationRevisionSetRepository();
1271  initIdentifierRepository();
1272  initTypeRepository();
1273  initInstantiationInformationRepository();
1274 
1275  Importers::self();
1276 
1277  globalImportIdentifier();
1278  globalIndexedImportIdentifier();
1279  globalAliasIdentifier();
1280  globalIndexedAliasIdentifier();
1281 }
1282 
1283 DUChainLock* DUChain::lock()
1284 {
1285  return &sdDUChainPrivate->lock;
1286 }
1287 
1288 QList<TopDUContext*> DUChain::allChains() const
1289 {
1290  QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
1291  return sdDUChainPrivate->m_chainsByUrl.values();
1292 }
1293 
1294 void DUChain::updateContextEnvironment(TopDUContext* context, ParsingEnvironmentFile* file)
1295 {
1296  QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
1297 
1298  removeFromEnvironmentManager(context);
1299 
1300  context->setParsingEnvironmentFile(file);
1301 
1302  addToEnvironmentManager(context);
1303 }
1304 
1305 void DUChain::removeDocumentChain(TopDUContext* context)
1306 {
1307  ENSURE_CHAIN_WRITE_LOCKED;
1308  IndexedTopDUContext indexed(context->indexed());
1309  Q_ASSERT(indexed.data() == context);
1310  context->m_dynamicData->deleteOnDisk();
1311  Q_ASSERT(indexed.data() == context);
1312  sdDUChainPrivate->removeDocumentChainFromMemory(context);
1313  Q_ASSERT(!indexed.data());
1314  Q_ASSERT(!environmentFileForDocument(indexed));
1315 
1316  QMutexLocker lock(&sdDUChainPrivate->m_chainsMutex);
1317  sdDUChainPrivate->m_availableTopContextIndices.push_back(indexed.index());
1318 }
1319 
1320 void DUChain::addDocumentChain(TopDUContext* chain)
1321 {
1322  QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
1323 
1324 // qCDebug(LANGUAGE) << "duchain: adding document" << chain->url().str() << " " << chain;
1325  Q_ASSERT(chain);
1326 
1327  Q_ASSERT(!sdDUChainPrivate->hasChainForIndex(chain->ownIndex()));
1328 
1329  {
1330  QMutexLocker lock(&DUChain::chainsByIndexLock);
1331  if (DUChain::chainsByIndex.size() <= chain->ownIndex())
1332  DUChain::chainsByIndex.resize(chain->ownIndex() + 100, nullptr);
1333 
1334  DUChain::chainsByIndex[chain->ownIndex()] = chain;
1335  }
1336  {
1337  Q_ASSERT(DUChain::chainsByIndex[chain->ownIndex()]);
1338  }
1339  Q_ASSERT(sdDUChainPrivate->hasChainForIndex(chain->ownIndex()));
1340 
1341  sdDUChainPrivate->m_chainsByUrl.insert(chain->url(), chain);
1342 
1343  Q_ASSERT(sdDUChainPrivate->hasChainForIndex(chain->ownIndex()));
1344 
1345  chain->setInDuChain(true);
1346 
1347  l.unlock();
1348 
1349  addToEnvironmentManager(chain);
1350 
1351  // This function might be called during shutdown by stale parse jobs
1352  // Make sure we don't access null-pointers here
1353  if (ICore::self() && ICore::self()->languageController() &&
1354  ICore::self()->languageController()->backgroundParser()->trackerForUrl(chain->url())) {
1355  //Make sure the context stays alive at least as long as the context is open
1356  ReferencedTopDUContext ctx(chain);
1357  sdDUChainPrivate->m_openDocumentContexts.insert(ctx);
1358  }
1359 }
1360 
1361 void DUChain::addToEnvironmentManager(TopDUContext* chain)
1362 {
1363  ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile();
1364  if (!file)
1365  return; //We don't need to manage
1366 
1367  Q_ASSERT(file->indexedTopContext().index() == chain->ownIndex());
1368 
1369  if (ParsingEnvironmentFile* alreadyHave = sdDUChainPrivate->findInformation(file->indexedTopContext().index())) {
1372  Q_ASSERT(alreadyHave == file.data());
1373  Q_UNUSED(alreadyHave);
1374  return;
1375  }
1376 
1377  sdDUChainPrivate->addEnvironmentInformation(file);
1378 }
1379 
1380 void DUChain::removeFromEnvironmentManager(TopDUContext* chain)
1381 {
1382  ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile();
1383  if (!file)
1384  return; //We don't need to manage
1385 
1386  sdDUChainPrivate->removeEnvironmentInformation(file);
1387 }
1388 
1389 TopDUContext* DUChain::chainForDocument(const QUrl& document, bool proxyContext) const
1390 {
1391  return chainForDocument(IndexedString(document), proxyContext);
1392 }
1393 
1394 bool DUChain::isInMemory(uint topContextIndex) const
1395 {
1396  return DUChainPrivate::hasChainForIndex(topContextIndex);
1397 }
1398 
1399 IndexedString DUChain::urlForIndex(uint index) const
1400 {
1401  {
1402  TopDUContext* chain = DUChainPrivate::readChainForIndex(index);
1403  if (chain)
1404  return chain->url();
1405  }
1406 
1407  return TopDUContextDynamicData::loadUrl(index);
1408 }
1409 
1410 TopDUContext* DUChain::loadChain(uint index)
1411 {
1412  QSet<uint> loaded;
1413  sdDUChainPrivate->loadChain(index, loaded);
1414 
1415  {
1416  QMutexLocker lock(&chainsByIndexLock);
1417 
1418  if (chainsByIndex.size() > index) {
1419  TopDUContext* top = chainsByIndex[index];
1420  if (top)
1421  return top;
1422  }
1423  }
1424 
1425  return nullptr;
1426 }
1427 
1428 TopDUContext* DUChain::chainForDocument(const KDevelop::IndexedString& document, bool proxyContext) const
1429 {
1430  ENSURE_CHAIN_READ_LOCKED;
1431 
1432  if (sdDUChainPrivate->m_destroyed)
1433  return nullptr;
1434 
1435  const QList<ParsingEnvironmentFilePointer> list = sdDUChainPrivate->getEnvironmentInformation(document);
1436 
1437  for (const ParsingEnvironmentFilePointer& file : list) {
1438  if (isInMemory(file->indexedTopContext().index()) && file->isProxyContext() == proxyContext) {
1439  return file->topContext();
1440  }
1441  }
1442 
1443  for (const ParsingEnvironmentFilePointer& file : list) {
1444  if (proxyContext == file->isProxyContext()) {
1445  return file->topContext();
1446  }
1447  }
1448 
1449  //Allow selecting a top-context even if there is no ParsingEnvironmentFile
1450  const QList<TopDUContext*> ret = chainsForDocument(document);
1451  for (TopDUContext* ctx : ret) {
1452  if (!ctx->parsingEnvironmentFile() || (ctx->parsingEnvironmentFile()->isProxyContext() == proxyContext))
1453  return ctx;
1454  }
1455 
1456  return nullptr;
1457 }
1458 
1459 QList<TopDUContext*> DUChain::chainsForDocument(const QUrl& document) const
1460 {
1461  return chainsForDocument(IndexedString(document));
1462 }
1463 
1464 QList<TopDUContext*> DUChain::chainsForDocument(const IndexedString& document) const
1465 {
1466  QList<TopDUContext*> chains;
1467 
1468  if (sdDUChainPrivate->m_destroyed)
1469  return chains;
1470 
1471  QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
1472 
1473  // Match all parsed versions of this document
1474  for (auto it = sdDUChainPrivate->m_chainsByUrl.lowerBound(document); it != sdDUChainPrivate->m_chainsByUrl.end();
1475  ++it) {
1476  if (it.key() == document)
1477  chains << it.value();
1478  else
1479  break;
1480  }
1481 
1482  return chains;
1483 }
1484 
1485 TopDUContext* DUChain::chainForDocument(const QUrl& document, const KDevelop::ParsingEnvironment* environment,
1486  bool proxyContext) const
1487 {
1488  return chainForDocument(IndexedString(document), environment, proxyContext);
1489 }
1490 
1491 ParsingEnvironmentFilePointer DUChain::environmentFileForDocument(const IndexedString& document,
1492  const ParsingEnvironment* environment,
1493  bool proxyContext) const
1494 {
1495  ENSURE_CHAIN_READ_LOCKED;
1496 
1497  if (sdDUChainPrivate->m_destroyed)
1498  return ParsingEnvironmentFilePointer();
1499  const QList<ParsingEnvironmentFilePointer> list = sdDUChainPrivate->getEnvironmentInformation(document);
1500 
1501 // qCDebug(LANGUAGE) << document.str() << ": matching" << list.size() << (onlyProxyContexts ? "proxy-contexts" : (noProxyContexts ? "content-contexts" : "contexts"));
1502 
1503  for (auto& envFilePtr : list) {
1504  if (envFilePtr && (envFilePtr->isProxyContext() == proxyContext) && envFilePtr->matchEnvironment(environment) &&
1505  // Verify that the environment-file and its top-context are "good": The top-context must exist,
1506  // and there must be a content-context associated to the proxy-context.
1507  envFilePtr->topContext() &&
1508  (!proxyContext || DUChainUtils::contentContextFromProxyContext(envFilePtr->topContext()))) {
1509  return envFilePtr;
1510  }
1511  }
1512  return ParsingEnvironmentFilePointer();
1513 }
1514 
1515 QList<ParsingEnvironmentFilePointer> DUChain::allEnvironmentFiles(const IndexedString& document)
1516 {
1517  return sdDUChainPrivate->getEnvironmentInformation(document);
1518 }
1519 
1520 ParsingEnvironmentFilePointer DUChain::environmentFileForDocument(IndexedTopDUContext topContext) const
1521 {
1522  if (topContext.index() == 0)
1523  return ParsingEnvironmentFilePointer();
1524 
1525  return ParsingEnvironmentFilePointer(sdDUChainPrivate->loadInformation(topContext.index()));
1526 }
1527 
1528 TopDUContext* DUChain::chainForDocument(const IndexedString& document, const ParsingEnvironment* environment,
1529  bool proxyContext) const
1530 {
1531  if (sdDUChainPrivate->m_destroyed)
1532  return nullptr;
1533  ParsingEnvironmentFilePointer envFile = environmentFileForDocument(document, environment, proxyContext);
1534  if (envFile) {
1535  return envFile->topContext();
1536  } else {
1537  return nullptr;
1538  }
1539 }
1540 
1541 QList<QUrl> DUChain::documents() const
1542 {
1543  QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
1544 
1545  QList<QUrl> ret;
1546  ret.reserve(sdDUChainPrivate->m_chainsByUrl.count());
1547  for (TopDUContext* top : qAsConst(sdDUChainPrivate->m_chainsByUrl)) {
1548  ret << top->url().toUrl();
1549  }
1550 
1551  return ret;
1552 }
1553 
1554 QList<IndexedString> DUChain::indexedDocuments() const
1555 {
1556  QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
1557 
1558  QList<IndexedString> ret;
1559  ret.reserve(sdDUChainPrivate->m_chainsByUrl.count());
1560  for (TopDUContext* top : qAsConst(sdDUChainPrivate->m_chainsByUrl)) {
1561  ret << top->url();
1562  }
1563 
1564  return ret;
1565 }
1566 
1567 void DUChain::documentActivated(KDevelop::IDocument* doc)
1568 {
1569  if (sdDUChainPrivate->m_destroyed)
1570  return;
1571 
1572  DUChainReadLocker lock(DUChain::lock());
1573  QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
1574 
1575  auto backgroundParser = ICore::self()->languageController()->backgroundParser();
1576  auto addWithHighPriority = [backgroundParser, doc]() {
1577  backgroundParser->addDocument(IndexedString(doc->url()),
1578  TopDUContext::VisibleDeclarationsAndContexts,
1579  BackgroundParser::BestPriority);
1580  };
1581 
1582  TopDUContext* ctx = DUChainUtils::standardContextForUrl(doc->url(), true);
1583  //Check whether the document has an attached environment-manager, and whether that one thinks the document needs to be updated.
1584  //If yes, update it.
1585  if (ctx && ctx->parsingEnvironmentFile() && ctx->parsingEnvironmentFile()->needsUpdate()) {
1586  qCDebug(LANGUAGE) << "Document needs update, using best priority since it just got activated:" << doc->url();
1587  addWithHighPriority();
1588  } else if (backgroundParser->managedDocuments().contains(IndexedString(doc->url()))) {
1589  // increase priority if there's already parse job of this document in the queue
1590  qCDebug(LANGUAGE) << "Prioritizing activated document:" << doc->url();
1591  addWithHighPriority();
1592  }
1593 }
1594 
1595 void DUChain::documentClosed(IDocument* document)
1596 {
1597  if (sdDUChainPrivate->m_destroyed)
1598  return;
1599 
1600  IndexedString url(document->url());
1601 
1602  const auto currentDocumentContexts = sdDUChainPrivate->m_openDocumentContexts;
1603  for (const ReferencedTopDUContext& top : currentDocumentContexts) {
1604  if (top->url() == url)
1605  sdDUChainPrivate->m_openDocumentContexts.remove(top);
1606  }
1607 }
1608 
1609 void DUChain::documentLoadedPrepare(KDevelop::IDocument* doc)
1610 {
1611  if (sdDUChainPrivate->m_destroyed)
1612  return;
1613 
1614  const IndexedString url(doc->url());
1615  DUChainWriteLocker lock(DUChain::lock());
1616  QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
1617 
1618  TopDUContext* standardContext = DUChainUtils::standardContextForUrl(doc->url());
1619  QList<TopDUContext*> chains = chainsForDocument(url);
1620 
1621  const auto languages = ICore::self()->languageController()->languagesForUrl(doc->url());
1622 
1623  if (standardContext) {
1624  Q_ASSERT(chains.contains(standardContext)); //We have just loaded it
1625  Q_ASSERT((standardContext->url() == url));
1626 
1627  sdDUChainPrivate->m_openDocumentContexts.insert(standardContext);
1628 
1629  bool needsUpdate = standardContext->parsingEnvironmentFile() &&
1630  standardContext->parsingEnvironmentFile()->needsUpdate();
1631  if (!needsUpdate) {
1632  //Only apply the highlighting if we don't need to update, else we might highlight total crap
1633  //Do instant highlighting only if all imports are loaded, to make sure that we don't block the user-interface too long
1634  //Else the highlighting will be done in the background-thread
1635  //This is not exactly right, as the direct imports don't necessarily equal the real imports used by uses
1636  //but it approximates the correct behavior.
1637  bool allImportsLoaded = true;
1638  const auto importedParentContexts = standardContext->importedParentContexts();
1639  for (const DUContext::Import& import : importedParentContexts) {
1640  if (!import.indexedContext().indexedTopContext().isLoaded())
1641  allImportsLoaded = false;
1642  }
1643 
1644  if (allImportsLoaded) {
1645  l.unlock();
1646  lock.unlock();
1647  for (const auto language : languages) {
1648  if (language->codeHighlighting()) {
1649  language->codeHighlighting()->highlightDUChain(standardContext);
1650  }
1651  }
1652 
1653  qCDebug(LANGUAGE) << "highlighted" << doc->url() << "in foreground";
1654  return;
1655  }
1656  } else {
1657  qCDebug(LANGUAGE) << "not highlighting the duchain because the documents needs an update";
1658  }
1659 
1660  if (needsUpdate || !(standardContext->features() & TopDUContext::AllDeclarationsContextsAndUses)) {
1661  ICore::self()->languageController()->backgroundParser()->addDocument(IndexedString(doc->url()),
1662  TopDUContext::AllDeclarationsContextsAndUses
1663  | TopDUContext::ForceUpdate);
1664  return;
1665  }
1666  }
1667 
1668  //Add for highlighting etc.
1669  ICore::self()->languageController()->backgroundParser()->addDocument(IndexedString(
1670  doc->url()),
1671  TopDUContext::AllDeclarationsContextsAndUses);
1672 }
1673 
1674 void DUChain::documentRenamed(KDevelop::IDocument* doc)
1675 {
1676  if (sdDUChainPrivate->m_destroyed)
1677  return;
1678 
1679  if (!doc->url().isValid()) {
1681  qCWarning(LANGUAGE) << "Strange, url of renamed document is invalid!";
1682  } else {
1683  ICore::self()->languageController()->backgroundParser()->addDocument(IndexedString(doc->url()),
1684  TopDUContext::AllDeclarationsContextsAndUses
1685  | TopDUContext::ForceUpdate);
1686  }
1687 }
1688 
1689 Uses* DUChain::uses()
1690 {
1691  return &sdDUChainPrivate->m_uses;
1692 }
1693 
1694 Definitions* DUChain::definitions()
1695 {
1696  return &sdDUChainPrivate->m_definitions;
1697 }
1698 
1699 static void finalCleanup()
1700 {
1701  DUChainWriteLocker writeLock(DUChain::lock());
1702  qCDebug(LANGUAGE) << "doing final cleanup";
1703 
1704  int cleaned = 0;
1705  while ((cleaned = globalItemRepositoryRegistry().finalCleanup())) {
1706  qCDebug(LANGUAGE) << "cleaned" << cleaned << "B";
1707  if (cleaned < 1000) {
1708  qCDebug(LANGUAGE) << "cleaned enough";
1709  break;
1710  }
1711  }
1712  qCDebug(LANGUAGE) << "final cleanup ready";
1713 }
1714 
1715 void DUChain::shutdown()
1716 {
1717  // if core is not shutting down, we can end up in deadlocks or crashes
1718  // since language plugins might still try to access static duchain stuff
1719  Q_ASSERT(!ICore::self() || ICore::self()->shuttingDown());
1720 
1721  qCDebug(LANGUAGE) << "Cleaning up and shutting down DUChain";
1722 
1723  QMutexLocker lock(&sdDUChainPrivate->cleanupMutex());
1724 
1725  {
1726  //Acquire write-lock of the repository, so when kdevelop crashes in that process, the repository is discarded
1727  //Crashes here may happen in an inconsistent state, thus this makes sense, to protect the user from more crashes
1728  globalItemRepositoryRegistry().lockForWriting();
1729  sdDUChainPrivate->cleanupTopContexts();
1730  globalItemRepositoryRegistry().unlockForWriting();
1731  }
1732 
1733  sdDUChainPrivate->doMoreCleanup(); //Must be done _before_ finalCleanup, else we may be deleting yet needed data
1734 
1735  sdDUChainPrivate->m_openDocumentContexts.clear();
1736  sdDUChainPrivate->m_destroyed = true;
1737  sdDUChainPrivate->clear();
1738 
1739  {
1740  //Acquire write-lock of the repository, so when kdevelop crashes in that process, the repository is discarded
1741  //Crashes here may happen in an inconsistent state, thus this makes sense, to protect the user from more crashes
1742  globalItemRepositoryRegistry().lockForWriting();
1743  finalCleanup();
1744  globalItemRepositoryRegistry().unlockForWriting();
1745  }
1746 
1747  globalItemRepositoryRegistry().shutdown();
1748 }
1749 
1750 uint DUChain::newTopContextIndex()
1751 {
1752  {
1753  QMutexLocker lock(&sdDUChainPrivate->m_chainsMutex);
1754  if (!sdDUChainPrivate->m_availableTopContextIndices.isEmpty()) {
1755  uint ret = sdDUChainPrivate->m_availableTopContextIndices.back();
1756  sdDUChainPrivate->m_availableTopContextIndices.pop_back();
1757  if (TopDUContextDynamicData::fileExists(ret)) {
1758  qCWarning(LANGUAGE) << "Problem in the management of available top-context indices";
1759  return newTopContextIndex();
1760  }
1761  return ret;
1762  }
1763  }
1764  static QAtomicInt& currentId(globalItemRepositoryRegistry().customCounter(QStringLiteral("Top-Context Counter"),
1765  1));
1766  return currentId.fetchAndAddRelaxed(1);
1767 }
1768 
1769 void DUChain::refCountUp(TopDUContext* top)
1770 {
1771  QMutexLocker l(&sdDUChainPrivate->m_referenceCountsMutex);
1772  // note: value is default-constructed to zero if it does not exist
1773  ++sdDUChainPrivate->m_referenceCounts[top];
1774 }
1775 
1776 bool DUChain::deleted()
1777 {
1778  return m_deleted;
1779 }
1780 
1781 void DUChain::refCountDown(TopDUContext* top)
1782 {
1783  QMutexLocker l(&sdDUChainPrivate->m_referenceCountsMutex);
1784  auto it = sdDUChainPrivate->m_referenceCounts.find(top);
1785  if (it == sdDUChainPrivate->m_referenceCounts.end()) {
1786  //qCWarning(LANGUAGE) << "tried to decrease reference-count for" << top->url().str() << "but this top-context is not referenced";
1787  return;
1788  }
1789  auto& refCount = *it;
1790  --refCount;
1791  if (!refCount) {
1792  sdDUChainPrivate->m_referenceCounts.erase(it);
1793  }
1794 }
1795 
1796 void DUChain::emitDeclarationSelected(const DeclarationPointer& decl)
1797 {
1798  if (sdDUChainPrivate->m_destroyed)
1799  return;
1800 
1801  emit declarationSelected(decl);
1802 }
1803 
1804 void DUChain::emitUpdateReady(const IndexedString& url, const ReferencedTopDUContext& topContext)
1805 {
1806  if (sdDUChainPrivate->m_destroyed)
1807  return;
1808 
1809  emit updateReady(url, topContext);
1810 }
1811 
1812 KDevelop::ReferencedTopDUContext DUChain::waitForUpdate(const KDevelop::IndexedString& document,
1813  KDevelop::TopDUContext::Features minFeatures, bool proxyContext)
1814 {
1815  Q_ASSERT(!lock()->currentThreadHasReadLock() && !lock()->currentThreadHasWriteLock());
1816 
1817  WaitForUpdate waiter;
1818  updateContextForUrl(document, minFeatures, &waiter);
1819 
1820  while (!waiter.m_ready) {
1821  // we might have been shut down in the meanwhile
1822  if (!ICore::self()) {
1823  return nullptr;
1824  }
1825 
1826  QMetaObject::invokeMethod(ICore::self()->languageController()->backgroundParser(), "parseDocuments");
1827  QCoreApplication::processEvents();
1828  QThread::usleep(1000);
1829  }
1830 
1831  if (!proxyContext) {
1832  DUChainReadLocker readLock(DUChain::lock());
1833  return DUChainUtils::contentContextFromProxyContext(waiter.m_topContext);
1834  }
1835 
1836  return waiter.m_topContext;
1837 }
1838 
1839 void DUChain::updateContextForUrl(const IndexedString& document, TopDUContext::Features minFeatures,
1840  QObject* notifyReady, int priority) const
1841 {
1842  DUChainReadLocker lock(DUChain::lock());
1843  TopDUContext* standardContext = DUChainUtils::standardContextForUrl(document.toUrl());
1844  if (standardContext && standardContext->parsingEnvironmentFile() &&
1845  !standardContext->parsingEnvironmentFile()->needsUpdate() &&
1846  standardContext->parsingEnvironmentFile()->featuresSatisfied(minFeatures)) {
1847  lock.unlock();
1848  if (notifyReady)
1849  QMetaObject::invokeMethod(notifyReady, "updateReady", Qt::DirectConnection,
1850  Q_ARG(KDevelop::IndexedString, document),
1851  Q_ARG(KDevelop::ReferencedTopDUContext, ReferencedTopDUContext(standardContext)));
1852  } else {
1854  ICore::self()->languageController()->backgroundParser()->addDocument(document, minFeatures, priority,
1855  notifyReady);
1856  }
1857 }
1858 
1859 void DUChain::disablePersistentStorage(bool disable)
1860 {
1861  sdDUChainPrivate->m_cleanupDisabled = disable;
1862 }
1863 
1864 void DUChain::storeToDisk()
1865 {
1866  bool wasDisabled = sdDUChainPrivate->m_cleanupDisabled;
1867  sdDUChainPrivate->m_cleanupDisabled = false;
1868 
1869  sdDUChainPrivate->doMoreCleanup();
1870 
1871  sdDUChainPrivate->m_cleanupDisabled = wasDisabled;
1872 }
1873 
1874 bool DUChain::compareToDisk()
1875 {
1876  DUChainWriteLocker writeLock(DUChain::lock());
1877 
1879  return true;
1880 }
1881 }
duchainlock.h
KDevelop::DUChainLock::lockForWrite
bool lockForWrite(unsigned int timeout=0)
Acquires a write lock.
Definition: duchainlock.cpp:122
KDevelop::IndexedTopDUContext
Allows simple indirect access to top-contexts with on-demand loading.
Definition: indexedtopducontext.h:35
KDevelop::DUChainReadLocker::unlock
void unlock()
Unlock the read lock.
Definition: duchainlock.cpp:248
QMutex
KDevelop::TopDUContext::rebuildDynamicImportStructure
void rebuildDynamicImportStructure()
Definition: topducontext.cpp:519
QSet< uint >
QMultiMap
KDevelop::DUChainWriteLocker
Customized write locker for the definition-use chain.
Definition: duchainlock.h:148
KDevelop::DUChain::urlForIndex
IndexedString urlForIndex(uint index) const
Returns the url for the given top-context index if available. This does have some cost,...
Definition: duchain.cpp:1399
KDevelop::DUChainReadLocker
Customized read locker for the definition-use chain.
Definition: duchainlock.h:114
QList::findIndex
int findIndex(const T &t) const
KDevelop::PersistentSymbolTable::self
static PersistentSymbolTable & self()
Definition: persistentsymboltable.cpp:494
QMap::iterator
QTimer
QSet::remove
bool remove(const T &value)
KDevelop::initInstantiationInformationRepository
void initInstantiationInformationRepository()
Definition: instantiationinformation.cpp:152
KDevelop::DUChain::lock
static DUChainLock * lock()
Retrieve the read write lock for the entire definition-use chain.
Definition: duchain.cpp:1283
QMap::size
int size() const
KDevelop::DUChainUtils::standardContextForUrl
KDEVPLATFORMLANGUAGE_EXPORT KDevelop::TopDUContext * standardContextForUrl(const QUrl &url, bool preferProxyContext=false)
Asks the language-plugins for standard-contexts for the given url, and returns one if available.
Definition: duchainutils.cpp:273
KDevelop::TopDUContextDynamicData::load
static TopDUContext * load(uint topContextIndex)
Loads the top-context from disk, or returns zero on failure.
Definition: topducontextdynamicdata.cpp:594
KDevelop::initIdentifierRepository
void initIdentifierRepository()
Definition: identifier.cpp:1578
KDevelop::RecursiveImportRepository::repository
static Utils::BasicSetRepository * repository()
Definition: topducontext.cpp:48
KDevelop::TopDUContext::ownIndex
uint ownIndex() const
Definition: topducontext.cpp:537
QUrl
duchainutils.h
KDevelop::DUChain::indexedDocuments
QList< IndexedString > indexedDocuments() const
Only used for debugging at the moment Prefer that over the QUrl version for performance reasons.
Definition: duchain.cpp:1554
QThread
KDevelop::finalCleanup
static void finalCleanup()
Definition: duchain.cpp:1699
QFile::open
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
DEFINE_LIST_MEMBER_HASH
#define DEFINE_LIST_MEMBER_HASH(container, member, type)
Definition: appendedlist.h:218
KDevelop::TopDUContext::setInDuChain
void setInDuChain(bool)
This flag is only used by DUChain, never change it from outside.
Definition: topducontext.cpp:1120
KDevelop::DUChain::allChains
QList< TopDUContext * > allChains() const
Return a list of all chains available.
Definition: duchain.cpp:1288
KDevelop::ReferencedTopDUContext
KDevelop can unload unused top-context at any time.
Definition: topducontext.h:59
APPENDED_LIST_FIRST
#define APPENDED_LIST_FIRST(container, type, name)
Definition: appendedlist.h:321
KDevelop::DUChain::updateContextForUrl
void updateContextForUrl(const IndexedString &document, TopDUContext::Features minFeatures, QObject *notifyReady=nullptr, int priority=1) const
Makes sure the standard-context for the given url is up-to-date.
Definition: duchain.cpp:1839
KDevelop::DUChainUtils::contentContextFromProxyContext
KDEVPLATFORMLANGUAGE_EXPORT TopDUContext * contentContextFromProxyContext(TopDUContext *top)
Returns the content-context associated to the given proxy-contex.
Definition: duchainutils.cpp:247
QSet::reserve
void reserve(int size)
QProcessEnvironment::systemEnvironment
QProcessEnvironment systemEnvironment()
KDevelop::initDeclarationRepositories
void initDeclarationRepositories()
Definition: declaration.cpp:73
KDevelop::WaitForUpdate::m_topContext
ReferencedTopDUContext m_topContext
Definition: waitforupdate.h:43
QExplicitlySharedDataPointer::data
T * data() const
KDevelop::Definitions
Global mapping of one Declaration-Ids to multiple Definitions, protected through DUChainLock.
Definition: definitions.h:40
KDevelop::PersistentSymbolTable::clearCache
void clearCache()
Definition: persistentsymboltable.cpp:183
KDevelop::DUChainPointer< Declaration >
KDevelop::WaitForUpdate::m_ready
bool m_ready
Definition: waitforupdate.h:42
QVector::data
T * data()
QList::contains
bool contains(const T &value) const
KDevelop::DUChain::emitUpdateReady
void emitUpdateReady(const KDevelop::IndexedString &url, const KDevelop::ReferencedTopDUContext &topContext)
Call this after you have modified the DUChain data associated with the file url.
Definition: duchain.cpp:1804
QList
QSet::size
int size() const
topducontext.h
KDevelop::DUChain::isInMemory
bool isInMemory(uint topContextIndex) const
Returns whether the top-context with the given index is currently loaded in memory.
Definition: duchain.cpp:1394
KDevelop::DUChainLock
Customized read/write locker for the definition-use chain.
Definition: duchainlock.h:53
waitforupdate.h
KDevelop::TopDUContext::parsingEnvironmentFile
QExplicitlySharedDataPointer< ParsingEnvironmentFile > parsingEnvironmentFile() const
Definition: topducontext.cpp:567
QExplicitlySharedDataPointer
Definition: topducontext.h:28
QList::reserve
void reserve(int alloc)
KDevelop::DUChain::waitForUpdate
KDevelop::ReferencedTopDUContext waitForUpdate(const KDevelop::IndexedString &document, KDevelop::TopDUContext::Features minFeatures, bool proxyContext=false)
Convenience-function similar to updateContextForUrl that blocks this thread until the update of the g...
Definition: duchain.cpp:1812
KDevelop::DUChain::addDocumentChain
void addDocumentChain(TopDUContext *chain)
Registers a new definition-use chain for the given document.
Definition: duchain.cpp:1320
QTime::currentTime
QTime currentTime()
ENSURE_CHAIN_READ_LOCKED
#define ENSURE_CHAIN_READ_LOCKED
Macros for ensuring the DUChain is locked properly.
Definition: duchainlock.h:37
KDevelop::DUChain::chainForDocument
TopDUContext * chainForDocument(const QUrl &document, bool proxyContext=false) const
Return any chain for the given document If available, the version accepting IndexedString should be u...
Definition: duchain.cpp:1389
QHash::iterator
KDevelop::TopDUContext::url
IndexedString url() const override
Definition: topducontext.cpp:1228
KDevelop::initTypeRepository
void initTypeRepository()
Definition: typerepository.cpp:107
KDevelop::DUChain::initialize
static void initialize()
Initializes common static item repositories.
Definition: duchain.cpp:1250
QTimer::start
void start(int msec)
KDevelop::globalIndexedImportIdentifier
const IndexedIdentifier & globalIndexedImportIdentifier()
This is the identifier that can be used to search namespace-import declarations, and should be used t...
Definition: ducontext.cpp:96
KDevelop::TopDUContext::VisibleDeclarationsAndContexts
Definition: topducontext.h:196
KDevelop::DUChain::deleted
static bool deleted()
Returns true if the global duchain instance has already been deleted.
Definition: duchain.cpp:1776
FOREACH_FUNCTION
#define FOREACH_FUNCTION(item, container)
Foreach macro that takes a container and a function-name, and will iterate through the vector returne...
Definition: appendedlist.h:213
KDevelop::DUChainLock::currentThreadHasReadLock
bool currentThreadHasReadLock()
Determines if the current thread has a read lock.
Definition: duchainlock.cpp:115
QObject
KDevelop::StaticParsingEnvironmentData
Internal.
Definition: parsingenvironment.h:56
declaration.h
QMutexLocker::unlock
void unlock()
KDevelop::DUChainItemSystem::copy
void copy(const DUChainBaseData &from, DUChainBaseData &to, bool constant) const
This just calls the correct constructor on the target.
Definition: duchainregister.cpp:84
ENSURE_CHAIN_WRITE_LOCKED
#define ENSURE_CHAIN_WRITE_LOCKED
Definition: duchainlock.h:40
QHash::constBegin
const_iterator constBegin() const
QHash::constEnd
const_iterator constEnd() const
QString
KDevelop::TopDUContext::usingImportsCache
bool usingImportsCache() const
Definition: topducontext.cpp:465
KDevelop::DUChain::compareToDisk
bool compareToDisk()
Compares the whole duchain and all its repositories in the current state to disk When the comparison ...
Definition: duchain.cpp:1874
KDevelop::DUChain::environmentFileForDocument
ParsingEnvironmentFilePointer environmentFileForDocument(const IndexedString &document, const ParsingEnvironment *environment, bool proxyContext=false) const
Find the environment-file of a chain that fits into the given environment.
Definition: duchain.cpp:1491
QTimer::timeout
void timeout()
KDevVarLengthArray
KDevelop::DUChain::definitions
static Definitions * definitions()
Returns the structure that manages mapping between definitions and declarations.
Definition: duchain.cpp:1694
KDevelop::IndexedTopDUContext::index
uint index() const
Definition: indexedtopducontext.h:85
KDevelop::ParsingEnvironment
Use this as base-class to define new parsing-environments.
Definition: parsingenvironment.h:79
KDevelop::TopDUContext::deleteSelf
void deleteSelf()
Called by DUChain::removeDocumentChain to destroy this top-context.
Definition: topducontext.cpp:591
KDevelop::Importers::self
static Importers & self()
Definition: importers.cpp:210
KDevelop::TopDUContext
The top context in a definition-use chain for one source file.
Definition: topducontext.h:113
KDevelop::DUChainLock::currentThreadHasWriteLock
bool currentThreadHasWriteLock() const
Determines if the current thread has a write lock.
Definition: duchainlock.cpp:201
KDevelop::DUChain::uses
static Uses * uses()
Returns the structure that manages mapping between declarations, and which top level contexts contain...
Definition: duchain.cpp:1689
QThread::currentThread
QThread * currentThread()
use.h
KDevelop::DUChain::newTopContextIndex
static uint newTopContextIndex()
Allocates a new identity for a new top-context, no lock needed. The returned value is never zero.
Definition: duchain.cpp:1750
QThread::usleep
void usleep(unsigned long usecs)
KDevelop::DUChain::repositoryPathForSession
static QString repositoryPathForSession(const KDevelop::ISessionLock::Ptr &session)
Definition: duchain.cpp:1241
KDevelop::TopDUContextDynamicData::loadUrl
static IndexedString loadUrl(uint topContextIndex)
Loads only the url out of the data stored on disk for the top-context.
Definition: topducontextdynamicdata.cpp:535
KDevelop::WaitForUpdate
Definition: waitforupdate.h:29
definitions.h
QCoreApplication::applicationName
QString applicationName()
QLatin1String
KDevelop::DUChainWriteLocker::lock
bool lock()
Acquire the write lock (again). Uses the same timeout given to the constructor.
Definition: duchainlock.cpp:269
KDevelop::globalImportIdentifier
const Identifier & globalImportIdentifier()
We leak here, to prevent a possible crash during destruction, as the destructor of Identifier is not ...
Definition: ducontext.cpp:84
QSet::contains
bool contains(const T &value) const
KDevelop::DUChain::emitDeclarationSelected
void emitDeclarationSelected(const KDevelop::DeclarationPointer &decl)
Emits the declarationSelected signal, so other parties can notice it.
Definition: duchain.cpp:1796
QAtomicInt
KDevelop::DUChain::documents
QList< QUrl > documents() const
Only used for debugging at the moment.
Definition: duchain.cpp:1541
KDevelop::TopDUContext::AllDeclarationsContextsAndUses
Definition: topducontext.h:198
QFile::size
virtual qint64 size() const
KDevelop::DUChain::disablePersistentStorage
void disablePersistentStorage(bool disable=true)
If you call this, the persistent disk-storage structure will stay unaffected, and no duchain cleanup ...
Definition: duchain.cpp:1859
END_APPENDED_LISTS
#define END_APPENDED_LISTS(container, predecessor)
Definition: appendedlist.h:358
KDevelop::ParsingEnvironmentFile
This represents all information about a specific parsed file that is needed to match the file to a pa...
Definition: parsingenvironment.h:118
KDevelop::TopDUContext::imports
bool imports(const DUContext *origin, const CursorInRevision &position) const override
Determine if this chain imports another chain recursively.
Definition: topducontext.cpp:1030
persistentsymboltable.h
KDevelop::TopDUContextDynamicData::fileExists
static bool fileExists(uint topContextIndex)
Definition: topducontextdynamicdata.cpp:508
KDevelop::DUChain::storeToDisk
void storeToDisk()
Stores the whole duchain and all its repositories in the current state to disk The duchain must not b...
Definition: duchain.cpp:1864
QIODevice::read
qint64 read(char *data, qint64 maxSize)
KDevelop::DUChainItemSystem::dynamicSize
uint dynamicSize(const DUChainBaseData &data) const
Calls the dynamicSize(..) member on the given data, in the most special class. Since we cannot use vi...
Definition: duchainregister.cpp:70
KDevelop::duChainPrivateSelf
static DUChainPrivate * duChainPrivateSelf
Definition: duchain.cpp:301
KDevelop::DUChain::updateContextEnvironment
void updateContextEnvironment(TopDUContext *context, ParsingEnvironmentFile *file)
Changes the environment attached to the given top-level context, and updates the management-structure...
Definition: duchain.cpp:1294
KDevelop::IndexedTopDUContext::data
TopDUContext * data() const
Returns the top-context represented by this indexed top-context.
Definition: indexedtopducontext.cpp:49
QMutexLocker::relock
void relock()
KDevelop::cnt
int cnt
Definition: identifier.cpp:1422
KDevelop::Uses
Global mapping of Declaration-Ids to top-contexts, protected through DUChainLock.
Definition: uses.h:37
abstractfunctiondeclaration.h
importers.h
topducontextdynamicdata.h
duchain.h
QMetaObject::invokeMethod
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
KDevelop::DUChain::removeDocumentChain
void removeDocumentChain(KDevelop::TopDUContext *document)
Removes the given top-context from the duchain, and deletes it.
Definition: duchain.cpp:1305
QCoreApplication::processEvents
void processEvents(QFlags< QEventLoop::ProcessEventsFlag > flags)
uses.h
KDevelop
Definition: abstractfunctiondeclaration.cpp:27
QSet::insert
const_iterator insert(const T &value)
KDevelop::DUChainWriteLocker::unlock
void unlock()
Unlock the write lock.
Definition: duchainlock.cpp:292
QVector::size
int size() const
KDevelop::globalAliasIdentifier
const Identifier & globalAliasIdentifier()
This is the identifier that can be used to search namespace-alias declarations.
Definition: ducontext.cpp:90
KDevelop::TopDUContext::ForceUpdate
Definition: topducontext.h:204
KDevelop::DUChain::allEnvironmentFiles
QList< ParsingEnvironmentFilePointer > allEnvironmentFiles(const IndexedString &document)
Returns the list of the environment-infos of all versions of the given document.
Definition: duchain.cpp:1515
KDevelop::RecursiveImportCacheRepository::repository
static Utils::BasicSetRepository * repository()
Definition: persistentsymboltable.cpp:58
QMutexLocker
parsingenvironment.h
QVector< uint >
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
KDevelop::ParsingEnvironmentFilePointer
QExplicitlySharedDataPointer< ParsingEnvironmentFile > ParsingEnvironmentFilePointer
Definition: duchain.h:40
QTime::msecsTo
int msecsTo(const QTime &t) const
duchainregister.h
KDevelop::globalIndexedAliasIdentifier
const IndexedIdentifier & globalIndexedAliasIdentifier()
This is the identifier that can be used to search namespace-alias declarations.
Definition: ducontext.cpp:102
QReadWriteLock
QAtomicInt::fetchAndAddRelaxed
int fetchAndAddRelaxed(int valueToAdd)
KDevelop::DUChain::shutdown
void shutdown()
Shutdown and cleanup the DUChain.
Definition: duchain.cpp:1715
QTime
QHash
QFile
KDevelop::TopDUContext::isOnDisk
bool isOnDisk() const
Whether this top-context has a stored version on disk.
Definition: topducontext.cpp:1125
KDevelop::TopDUContext::indexed
IndexedTopDUContext indexed() const
Returns an indexed representation of this top-context. Indexed representations stay valid even if the...
Definition: topducontext.cpp:532
KDevelop::DUChainBaseData
Definition: duchainbase.h:59
KDevelop::TopDUContext::m_dynamicData
class TopDUContextDynamicData * m_dynamicData
Definition: topducontext.h:381
KDevelop::DUChain::chainsForDocument
QList< TopDUContext * > chainsForDocument(const QUrl &document) const
Return all chains for the given document that are currently in memory.
Definition: duchain.cpp:1459
KDevelop::DUChain
Holds references to all top level source file contexts.
Definition: duchain.h:54
KDevelop::TopDUContext::setParsingEnvironmentFile
void setParsingEnvironmentFile(ParsingEnvironmentFile *)
Definition: topducontext.cpp:636
START_APPENDED_LISTS
#define START_APPENDED_LISTS(container)
use this if the class does not have a base class that also uses appended lists
Definition: appendedlist.h:250
KDevelop::TopDUContextDynamicData::store
void store()
Stores this top-context to disk.
Definition: topducontextdynamicdata.cpp:669
QList::value
T value(int i) const
topducontextdata.h
KDevelop::initModificationRevisionSetRepository
void initModificationRevisionSetRepository()
QProcessEnvironment::value
QString value(const QString &name, const QString &defaultValue) const
QIODevice::write
qint64 write(const char *data, qint64 maxSize)
KDevelop::DUChainItemSystem::self
static DUChainItemSystem & self()
Access the static DUChainItemSystem instance.
Definition: duchainregister.cpp:93
KDevelop::DUChain::self
static DUChain * self()
Returns the global static instance.
Definition: duchain.cpp:1230
KDevelop::DUContext::Import
Represents an imported parent context.
Definition: ducontext.h:256
KDevelop::TopDUContextDynamicData::deleteOnDisk
void deleteOnDisk()
Stores all remnants of this top-context that are on disk. The top-context will be fully dynamic after...
Definition: topducontextdynamicdata.cpp:634
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Wed Mar 3 2021 00:37:28 by doxygen 1.8.16 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kdevplatform/language/duchain

Skip menu "kdevplatform/language/duchain"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdevelop API Reference

Skip menu "kdevelop API Reference"
  • kdevplatform
  •   debugger
  •   documentation
  •   interfaces
  •   language
  •     assistant
  •     backgroundparser
  •     checks
  •     classmodel
  •     codecompletion
  •     codegen
  •     duchain
  •     editor
  •     highlighting
  •     interfaces
  •     util
  •   outputview
  •   project
  •   serialization
  •   shell
  •   sublime
  •   tests
  •   util
  •   vcs

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal