1 /*
2     SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
3     SPDX-FileCopyrightText: 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
4 
5     SPDX-License-Identifier: LGPL-2.0-only
6 */
7 
8 #include "duchainbase.h"
9 
10 #include <QMutexLocker>
11 #include <QThreadStorage>
12 
13 #include "duchainpointer.h"
14 #include "parsingenvironment.h"
15 #include <serialization/indexedstring.h>
16 #include "topducontext.h"
17 #include "duchainregister.h"
18 #include <util/foregroundlock.h>
19 #include <interfaces/icore.h>
20 #include <interfaces/ilanguagecontroller.h>
21 #include <backgroundparser/backgroundparser.h>
22 #include <backgroundparser/documentchangetracker.h>
23 
24 namespace KDevelop {
25 REGISTER_DUCHAIN_ITEM(DUChainBase);
26 
classSize() const27 uint DUChainBaseData::classSize() const
28 {
29     return DUChainItemSystem::self().dataClassSize(*this);
30 }
31 
DUChainBase(const RangeInRevision & range)32 DUChainBase::DUChainBase(const RangeInRevision& range)
33     : d_ptr(new DUChainBaseData)
34 {
35     d_func_dynamic()->m_range = range;
36     d_func_dynamic()->setClassId(this);
37 }
38 
DUChainBase(DUChainBaseData & dd,const RangeInRevision & range)39 DUChainBase::DUChainBase(DUChainBaseData& dd, const RangeInRevision& range)
40     : d_ptr(&dd)
41 {
42     d_func_dynamic()->m_range = range;
43 }
44 
DUChainBase(DUChainBaseData & dd)45 DUChainBase::DUChainBase(DUChainBaseData& dd)
46     : d_ptr(&dd)
47 {
48 }
49 
DUChainBase(DUChainBase & rhs)50 DUChainBase::DUChainBase(DUChainBase& rhs)
51     : d_ptr(new DUChainBaseData(*rhs.d_func()))
52 {
53     d_func_dynamic()->setClassId(this);
54 }
55 
url() const56 IndexedString DUChainBase::url() const
57 {
58     TopDUContext* top = topContext();
59     if (top)
60         return top->TopDUContext::url();
61     else
62         return IndexedString();
63 }
64 
setData(DUChainBaseData * data,bool constructorCalled)65 void DUChainBase::setData(DUChainBaseData* data, bool constructorCalled)
66 {
67     Q_ASSERT(data);
68     Q_ASSERT(d_ptr);
69 
70     if (d_ptr->m_dynamic) {
71         Q_ASSERT(constructorCalled);
72         DUChainItemSystem::self().deleteDynamicData(d_ptr);
73     } else if (constructorCalled) {
74         // If the data object isn't dynamic, then it is part of a central repository, and cannot be deleted here.
75         // we still need to call the destructor though
76         KDevelop::DUChainItemSystem::self().callDestructor(static_cast<DUChainBaseData*>(d_ptr));
77     }
78 
79     d_ptr = data;
80 }
81 
~DUChainBase()82 DUChainBase::~DUChainBase()
83 {
84     if (m_ptr)
85         m_ptr->m_base = nullptr;
86 
87     if (d_ptr->m_dynamic) {
88         DUChainItemSystem::self().deleteDynamicData(d_ptr);
89         d_ptr = nullptr;
90     }
91 }
92 
topContext() const93 TopDUContext* DUChainBase::topContext() const
94 {
95     ///@todo Move the reference to the top-context right into this class, as it's common to all inheriters
96     return nullptr;
97 }
98 
99 namespace {
100 QMutex weakPointerMutex;
101 }
102 
weakPointer() const103 const QExplicitlySharedDataPointer<DUChainPointerData>& DUChainBase::weakPointer() const
104 {
105     if (!m_ptr) {
106         QMutexLocker lock(&weakPointerMutex); // The mutex is used to make sure we don't create m_ptr twice at the same time
107         m_ptr = new DUChainPointerData(const_cast<DUChainBase*>(this));
108         m_ptr->m_base = const_cast<DUChainBase*>(this);
109     }
110 
111     return m_ptr;
112 }
113 
rebuildDynamicData(DUContext * parent,uint ownIndex)114 void DUChainBase::rebuildDynamicData(DUContext* parent, uint ownIndex)
115 {
116     Q_UNUSED(parent)
117     Q_UNUSED(ownIndex)
118 }
119 
makeDynamic()120 void DUChainBase::makeDynamic()
121 {
122     Q_ASSERT(d_ptr);
123     if (!d_func()->m_dynamic) {
124         Q_ASSERT(d_func()->classId);
125         DUChainBaseData* newData = DUChainItemSystem::self().cloneData(*d_func());
126         {
127             auto* const baseData = static_cast<DUChainBaseData*>(d_ptr);
128             const DUChainReferenceCountingEnabler rcEnabler(d_ptr, DUChainItemSystem::self().dynamicSize(*baseData));
129             //We don't delete the previous data, because it's embedded in the top-context when it isn't dynamic.
130             //However we do call the destructor, to keep semantic stuff like reference-counting within the data class working correctly.
131             DUChainItemSystem::self().callDestructor(baseData);
132         }
133         d_ptr = newData;
134         Q_ASSERT(d_ptr);
135         Q_ASSERT(d_func()->m_dynamic);
136         Q_ASSERT(d_func()->classId);
137     }
138 }
139 
range() const140 RangeInRevision DUChainBase::range() const
141 {
142     return d_func()->m_range;
143 }
144 
rangeInCurrentRevision() const145 KTextEditor::Range DUChainBase::rangeInCurrentRevision() const
146 {
147     DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url());
148 
149     if (tracker && topContext() && topContext()->parsingEnvironmentFile()) {
150         qint64 revision = topContext()->parsingEnvironmentFile()->modificationRevision().revision;
151         return tracker->transformToCurrentRevision(d_func()->m_range, revision);
152     }
153 
154     // If the document is not open, we can simply cast the range over, as no translation can be done
155     return d_func()->m_range.castToSimpleRange();
156 }
157 
createRangeMoving() const158 PersistentMovingRange::Ptr DUChainBase::createRangeMoving() const
159 {
160     VERIFY_FOREGROUND_LOCKED
161     return PersistentMovingRange::Ptr(new PersistentMovingRange(rangeInCurrentRevision(), url()));
162 }
163 
transformToLocalRevision(const KTextEditor::Cursor & cursor) const164 CursorInRevision DUChainBase::transformToLocalRevision(const KTextEditor::Cursor& cursor) const
165 {
166     DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url());
167 
168     if (tracker && topContext() && topContext()->parsingEnvironmentFile()) {
169         qint64 revision = topContext()->parsingEnvironmentFile()->modificationRevision().revision;
170         return tracker->transformToRevision(cursor, revision);
171     }
172 
173     return CursorInRevision::castFromSimpleCursor(cursor);
174 }
175 
transformToLocalRevision(const KTextEditor::Range & range) const176 RangeInRevision DUChainBase::transformToLocalRevision(const KTextEditor::Range& range) const
177 {
178     DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url());
179 
180     if (tracker && topContext() && topContext()->parsingEnvironmentFile()) {
181         qint64 revision = topContext()->parsingEnvironmentFile()->modificationRevision().revision;
182         return tracker->transformToRevision(range, revision);
183     }
184 
185     return RangeInRevision::castFromSimpleRange(range);
186 }
187 
transformFromLocalRevision(const KDevelop::RangeInRevision & range) const188 KTextEditor::Range DUChainBase::transformFromLocalRevision(const KDevelop::RangeInRevision& range) const
189 {
190     DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url());
191 
192     if (tracker && topContext() && topContext()->parsingEnvironmentFile()) {
193         qint64 revision = topContext()->parsingEnvironmentFile()->modificationRevision().revision;
194         return tracker->transformToCurrentRevision(range, revision);
195     }
196 
197     return range.castToSimpleRange();
198 }
199 
transformFromLocalRevision(const KDevelop::CursorInRevision & cursor) const200 KTextEditor::Cursor DUChainBase::transformFromLocalRevision(const KDevelop::CursorInRevision& cursor) const
201 {
202     DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url());
203 
204     if (tracker && topContext() && topContext()->parsingEnvironmentFile()) {
205         qint64 revision = topContext()->parsingEnvironmentFile()->modificationRevision().revision;
206         return tracker->transformToCurrentRevision(cursor, revision);
207     }
208 
209     return cursor.castToSimpleCursor();
210 }
211 
setRange(const RangeInRevision & range)212 void DUChainBase::setRange(const RangeInRevision& range)
213 {
214     d_func_dynamic()->m_range = range;
215 }
216 
217 QThreadStorage<bool> shouldCreateConstantDataStorage;
218 
shouldCreateConstantData()219 bool& DUChainBaseData::shouldCreateConstantData()
220 {
221     return shouldCreateConstantDataStorage.localData();
222 }
223 }
224