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

0001 /*
0002     SPDX-FileCopyrightText: 2014 Milian Wolff <mail@milianw.de>
0003     SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "topducontextdynamicdata.h"
0009 
0010 #include <typeinfo>
0011 #include <QFile>
0012 #include <QByteArray>
0013 
0014 #include "declaration.h"
0015 #include "declarationdata.h"
0016 #include "ducontext.h"
0017 #include "topducontext.h"
0018 #include "topducontextdata.h"
0019 #include "ducontextdata.h"
0020 #include "ducontextdynamicdata.h"
0021 #include "duchainregister.h"
0022 #include "serialization/itemrepository.h"
0023 #include "problem.h"
0024 #include <debug.h>
0025 
0026 //#define DEBUG_DATA_INFO
0027 
0028 //This might be problematic on some systems, because really many mmaps are created
0029 #define USE_MMAP
0030 using namespace KDevelop;
0031 
0032 namespace {
0033 /**
0034  * Serialize @p item into @p data and update @p totalDataOffset.
0035  *
0036  * If @p isSharedDataItem is true, then the item's internal data pointer is not updated
0037  * to point to the serialized data. Otherwise the dynamic data is deleted and the items
0038  * data will point to the constant serialized data.
0039  *
0040  * NOTE: The above is required to support serialization of shared-data such as from ProblemPointer.
0041  * If we'd set the data to point to the constant region, we'd get crashes due to use-after-free when
0042  * we unmap the data and a shared pointer outlives that.
0043  */
0044 void saveDUChainItem(QVector<TopDUContextDynamicData::ArrayWithPosition>& data, DUChainBase& item,
0045                      uint& totalDataOffset, bool isSharedDataItem)
0046 {
0047     if (!item.d_func()->classId) {
0048         //If this triggers, you have probably created an own DUChainBase based class, but haven't called setClassId(this) in the constructor.
0049         qCritical() << "no class-id set for data attached to a declaration of type" << typeid(item).name();
0050         Q_ASSERT(0);
0051     }
0052 
0053     int size = DUChainItemSystem::self().dynamicSize(*item.d_func());
0054 
0055     if (data.back().array.size() - int( data.back().position ) < size)
0056         //Create a new data item
0057         data.append({QByteArray(size > 10000 ? size : 10000, 0), 0u});
0058 
0059     uint pos = data.back().position;
0060     data.back().position += size;
0061     totalDataOffset += size;
0062 
0063     DUChainBaseData& target(*(reinterpret_cast<DUChainBaseData*>(data.back().array.data() + pos)));
0064 
0065     if (item.d_func()->isDynamic()) {
0066         //Change from dynamic data to constant data
0067         const DUChainReferenceCountingEnabler rcEnabler(data.back().array.data(), data.back().array.size());
0068         DUChainItemSystem::self().copy(*item.d_func(), target, true);
0069         Q_ASSERT(!target.isDynamic());
0070         if (!isSharedDataItem) {
0071             item.setData(&target);
0072         }
0073     } else {
0074         //Just copy the data into another place, expensive copy constructors are not needed
0075 #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 800)
0076 #pragma GCC diagnostic push
0077 #pragma GCC diagnostic ignored "-Wclass-memaccess"
0078 #endif
0079         memcpy(&target, item.d_func(), size);
0080 #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 800)
0081 #pragma GCC diagnostic pop
0082 #endif
0083         if (!isSharedDataItem) {
0084             item.setData(&target, false);
0085         }
0086     }
0087 
0088     if (!isSharedDataItem) {
0089         Q_ASSERT(item.d_func() == &target);
0090 
0091         Q_ASSERT(!item.d_func()->isDynamic());
0092     }
0093 }
0094 
0095 uint indexForParentContext(DUContext* context)
0096 {
0097     return LocalIndexedDUContext(context->parentContext()).localIndex();
0098 }
0099 
0100 uint indexForParentContext(Declaration* declaration)
0101 {
0102     return LocalIndexedDUContext(declaration->context()).localIndex();
0103 }
0104 
0105 uint indexForParentContext(const ProblemPointer& /*problem*/)
0106 {
0107     // always stored in the top context
0108     return 0;
0109 }
0110 
0111 #ifndef QT_NO_DEBUG
0112 void validateItem(const DUChainBaseData* const data, const uchar* const mappedData, const size_t mappedDataSize)
0113 {
0114     Q_ASSERT(!data->isDynamic());
0115     if (mappedData) {
0116         Q_ASSERT((( size_t )data) < (( size_t )mappedData)
0117                  || (( size_t )data) > (( size_t )mappedData) + mappedDataSize);
0118     }
0119 }
0120 #endif
0121 
0122 const char* pointerInData(const QVector<TopDUContextDynamicData::ArrayWithPosition>& data, uint totalOffset)
0123 {
0124     for (auto& awp : data) {
0125         if (totalOffset < awp.position) {
0126             return awp.array.constData() + totalOffset;
0127         }
0128         totalOffset -= awp.position;
0129     }
0130 
0131     Q_ASSERT_X(false, Q_FUNC_INFO, "Offset doesn't exist in the data.");
0132     return nullptr;
0133 }
0134 
0135 void verifyDataInfo(const TopDUContextDynamicData::ItemDataInfo& info,
0136                     const QVector<TopDUContextDynamicData::ArrayWithPosition>& data)
0137 {
0138     Q_UNUSED(info);
0139     Q_UNUSED(data);
0140 #ifdef DEBUG_DATA_INFO
0141     DUChainBaseData* item = ( DUChainBaseData* )(pointerInData(data, info.dataOffset));
0142     int size = DUChainItemSystem::self().dynamicSize(*item);
0143     Q_ASSERT(size);
0144 #endif
0145 }
0146 
0147 QString basePath()
0148 {
0149     return globalItemRepositoryRegistry().path() + QLatin1String("/topcontexts/");
0150 }
0151 
0152 QString pathForTopContext(const uint topContextIndex)
0153 {
0154     return basePath() + QString::number(topContextIndex);
0155 }
0156 
0157 enum LoadType {
0158     PartialLoad, ///< Only load the direct member data
0159     FullLoad   ///< Load everything, including appended lists
0160 };
0161 template <typename F>
0162 void loadTopDUContextData(const uint topContextIndex, LoadType loadType, F callback)
0163 {
0164     QFile file(pathForTopContext(topContextIndex));
0165     if (!file.open(QIODevice::ReadOnly)) {
0166         return;
0167     }
0168 
0169     uint readValue;
0170     file.read(reinterpret_cast<char*>(&readValue), sizeof(uint));
0171     // now readValue is filled with the top-context data size
0172     Q_ASSERT(readValue >= sizeof(TopDUContextData));
0173     const QByteArray data = file.read(loadType == FullLoad ? readValue : sizeof(TopDUContextData));
0174     const auto* topData = reinterpret_cast<const TopDUContextData*>(data.constData());
0175     callback(topData);
0176 }
0177 
0178 template <typename T>
0179 struct PtrType;
0180 
0181 template <typename T>
0182 struct PtrType<T*>
0183 {
0184     using value = T *;
0185 };
0186 
0187 template <typename T>
0188 struct PtrType<QExplicitlySharedDataPointer<T>>
0189 {
0190     using value = T *;
0191 };
0192 
0193 template <typename T>
0194 Q_DECL_CONSTEXPR bool isSharedDataItem()
0195 {
0196     return false;
0197 }
0198 
0199 template <>
0200 Q_DECL_CONSTEXPR bool isSharedDataItem<ProblemPointer>()
0201 {
0202     return true;
0203 }
0204 }
0205 
0206 //BEGIN DUChainItemStorage
0207 
0208 template <class Item>
0209 TopDUContextDynamicData::DUChainItemStorage<Item>::DUChainItemStorage(TopDUContextDynamicData* data)
0210     : data(data)
0211 {
0212 }
0213 
0214 template <class Item>
0215 TopDUContextDynamicData::DUChainItemStorage<Item>::~DUChainItemStorage()
0216 {
0217     clearItems();
0218 }
0219 
0220 template <class Item>
0221 void TopDUContextDynamicData::DUChainItemStorage<Item>::clearItems()
0222 {
0223     //Due to template specialization it's possible that a declaration is not reachable through the normal context structure.
0224     //For that reason we have to check here, and delete all remaining declarations.
0225     qDeleteAll(temporaryItems);
0226     temporaryItems.clear();
0227     qDeleteAll(items);
0228     items.clear();
0229 }
0230 
0231 namespace KDevelop {
0232 template <>
0233 void TopDUContextDynamicData::DUChainItemStorage<ProblemPointer>::clearItems()
0234 {
0235     // don't delete anything - the problem is shared
0236     items.clear();
0237 }
0238 }
0239 
0240 template <class Item>
0241 void TopDUContextDynamicData::DUChainItemStorage<Item>::clearItemIndex(const Item& item, const uint index)
0242 {
0243     if (!data->m_dataLoaded)
0244         data->loadData();
0245 
0246     if (index < (0x0fffffff / 2)) {
0247         if (index == 0 || index > uint(items.size())) {
0248             return;
0249         } else {
0250             const uint realIndex = index - 1;
0251             Q_ASSERT(items[realIndex] == item);
0252             items[realIndex] = nullptr;
0253 
0254             if (realIndex < ( uint )offsets.size()) {
0255                 offsets[realIndex] = ItemDataInfo();
0256             }
0257         }
0258     } else {
0259         const uint realIndex = 0x0fffffff - index; //We always keep the highest bit at zero
0260         if (realIndex == 0 || realIndex > uint(temporaryItems.size())) {
0261             return;
0262         } else {
0263             Q_ASSERT(temporaryItems[realIndex - 1] == item);
0264             temporaryItems[realIndex - 1] = nullptr;
0265         }
0266     }
0267 
0268     Q_UNUSED(item);
0269 }
0270 
0271 template <class Item>
0272 void TopDUContextDynamicData::DUChainItemStorage<Item>::storeData(uint& currentDataOffset,
0273                                                                   const QVector<ArrayWithPosition>& oldData)
0274 {
0275     auto const oldOffsets = offsets;
0276     offsets.clear();
0277     offsets.reserve(items.size());
0278     for (int a = 0; a < items.size(); ++a) {
0279         auto item = items[a];
0280         if (!item) {
0281             if (oldOffsets.size() > a && oldOffsets[a].dataOffset) {
0282                 //Directly copy the old data range into the new data
0283                 const DUChainBaseData* itemData = nullptr;
0284                 if (data->m_mappedData) {
0285                     itemData = reinterpret_cast<const DUChainBaseData*>(data->m_mappedData + oldOffsets[a].dataOffset);
0286                 } else {
0287                     itemData =
0288                         reinterpret_cast<const DUChainBaseData*>(::pointerInData(oldData, oldOffsets[a].dataOffset));
0289                 }
0290                 offsets << data->writeDataInfo(oldOffsets[a], itemData, currentDataOffset);
0291             } else {
0292                 offsets << ItemDataInfo();
0293             }
0294         } else {
0295             offsets << ItemDataInfo{currentDataOffset, indexForParentContext(item)};
0296             saveDUChainItem(data->m_data, *item, currentDataOffset, isSharedDataItem<Item>());
0297         }
0298     }
0299 
0300 #ifndef QT_NO_DEBUG
0301     if (!isSharedDataItem<Item>()) {
0302         for (auto item : items) {
0303             if (item) {
0304                 validateItem(item->d_func(), data->m_mappedData, data->m_mappedDataSize);
0305             }
0306         }
0307     }
0308 #endif
0309 }
0310 
0311 template <typename Item>
0312 bool TopDUContextDynamicData::DUChainItemStorage<Item>::itemsHaveChanged() const
0313 {
0314     for (auto item : items) {
0315         if (item && item->d_func()->m_dynamic) {
0316             return true;
0317         }
0318     }
0319 
0320     return false;
0321 }
0322 
0323 template <class Item>
0324 uint TopDUContextDynamicData::DUChainItemStorage<Item>::allocateItemIndex(const Item& item, const bool temporary)
0325 {
0326     if (!data->m_dataLoaded) {
0327         data->loadData();
0328     }
0329     if (!temporary) {
0330         items.append(item);
0331         return items.size();
0332     } else {
0333         temporaryItems.append(item);
0334         return 0x0fffffff - temporaryItems.size(); //We always keep the highest bit at zero
0335     }
0336 }
0337 
0338 template <class Item>
0339 bool TopDUContextDynamicData::DUChainItemStorage<Item>::isItemForIndexLoaded(uint index) const
0340 {
0341     if (!data->m_dataLoaded) {
0342         return false;
0343     }
0344     if (index < (0x0fffffff / 2)) {
0345         if (index == 0 || index > uint(items.size())) {
0346             return false;
0347         }
0348         return items[index - 1];
0349     } else {
0350         // temporary item
0351         return true;
0352     }
0353 }
0354 
0355 template <class Item>
0356 Item TopDUContextDynamicData::DUChainItemStorage<Item>::itemForIndex(uint index) const
0357 {
0358     if (index >= (0x0fffffff / 2)) {
0359         index = 0x0fffffff - index; //We always keep the highest bit at zero
0360         if (index == 0 || index > uint(temporaryItems.size()))
0361             return {};
0362         else
0363             return temporaryItems.at(index - 1);
0364     }
0365 
0366     if (index == 0 || index > static_cast<uint>(items.size())) {
0367         qCWarning(LANGUAGE) << "item index out of bounds:" << index << "count:" << items.size();
0368         return {};
0369     }
0370     const uint realIndex = index - 1;
0371     const auto& item = items.at(realIndex);
0372     if (item) {
0373         //Shortcut, because this is the most common case
0374         return item;
0375     }
0376 
0377     if (realIndex < ( uint )offsets.size() && offsets[realIndex].dataOffset) {
0378         Q_ASSERT(!data->m_itemRetrievalForbidden);
0379 
0380         //Construct the context, and eventually its parent first
0381         ///TODO: ugly, remove need for const_cast
0382         auto itemData = const_cast<DUChainBaseData*>(
0383             reinterpret_cast<const DUChainBaseData*>(data->pointerInData(offsets[realIndex].dataOffset))
0384                         );
0385 
0386         auto& item = items[realIndex];
0387         item = dynamic_cast<typename PtrType<Item>::value>(DUChainItemSystem::self().create(itemData));
0388         if (!item) {
0389             //When this happens, the item has not been registered correctly.
0390             //We can stop here, because else we will get crashes later.
0391             qCritical() << "Failed to load item with identity" << itemData->classId;
0392             return {};
0393         }
0394 
0395         if (isSharedDataItem<Item>()) {
0396             // NOTE: shared data must never point to mmapped data regions as otherwise we might end up with
0397             // use-after-free or double-deletions etc. pp.
0398             // thus, make the item always dynamic after deserialization
0399             item->makeDynamic();
0400         }
0401 
0402         auto parent = data->contextForIndex(offsets[realIndex].parentContext);
0403         Q_ASSERT_X(parent, Q_FUNC_INFO, "Could not find parent context for loaded item.\n"
0404                    "Potentially, the context has been deleted without deleting its children.");
0405         item->rebuildDynamicData(parent, index);
0406     } else {
0407         qCWarning(LANGUAGE) << "invalid item for index" << index << offsets.size() <<
0408             offsets.value(realIndex).dataOffset;
0409     }
0410 
0411     return item;
0412 }
0413 
0414 template <class Item>
0415 void TopDUContextDynamicData::DUChainItemStorage<Item>::deleteOnDisk()
0416 {
0417     for (auto& item : items) {
0418         if (item) {
0419             item->makeDynamic();
0420         }
0421     }
0422 }
0423 
0424 template <class Item>
0425 void TopDUContextDynamicData::DUChainItemStorage<Item>::loadData(QFile* file) const
0426 {
0427     Q_ASSERT(offsets.isEmpty());
0428     Q_ASSERT(items.isEmpty());
0429 
0430     uint readValue;
0431     file->read(reinterpret_cast<char*>(&readValue), sizeof(uint));
0432     offsets.resize(readValue);
0433 
0434     file->read(reinterpret_cast<char*>(offsets.data()), sizeof(ItemDataInfo) * offsets.size());
0435 
0436     //Fill with zeroes for now, will be initialized on-demand
0437     items.resize(offsets.size());
0438 }
0439 
0440 template <class Item>
0441 void TopDUContextDynamicData::DUChainItemStorage<Item>::writeData(QFile* file)
0442 {
0443     uint writeValue = offsets.size();
0444     file->write(reinterpret_cast<const char*>(&writeValue), sizeof(uint));
0445     file->write(reinterpret_cast<const char*>(offsets.data()), sizeof(ItemDataInfo) * offsets.size());
0446 }
0447 
0448 //END DUChainItemStorage
0449 
0450 const char* TopDUContextDynamicData::pointerInData(uint totalOffset) const
0451 {
0452     Q_ASSERT(!m_mappedData || m_data.isEmpty());
0453 
0454     if (m_mappedData && m_mappedDataSize)
0455         return reinterpret_cast<const char*>(m_mappedData) + totalOffset;
0456 
0457     return ::pointerInData(m_data, totalOffset);
0458 }
0459 
0460 TopDUContextDynamicData::TopDUContextDynamicData(TopDUContext* topContext)
0461     : m_deleting(false)
0462     , m_topContext(topContext)
0463     , m_contexts(this)
0464     , m_declarations(this)
0465     , m_problems(this)
0466     , m_onDisk(false)
0467     , m_dataLoaded(true)
0468     , m_mappedFile(nullptr)
0469     , m_mappedData(nullptr)
0470     , m_mappedDataSize(0)
0471     , m_itemRetrievalForbidden(false)
0472 {
0473 }
0474 
0475 void KDevelop::TopDUContextDynamicData::clear()
0476 {
0477     m_contexts.clearItems();
0478     m_declarations.clearItems();
0479     m_problems.clearItems();
0480 }
0481 
0482 TopDUContextDynamicData::~TopDUContextDynamicData()
0483 {
0484     unmap();
0485 }
0486 
0487 void KDevelop::TopDUContextDynamicData::unmap()
0488 {
0489     delete m_mappedFile;
0490     m_mappedFile = nullptr;
0491     m_mappedData = nullptr;
0492     m_mappedDataSize = 0;
0493 }
0494 
0495 bool TopDUContextDynamicData::fileExists(uint topContextIndex)
0496 {
0497     return QFile::exists(pathForTopContext(topContextIndex));
0498 }
0499 
0500 QList<IndexedDUContext> TopDUContextDynamicData::loadImporters(uint topContextIndex)
0501 {
0502     QList<IndexedDUContext> ret;
0503     loadTopDUContextData(topContextIndex, FullLoad, [&ret](const TopDUContextData* topData) {
0504         ret.reserve(topData->m_importersSize());
0505         FOREACH_FUNCTION(const IndexedDUContext &importer, topData->m_importers)
0506         ret << importer;
0507     });
0508     return ret;
0509 }
0510 
0511 QList<IndexedDUContext> TopDUContextDynamicData::loadImports(uint topContextIndex)
0512 {
0513     QList<IndexedDUContext> ret;
0514     loadTopDUContextData(topContextIndex, FullLoad, [&ret](const TopDUContextData* topData) {
0515         ret.reserve(topData->m_importedContextsSize());
0516         FOREACH_FUNCTION(const DUContext::Import& import, topData->m_importedContexts)
0517         ret << import.indexedContext();
0518     });
0519     return ret;
0520 }
0521 
0522 IndexedString TopDUContextDynamicData::loadUrl(uint topContextIndex)
0523 {
0524     IndexedString url;
0525     loadTopDUContextData(topContextIndex, PartialLoad, [&url](const TopDUContextData* topData) {
0526         Q_ASSERT(topData->m_url.isEmpty() || topData->m_url.index() >> 16);
0527         url = topData->m_url;
0528     });
0529     return url;
0530 }
0531 
0532 void TopDUContextDynamicData::loadData() const
0533 {
0534     //This function has to be protected by an additional mutex, since it can be triggered from multiple threads at the same time
0535     static QMutex mutex;
0536     QMutexLocker lock(&mutex);
0537     if (m_dataLoaded)
0538         return;
0539 
0540     Q_ASSERT(!m_dataLoaded);
0541     Q_ASSERT(m_data.isEmpty());
0542 
0543     auto* file = new QFile(pathForTopContext(m_topContext->ownIndex()));
0544     bool open = file->open(QIODevice::ReadOnly);
0545     Q_UNUSED(open);
0546     Q_ASSERT(open);
0547     Q_ASSERT(file->size());
0548 
0549     //Skip the offsets, we're already read them
0550     //Skip top-context data
0551     uint readValue;
0552     file->read(reinterpret_cast<char*>(&readValue), sizeof(uint));
0553     file->seek(readValue + file->pos());
0554 
0555     m_contexts.loadData(file);
0556     m_declarations.loadData(file);
0557     m_problems.loadData(file);
0558 
0559 #ifdef USE_MMAP
0560 
0561     m_mappedData = file->map(file->pos(), file->size() - file->pos());
0562     if (m_mappedData) {
0563         m_mappedFile = file;
0564         m_mappedDataSize = file->size() - file->pos();
0565         file->close(); //Close the file, so there is less open file descriptors(May be problematic)
0566     } else {
0567         qCDebug(LANGUAGE) << "Failed to map" << file->fileName();
0568     }
0569 
0570 #endif
0571 
0572     if (!m_mappedFile) {
0573         QByteArray data = file->readAll();
0574         m_data.append({data, ( uint )data.size()});
0575         delete file;
0576     }
0577 
0578     m_dataLoaded = true;
0579 }
0580 
0581 TopDUContext* TopDUContextDynamicData::load(uint topContextIndex)
0582 {
0583     QFile file(pathForTopContext(topContextIndex));
0584     if (file.open(QIODevice::ReadOnly)) {
0585         if (file.size() == 0) {
0586             qCWarning(LANGUAGE) << "Top-context file is empty" << file.fileName();
0587             return nullptr;
0588         }
0589 
0590         uint readValue;
0591         file.read(reinterpret_cast<char*>(&readValue), sizeof(uint));
0592         //now readValue is filled with the top-context data size
0593         QByteArray topContextData = file.read(readValue);
0594 
0595         auto* topData = reinterpret_cast<DUChainBaseData*>(topContextData.data());
0596         auto* ret = dynamic_cast<TopDUContext*>(DUChainItemSystem::self().create(topData));
0597         if (!ret) {
0598             qCWarning(LANGUAGE) << "Cannot load a top-context from file" << file.fileName() <<
0599                 "- the required language-support for handling ID" << topData->classId << "is probably not loaded";
0600             return nullptr;
0601         }
0602 
0603         TopDUContextDynamicData& target(*ret->m_dynamicData);
0604 
0605         target.m_data.clear();
0606         target.m_dataLoaded = false;
0607         target.m_onDisk = true;
0608         ret->rebuildDynamicData(nullptr, topContextIndex);
0609         target.m_topContextData.append({topContextData, ( uint )0});
0610         return ret;
0611     } else {
0612         return nullptr;
0613     }
0614 }
0615 
0616 bool TopDUContextDynamicData::isOnDisk() const
0617 {
0618     return m_onDisk;
0619 }
0620 
0621 void TopDUContextDynamicData::deleteOnDisk()
0622 {
0623     if (!isOnDisk())
0624         return;
0625     qCDebug(LANGUAGE) << "deleting" << m_topContext->ownIndex() << m_topContext->url().str();
0626 
0627     if (!m_dataLoaded)
0628         loadData();
0629 
0630     m_contexts.deleteOnDisk();
0631     m_declarations.deleteOnDisk();
0632     m_problems.deleteOnDisk();
0633 
0634     m_topContext->makeDynamic();
0635 
0636     m_onDisk = false;
0637 
0638     bool successfullyRemoved = QFile::remove(filePath());
0639     Q_UNUSED(successfullyRemoved);
0640     Q_ASSERT(successfullyRemoved);
0641     qCDebug(LANGUAGE) << "deletion ready";
0642 }
0643 
0644 QString KDevelop::TopDUContextDynamicData::filePath() const
0645 {
0646     return pathForTopContext(m_topContext->ownIndex());
0647 }
0648 
0649 bool TopDUContextDynamicData::hasChanged() const
0650 {
0651     return !m_onDisk || m_topContext->d_func()->m_dynamic
0652            || m_contexts.itemsHaveChanged() || m_declarations.itemsHaveChanged()
0653            || m_problems.itemsHaveChanged();
0654 }
0655 
0656 void TopDUContextDynamicData::store()
0657 {
0658 //   qCDebug(LANGUAGE) << "storing" << m_topContext->url().str() << m_topContext->ownIndex() << "import-count:" << m_topContext->importedParentContexts().size();
0659 
0660     //Check if something has changed. If nothing has changed, don't store to disk.
0661     bool contentDataChanged = hasChanged();
0662     if (!contentDataChanged) {
0663         return;
0664     }
0665 
0666     ///@todo Save the meta-data into a repository, and only the actual content data into a file.
0667     ///      This will make saving+loading more efficient, and will reduce the disk-usage.
0668     ///      Then we also won't need to load the data if only the meta-data changed.
0669     if (!m_dataLoaded)
0670         loadData();
0671 
0672     ///If the data is mapped, and we re-write the file, we must make sure that the data is copied out of the map,
0673     ///even if only metadata is changed.
0674     ///@todo If we split up data and metadata, we don't need to do this
0675     if (m_mappedData)
0676         contentDataChanged = true;
0677 
0678     m_topContext->makeDynamic();
0679     m_topContextData.clear();
0680     Q_ASSERT(m_topContext->d_func()->m_ownIndex == m_topContext->ownIndex());
0681 
0682     uint topContextDataSize = DUChainItemSystem::self().dynamicSize(*m_topContext->d_func());
0683     m_topContextData.append({QByteArray(DUChainItemSystem::self().dynamicSize(*m_topContext->d_func()),
0684                                         topContextDataSize), 0u});
0685     uint actualTopContextDataSize = 0;
0686 
0687     if (contentDataChanged) {
0688         //We don't need these structures any more, since we have loaded all the declarations/contexts, and m_data
0689         //will be reset which these structures pointed into
0690         //Load all lazy declarations/contexts
0691 
0692         const auto oldData = m_data; //Keep the old data alive until everything is stored into a new data structure
0693 
0694         m_data.clear();
0695 
0696         uint newDataSize = 0;
0697         for (const ArrayWithPosition& array : oldData) {
0698             newDataSize += array.position;
0699         }
0700 
0701         newDataSize = std::max(newDataSize, 10000u);
0702 
0703         //We always put 1 byte to the front, so we don't have zero data-offsets, since those are used for "invalid".
0704         uint currentDataOffset = 1;
0705         m_data.append({QByteArray(newDataSize, 0), currentDataOffset});
0706 
0707         m_itemRetrievalForbidden = true;
0708 
0709         m_contexts.storeData(currentDataOffset, oldData);
0710         m_declarations.storeData(currentDataOffset, oldData);
0711         m_problems.storeData(currentDataOffset, oldData);
0712 
0713         m_itemRetrievalForbidden = false;
0714     }
0715 
0716     saveDUChainItem(m_topContextData, *m_topContext, actualTopContextDataSize, false);
0717     Q_ASSERT(actualTopContextDataSize == topContextDataSize);
0718     Q_ASSERT(m_topContextData.size() == 1);
0719     Q_ASSERT(!m_topContext->d_func()->isDynamic());
0720 
0721     unmap();
0722 
0723     QDir().mkpath(basePath());
0724 
0725     QFile file(filePath());
0726     if (file.open(QIODevice::WriteOnly)) {
0727         file.resize(0);
0728 
0729         file.write(reinterpret_cast<const char*>(&topContextDataSize), sizeof(uint));
0730         for (const ArrayWithPosition& pos : qAsConst(m_topContextData)) {
0731             file.write(pos.array.constData(), pos.position);
0732         }
0733 
0734         m_contexts.writeData(&file);
0735         m_declarations.writeData(&file);
0736         m_problems.writeData(&file);
0737 
0738         for (const ArrayWithPosition& pos : qAsConst(m_data)) {
0739             file.write(pos.array.constData(), pos.position);
0740         }
0741 
0742         m_onDisk = true;
0743 
0744         if (file.size() == 0) {
0745             qCWarning(LANGUAGE) << "Saving zero size top ducontext data";
0746         }
0747         file.close();
0748     } else {
0749         qCWarning(LANGUAGE) << "Cannot open top-context for writing";
0750     }
0751 //   qCDebug(LANGUAGE) << "stored" << m_topContext->url().str() << m_topContext->ownIndex() << "import-count:" << m_topContext->importedParentContexts().size();
0752 }
0753 
0754 TopDUContextDynamicData::ItemDataInfo TopDUContextDynamicData::writeDataInfo(const ItemDataInfo& info,
0755                                                                              const DUChainBaseData* data,
0756                                                                              uint& totalDataOffset)
0757 {
0758     ItemDataInfo ret(info);
0759     Q_ASSERT(info.dataOffset);
0760     const auto size = DUChainItemSystem::self().dynamicSize(*data);
0761     Q_ASSERT(size);
0762 
0763     if (m_data.back().array.size() - m_data.back().position < size) {
0764         //Create a new m_data item
0765         m_data.append({QByteArray(std::max(size, 10000u), 0), 0u});
0766     }
0767 
0768     ret.dataOffset = totalDataOffset;
0769 
0770     uint pos = m_data.back().position;
0771     m_data.back().position += size;
0772     totalDataOffset += size;
0773 
0774     auto target = reinterpret_cast<DUChainBaseData*>(m_data.back().array.data() + pos);
0775 #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 800)
0776 #pragma GCC diagnostic push
0777 #pragma GCC diagnostic ignored "-Wclass-memaccess"
0778 #endif
0779     memcpy(target, data, size);
0780 #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 800)
0781 #pragma GCC diagnostic pop
0782 #endif
0783 
0784     verifyDataInfo(ret, m_data);
0785     return ret;
0786 }
0787 
0788 uint TopDUContextDynamicData::allocateDeclarationIndex(Declaration* decl, bool temporary)
0789 {
0790     return m_declarations.allocateItemIndex(decl, temporary);
0791 }
0792 
0793 uint TopDUContextDynamicData::allocateContextIndex(DUContext* context, bool temporary)
0794 {
0795     return m_contexts.allocateItemIndex(context, temporary);
0796 }
0797 
0798 uint TopDUContextDynamicData::allocateProblemIndex(const ProblemPointer& problem)
0799 {
0800     return m_problems.allocateItemIndex(problem, false);
0801 }
0802 
0803 bool TopDUContextDynamicData::isDeclarationForIndexLoaded(uint index) const
0804 {
0805     return m_declarations.isItemForIndexLoaded(index);
0806 }
0807 
0808 bool TopDUContextDynamicData::isContextForIndexLoaded(uint index) const
0809 {
0810     return m_contexts.isItemForIndexLoaded(index);
0811 }
0812 
0813 bool TopDUContextDynamicData::isTemporaryContextIndex(uint index) const
0814 {
0815     return !(index < (0x0fffffff / 2));
0816 }
0817 
0818 bool TopDUContextDynamicData::isTemporaryDeclarationIndex(uint index) const
0819 {
0820     return !(index < (0x0fffffff / 2));
0821 }
0822 
0823 DUContext* TopDUContextDynamicData::contextForIndex(uint index) const
0824 {
0825     if (!m_dataLoaded)
0826         loadData();
0827 
0828     if (index == 0) {
0829         return m_topContext;
0830     }
0831 
0832     return m_contexts.itemForIndex(index);
0833 }
0834 
0835 Declaration* TopDUContextDynamicData::declarationForIndex(uint index) const
0836 {
0837     if (!m_dataLoaded)
0838         loadData();
0839 
0840     return m_declarations.itemForIndex(index);
0841 }
0842 
0843 ProblemPointer TopDUContextDynamicData::problemForIndex(uint index) const
0844 {
0845     if (!m_dataLoaded)
0846         loadData();
0847 
0848     return m_problems.itemForIndex(index);
0849 }
0850 
0851 void TopDUContextDynamicData::clearDeclarationIndex(Declaration* decl)
0852 {
0853     m_declarations.clearItemIndex(decl, decl->m_indexInTopContext);
0854 }
0855 
0856 void TopDUContextDynamicData::clearContextIndex(DUContext* context)
0857 {
0858     m_contexts.clearItemIndex(context, context->m_dynamicData->m_indexInTopContext);
0859 }
0860 
0861 void TopDUContextDynamicData::clearProblems()
0862 {
0863     m_problems.clearItems();
0864 }