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 }