File indexing completed on 2024-05-12 04:37:58

0001 /*
0002     SPDX-FileCopyrightText: 2006-2008 Hamish Rodda <rodda@kde.org>
0003     SPDX-FileCopyrightText: 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #ifndef KDEVPLATFORM_DUCHAIN_H
0009 #define KDEVPLATFORM_DUCHAIN_H
0010 
0011 #include <QObject>
0012 
0013 #include "topducontext.h"
0014 #include "parsingenvironment.h"
0015 
0016 #include <interfaces/isessionlock.h>
0017 
0018 class QUrl;
0019 
0020 namespace KDevelop {
0021 class IDocument;
0022 class TopDUContext;
0023 class DUChainLock;
0024 
0025 class ParsingEnvironmentManager;
0026 class ParsingEnvironment;
0027 class ParsingEnvironmentFile;
0028 using ParsingEnvironmentFilePointer = QExplicitlySharedDataPointer<ParsingEnvironmentFile>;
0029 class Definitions;
0030 class Uses;
0031 
0032 /**
0033  * \short Holds references to all top level source file contexts.
0034  *
0035  * The DUChain is a global static class which manages the definition-use
0036  * chains.  It performs the following functions:
0037  * \li registers chains with addDocumentChain() and deregisters with removeDocumentChain()
0038  * \li allows querying for existing chains
0039  * \li watches text editors, registering and deregistering them with the BackgroundParser when files
0040  *     are opened and closed.
0041  */
0042 class KDEVPLATFORMLANGUAGE_EXPORT DUChain
0043     : public QObject
0044 {
0045     Q_OBJECT
0046 
0047 public:
0048     /**
0049      * Initializes common static item repositories.
0050      * Must be called once for multi threaded applications to work reliably.
0051      */
0052     static void initialize();
0053 
0054     /**
0055      * Return a list of all chains available
0056      */
0057     QList<TopDUContext*> allChains() const;
0058 
0059     /**
0060      * Makes sure the standard-context for the given url is up-to-date.
0061      * This may trigger a parsing in background, so a QObject can be given that will be notified
0062      * asynchronously once the update is ready.
0063      * If the context is already up to date, the given QObject is notified directly.
0064      *
0065      * @param document Document to update
0066      * @param minFeatures The requested features. If you want to force a full update of the context, give TopDUContext::ForceUpdate.
0067      *                 If you want to force an update including all imports, use TopDUContext::ForceUpdateRecursive.
0068      * @param notifyReady An optional pointer to a QObject that should contain a slot
0069      *                    "void updateReady(KDevelop::IndexedString url, KDevelop::ReferencedTopDUContext topContext)".
0070      *                    The notification is guaranteed to be called once for each call to updateContextForUrl. The given top-context
0071      *                    may be invalid if the update failed. A queued connection is used if a re-parse has to be done. The duchain
0072      *                    will _not_ be locked when updateReady is called.
0073      * @param priority An optional priority for the job. The lower the value, the higher it's priority.
0074      * @note The duchain must _not_ be locked when this is called!
0075      */
0076     void updateContextForUrl(const IndexedString& document, TopDUContext::Features minFeatures,
0077                              QObject* notifyReady = nullptr, int priority = 1) const;
0078 
0079     /**
0080      * Convenience-function similar to updateContextForUrl that blocks this thread until the update of the given document is ready,
0081      * and returns the top-context.
0082      * @param document The document to update
0083      * @param minFeatures The requested features. If you want to force a full update of the context, give TopDUContext::ForceUpdate.
0084      *                 If you want to force an update including all imports, use TopDUContext::ForceUpdateRecursive.
0085      * @return The up-to-date top-context, or zero if the update failed
0086      *
0087      * @note The duchain must _not_ be locked when this is called!
0088      *
0089      */
0090     KDevelop::ReferencedTopDUContext waitForUpdate(const KDevelop::IndexedString& document,
0091                                                    KDevelop::TopDUContext::Features minFeatures,
0092                                                    bool proxyContext = false);
0093 
0094     /**
0095      * Return any chain for the given document
0096      * If available, the version accepting IndexedString should be used instead of this, for performance reasons.
0097      * When no fitting chain is in memory, one may be loaded from disk.
0098      *
0099      * @note The duchain must be at least read-locked locked when this is called!
0100      * */
0101     TopDUContext* chainForDocument(const QUrl& document, bool proxyContext = false) const;
0102     TopDUContext* chainForDocument(const IndexedString& document, bool proxyContext = false) const;
0103 
0104     /**
0105      * Return all chains for the given document that are currently in memory.
0106      * This does not load any chains from disk.
0107      * */
0108     QList<TopDUContext*> chainsForDocument(const QUrl& document) const;
0109 
0110     /**
0111      * Return all chains for the given document that are currently in memory.
0112      * This does not load any chains from disk.
0113      * Should be preferred over the QUrl version.
0114      * */
0115     QList<TopDUContext*> chainsForDocument(const IndexedString& document) const;
0116 
0117     /**
0118      * Find a chain that fits into the given environment. If no fitting chain is found, 0 is returned.
0119      * When no fitting chain is in memory, one may be loaded from disk.
0120      * @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned.
0121      *
0122      * @note The duchain must be at least read-locked locked when this is called!
0123      * */
0124     TopDUContext* chainForDocument(const QUrl& document, const ParsingEnvironment* environment,
0125                                    bool proxyContext = false) const;
0126 
0127     /**
0128      * Find a chain that fits into the given environment. If no fitting chain is found, 0 is returned.
0129      * When no fitting chain is in memory, one may be loaded from disk.
0130      * @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned.
0131      *
0132      * Prefer this over the QUrl version.
0133      *
0134      * @note The duchain must be at least read-locked locked when this is called!
0135      * */
0136     TopDUContext* chainForDocument(const IndexedString& document, const ParsingEnvironment* environment,
0137                                    bool proxyContext = false) const;
0138 
0139     /**
0140      * Find the environment-file of a chain that fits into the given environment. If no fitting chain is found, 0 is returned.
0141      * When no fitting chain is in memory, one may be loaded from disk.
0142      *
0143      * This should be preferred over chainForDocument when only the environment-info is needed, because the TopDUContext is not loaded in this function.
0144      *
0145      ** @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned.
0146      *
0147      * Prefer this over the QUrl version.
0148      *
0149      * @note The duchain must be at least read-locked locked when this is called!
0150      * */
0151     ParsingEnvironmentFilePointer environmentFileForDocument(const IndexedString& document,
0152                                                              const ParsingEnvironment* environment,
0153                                                              bool proxyContext = false) const;
0154 
0155     ParsingEnvironmentFilePointer environmentFileForDocument(IndexedTopDUContext topContext) const;
0156 
0157     /**
0158      * Returns the list of the environment-infos of all versions of the given document.
0159      */
0160     QList<ParsingEnvironmentFilePointer> allEnvironmentFiles(const IndexedString& document);
0161 
0162     ///Returns the top-context that has the given index assigned, or zero if it doesn't exist. @see TopDUContext::ownIndex
0163     ///The duchain must be read-locked when this is called
0164     ///This function is inlined because it is called in a very high frequency
0165     inline TopDUContext* chainForIndex(uint index)
0166     {
0167         if (m_deleted)
0168             return nullptr;
0169 
0170         {
0171             QMutexLocker lock(&chainsByIndexLock);
0172 
0173             if (chainsByIndex.size() > index) {
0174                 TopDUContext* top = chainsByIndex[index];
0175                 if (top)
0176                     return top;
0177             }
0178         }
0179 
0180         //Load the top-context
0181         return loadChain(index);
0182     }
0183 
0184     ///Returns the url for the given top-context index if available. This does have some cost, so avoid it when possible.
0185     IndexedString urlForIndex(uint index) const;
0186 
0187     /// Only used for debugging at the moment
0188     QList<QUrl> documents() const;
0189 
0190     /// Only used for debugging at the moment
0191     /// Prefer that over the QUrl version for performance reasons
0192     QList<IndexedString> indexedDocuments() const;
0193 
0194     /**
0195      * Registers a new definition-use \a chain for the given \a document.
0196      */
0197     void addDocumentChain(TopDUContext* chain);
0198 
0199     /// Returns true if the global duchain instance has already been deleted
0200     static bool deleted();
0201 
0202     /// Returns the global static instance.
0203     static DUChain* self();
0204 
0205     /// Returns the structure that manages mapping between definitions and declarations
0206     static Definitions* definitions();
0207 
0208     /// Returns the structure that manages mapping between declarations, and which top level contexts contain uses of them.
0209     static Uses* uses();
0210 
0211     static QString repositoryPathForSession(const KDevelop::ISessionLock::Ptr& session);
0212 
0213     /**
0214      * Retrieve the read write lock for the entire definition-use chain.
0215      * To call non-const methods, you must be holding a write lock.
0216      *
0217      * Evaluations made prior to holding a lock (including which objects
0218      * exist) must be verified once the lock is held, as they may have changed
0219      * or been deleted.
0220      *
0221      * \threadsafe
0222      */
0223     static DUChainLock* lock();
0224 
0225     /// Returns whether the top-context with the given index is currently loaded in memory
0226     bool isInMemory(uint topContextIndex) const;
0227 
0228     /**
0229      * Changes the environment attached to the given top-level context, and updates the management-structures to reflect that
0230      * */
0231     void updateContextEnvironment(TopDUContext* context, ParsingEnvironmentFile* file);
0232 
0233     ///Allocates a new identity for a new top-context, no lock needed. The returned value is never zero
0234     static uint newTopContextIndex();
0235 
0236     ///If you call this, the persistent disk-storage structure will stay unaffected, and no duchain cleanup will be done.
0237     ///Call this from within tests.
0238     void disablePersistentStorage(bool disable = true);
0239 
0240     ///Stores the whole duchain and all its repositories in the current state to disk
0241     ///The duchain must not be locked in any way
0242     void storeToDisk();
0243 
0244     ///Compares the whole duchain and all its repositories in the current state to disk
0245     ///When the comparison fails, debug-output will show why
0246     ///The duchain must not be locked when calling this
0247     ///@return true If the current memory state equals the disk state, else false
0248     bool compareToDisk();
0249 
0250 Q_SIGNALS:
0251     ///Is emitted when the declaration has been selected somewhere in the user-interface, for example in the completion-list
0252     void declarationSelected(const KDevelop::DeclarationPointer& decl);
0253 
0254     /**
0255      * This signal is emitted whenever the DUChain data associated with @p url was updated.
0256      *
0257      * You can connect to this signal to get notified when the DUChain for a given file was updated.
0258      */
0259     void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext);
0260 
0261 public Q_SLOTS:
0262     ///Removes the given top-context from the duchain, and deletes it.
0263     void removeDocumentChain(KDevelop::TopDUContext* document);
0264     ///Emits the declarationSelected signal, so other parties can notice it.
0265     void emitDeclarationSelected(const KDevelop::DeclarationPointer& decl);
0266 
0267     /**
0268      * Call this after you have modified the DUChain data associated with the file @p url.
0269      *
0270      * This triggers an emit of the @c updateReady signal.
0271      */
0272     void emitUpdateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext);
0273 
0274     /**
0275      * Shutdown and cleanup the DUChain.
0276      */
0277     void shutdown();
0278 
0279 private Q_SLOTS:
0280     void documentActivated(KDevelop::IDocument* doc);
0281     void documentLoadedPrepare(KDevelop::IDocument* document);
0282     void documentRenamed(KDevelop::IDocument* document);
0283     void documentClosed(KDevelop::IDocument*);
0284 
0285 private:
0286     TopDUContext* loadChain(uint index);
0287     //These two are exported here so that the extremely frequently called chainForIndex(..) can be inlined
0288     static bool m_deleted;
0289     static std::vector<TopDUContext*> chainsByIndex;
0290     static QMutex chainsByIndexLock;
0291 
0292     /// Increases the reference-count for the given top-context. The result: It will not be unloaded.
0293     /// Do this to prevent KDevelop from unloading a top-context that you plan to use. Don't forget calling unReferenceToContext again,
0294     /// else the top-context will stay in memory for ever.
0295     void refCountUp(TopDUContext* top);
0296 
0297     /// Decreases the reference-count for the given top-context. When it reaches zero, KDevelop is free to unload it at any time,
0298     /// also invalidating all the contained declarations and contexts.
0299     void refCountDown(TopDUContext* top);
0300 
0301     void addToEnvironmentManager(TopDUContext* chain);
0302     void removeFromEnvironmentManager(TopDUContext* chain);
0303     DUChain();
0304     ~DUChain() override;
0305 
0306     friend class DUChainPrivate;
0307     friend class ReferencedTopDUContext;
0308 };
0309 }
0310 
0311 #endif // KDEVPLATFORM_DUCHAIN_H