File indexing completed on 2024-05-12 04:37:57
0001 /* 0002 SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "codemodel.h" 0008 0009 #include "appendedlist.h" 0010 #include <debug.h> 0011 #include <serialization/itemrepository.h> 0012 #include "identifier.h" 0013 #include <serialization/indexedstring.h> 0014 #include <serialization/referencecounting.h> 0015 #include <util/embeddedfreetree.h> 0016 0017 #define ifDebug(x) 0018 0019 namespace KDevelop { 0020 class CodeModelItemHandler 0021 { 0022 public: 0023 static int leftChild(const CodeModelItem& m_data) 0024 { 0025 return ( int )m_data.referenceCount; 0026 } 0027 static void setLeftChild(CodeModelItem& m_data, int child) 0028 { 0029 m_data.referenceCount = ( uint )child; 0030 } 0031 static int rightChild(const CodeModelItem& m_data) 0032 { 0033 return ( int )m_data.uKind; 0034 } 0035 static void setRightChild(CodeModelItem& m_data, int child) 0036 { 0037 m_data.uKind = ( uint )child; 0038 } 0039 //Copies this item into the given one 0040 static void copyTo(const CodeModelItem& m_data, CodeModelItem& data) 0041 { 0042 data = m_data; 0043 } 0044 0045 static void createFreeItem(CodeModelItem& data) 0046 { 0047 data = CodeModelItem(); 0048 data.referenceCount = (uint) - 1; 0049 data.uKind = (uint) - 1; 0050 } 0051 0052 static bool isFree(const CodeModelItem& m_data) 0053 { 0054 return !m_data.id.isValid(); 0055 } 0056 0057 static const CodeModelItem& data(const CodeModelItem& m_data) 0058 { 0059 return m_data; 0060 } 0061 0062 static bool equals(const CodeModelItem& m_data, const CodeModelItem& rhs) 0063 { 0064 return m_data.id == rhs.id; 0065 } 0066 }; 0067 0068 DEFINE_LIST_MEMBER_HASH(CodeModelRepositoryItem, items, CodeModelItem) 0069 0070 class CodeModelRepositoryItem 0071 { 0072 public: 0073 CodeModelRepositoryItem() 0074 { 0075 initializeAppendedLists(); 0076 } 0077 CodeModelRepositoryItem(const CodeModelRepositoryItem& rhs, bool dynamic = true) : file(rhs.file) 0078 , centralFreeItem(rhs.centralFreeItem) 0079 { 0080 initializeAppendedLists(dynamic); 0081 copyListsFrom(rhs); 0082 } 0083 0084 ~CodeModelRepositoryItem() 0085 { 0086 freeAppendedLists(); 0087 } 0088 0089 CodeModelRepositoryItem& operator=(const CodeModelRepositoryItem& rhs) = delete; 0090 0091 unsigned int hash() const 0092 { 0093 //We only compare the declaration. This allows us implementing a map, although the item-repository 0094 //originally represents a set. 0095 return file.index(); 0096 } 0097 0098 uint itemSize() const 0099 { 0100 return dynamicSize(); 0101 } 0102 0103 uint classSize() const 0104 { 0105 return sizeof(CodeModelRepositoryItem); 0106 } 0107 0108 IndexedString file; 0109 int centralFreeItem = -1; 0110 0111 START_APPENDED_LISTS(CodeModelRepositoryItem); 0112 APPENDED_LIST_FIRST(CodeModelRepositoryItem, CodeModelItem, items); 0113 END_APPENDED_LISTS(CodeModelRepositoryItem, items); 0114 }; 0115 0116 class CodeModelRequestItem 0117 { 0118 public: 0119 0120 CodeModelRequestItem(const CodeModelRepositoryItem& item) : m_item(item) 0121 { 0122 } 0123 enum { 0124 AverageSize = 30 //This should be the approximate average size of an Item 0125 }; 0126 0127 unsigned int hash() const 0128 { 0129 return m_item.hash(); 0130 } 0131 0132 uint itemSize() const 0133 { 0134 return m_item.itemSize(); 0135 } 0136 0137 void createItem(CodeModelRepositoryItem* item) const 0138 { 0139 Q_ASSERT(shouldDoDUChainReferenceCounting(item)); 0140 Q_ASSERT(shouldDoDUChainReferenceCounting(reinterpret_cast<char*>(item) + (itemSize() - 1))); 0141 new (item) CodeModelRepositoryItem(m_item, false); 0142 } 0143 0144 static void destroy(CodeModelRepositoryItem* item, KDevelop::AbstractItemRepository&) 0145 { 0146 Q_ASSERT(shouldDoDUChainReferenceCounting(item)); 0147 // Q_ASSERT(shouldDoDUChainReferenceCounting(((char*)item) + (itemSize()-1))); 0148 item->~CodeModelRepositoryItem(); 0149 } 0150 0151 static bool persistent(const CodeModelRepositoryItem* item) 0152 { 0153 Q_UNUSED(item); 0154 return true; 0155 } 0156 0157 bool equals(const CodeModelRepositoryItem* item) const 0158 { 0159 return m_item.file == item->file; 0160 } 0161 0162 const CodeModelRepositoryItem& m_item; 0163 }; 0164 0165 // Maps declaration-ids to items 0166 using CodeModelRepo = ItemRepository<CodeModelRepositoryItem, CodeModelRequestItem>; 0167 template <> 0168 class ItemRepositoryFor<CodeModel> 0169 { 0170 friend struct LockedItemRepository; 0171 static CodeModelRepo& repo() 0172 { 0173 static QMutex mutex; 0174 static CodeModelRepo repo(QStringLiteral("Code Model"), &mutex); 0175 return repo; 0176 } 0177 }; 0178 0179 CodeModel::CodeModel() 0180 { 0181 LockedItemRepository::initialize<CodeModel>(); 0182 } 0183 0184 void CodeModel::addItem(const IndexedString& file, const IndexedQualifiedIdentifier& id, CodeModelItem::Kind kind) 0185 { 0186 ifDebug(qCDebug(LANGUAGE) << "addItem" << file.str() << id.identifier().toString() << id.index; ) 0187 0188 if (!id.isValid()) 0189 return; 0190 CodeModelRepositoryItem item; 0191 item.file = file; 0192 CodeModelRequestItem request(item); 0193 0194 CodeModelItem newItem; 0195 newItem.id = id; 0196 newItem.kind = kind; 0197 newItem.referenceCount = 1; 0198 0199 LockedItemRepository::write<CodeModel>([&](CodeModelRepo& repo) { 0200 uint index = repo.findIndex(item); 0201 0202 if (index) { 0203 const CodeModelRepositoryItem* oldItem = repo.itemFromIndex(index); 0204 EmbeddedTreeAlgorithms<CodeModelItem, CodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), 0205 oldItem->centralFreeItem); 0206 0207 int listIndex = alg.indexOf(newItem); 0208 0209 DynamicItem<CodeModelRepositoryItem, true> editableItem = repo.dynamicItemFromIndex(index); 0210 auto* items = const_cast<CodeModelItem*>(editableItem->items()); 0211 0212 if (listIndex != -1) { 0213 // Only update the reference-count 0214 ++items[listIndex].referenceCount; 0215 items[listIndex].kind = kind; 0216 return; 0217 } else { 0218 // Add the item to the list 0219 EmbeddedTreeAddItem<CodeModelItem, CodeModelItemHandler> add(items, editableItem->itemsSize(), 0220 editableItem->centralFreeItem, newItem); 0221 0222 if (add.newItemCount() != editableItem->itemsSize()) { 0223 // The data needs to be transferred into a bigger list. That list is within "item". 0224 0225 item.itemsList().resize(add.newItemCount()); 0226 add.transferData(item.itemsList().data(), item.itemsList().size(), &item.centralFreeItem); 0227 0228 repo.deleteItem(index); 0229 } else { 0230 // We're fine: The item fits into the existing list. 0231 return; 0232 } 0233 } 0234 } else { 0235 // We're creating a new index 0236 item.itemsList().append(newItem); 0237 } 0238 0239 Q_ASSERT(!repo.findIndex(request)); 0240 0241 // This inserts the changed item 0242 const uint newIndex = repo.index(request); 0243 Q_UNUSED(newIndex); 0244 ifDebug(qCDebug(LANGUAGE) << "new index" << newIndex;) 0245 0246 Q_ASSERT(repo.findIndex(request)); 0247 }); 0248 } 0249 0250 void CodeModel::updateItem(const IndexedString& file, const IndexedQualifiedIdentifier& id, CodeModelItem::Kind kind) 0251 { 0252 ifDebug(qCDebug(LANGUAGE) << file.str() << id.identifier().toString() << kind; ) 0253 0254 if (!id.isValid()) 0255 return; 0256 0257 CodeModelRepositoryItem item; 0258 item.file = file; 0259 CodeModelRequestItem request(item); 0260 0261 CodeModelItem newItem; 0262 newItem.id = id; 0263 newItem.kind = kind; 0264 newItem.referenceCount = 1; 0265 0266 LockedItemRepository::write<CodeModel>([&](CodeModelRepo& repo) { 0267 uint index = repo.findIndex(item); 0268 0269 if (index) { 0270 // Check whether the item is already in the mapped list, else copy the list into the new created item 0271 DynamicItem<CodeModelRepositoryItem, true> oldItem = repo.dynamicItemFromIndex(index); 0272 0273 EmbeddedTreeAlgorithms<CodeModelItem, CodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), 0274 oldItem->centralFreeItem); 0275 int listIndex = alg.indexOf(newItem); 0276 Q_ASSERT(listIndex != -1); 0277 0278 auto* items = const_cast<CodeModelItem*>(oldItem->items()); 0279 0280 Q_ASSERT(items[listIndex].id == id); 0281 items[listIndex].kind = kind; 0282 0283 return; 0284 } 0285 0286 Q_ASSERT(0); // The updated item as not in the symbol table! 0287 }); 0288 } 0289 0290 void CodeModel::removeItem(const IndexedString& file, const IndexedQualifiedIdentifier& id) 0291 { 0292 if (!id.isValid()) 0293 return; 0294 0295 ifDebug(qCDebug(LANGUAGE) << "removeItem" << file.str() << id.identifier().toString(); ) 0296 CodeModelRepositoryItem item; 0297 item.file = file; 0298 CodeModelRequestItem request(item); 0299 0300 LockedItemRepository::write<CodeModel>([&](CodeModelRepo& repo) { 0301 uint index = repo.findIndex(item); 0302 0303 if (index) { 0304 CodeModelItem searchItem; 0305 searchItem.id = id; 0306 0307 DynamicItem<CodeModelRepositoryItem, true> oldItem = repo.dynamicItemFromIndex(index); 0308 0309 EmbeddedTreeAlgorithms<CodeModelItem, CodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), 0310 oldItem->centralFreeItem); 0311 0312 int listIndex = alg.indexOf(searchItem); 0313 if (listIndex == -1) 0314 return; 0315 0316 auto* items = const_cast<CodeModelItem*>(oldItem->items()); 0317 0318 --items[listIndex].referenceCount; 0319 0320 if (oldItem->items()[listIndex].referenceCount) 0321 return; // Nothing to remove, there's still a reference-count left 0322 0323 // We have reduced the reference-count to zero, so remove the item from the list 0324 0325 EmbeddedTreeRemoveItem<CodeModelItem, CodeModelItemHandler> remove(items, oldItem->itemsSize(), 0326 oldItem->centralFreeItem, searchItem); 0327 0328 uint newItemCount = remove.newItemCount(); 0329 if (newItemCount != oldItem->itemsSize()) { 0330 if (newItemCount == 0) { 0331 // Has become empty, delete the item 0332 repo.deleteItem(index); 0333 0334 return; 0335 } else { 0336 // Make smaller 0337 item.itemsList().resize(newItemCount); 0338 remove.transferData(item.itemsList().data(), item.itemsSize(), &item.centralFreeItem); 0339 0340 // Delete the old list 0341 repo.deleteItem(index); 0342 // Add the new list 0343 repo.index(request); 0344 return; 0345 } 0346 } 0347 } 0348 }); 0349 } 0350 0351 // FIXME: this API is unsafe, we should return a KDevVarLengthArray of values instead 0352 void CodeModel::items(const IndexedString& file, uint& count, const CodeModelItem*& items) const 0353 { 0354 ifDebug(qCDebug(LANGUAGE) << "items" << file.str(); ) 0355 0356 CodeModelRepositoryItem item; 0357 item.file = file; 0358 CodeModelRequestItem request(item); 0359 0360 LockedItemRepository::read<CodeModel>([&](const CodeModelRepo& repo) { 0361 uint index = repo.findIndex(item); 0362 0363 if (index) { 0364 const CodeModelRepositoryItem* repositoryItem = repo.itemFromIndex(index); 0365 ifDebug(qCDebug(LANGUAGE) << "found index" << index << repositoryItem->itemsSize();) 0366 count = repositoryItem->itemsSize(); 0367 items = repositoryItem->items(); 0368 } else { 0369 ifDebug(qCDebug(LANGUAGE) << "found no index";) 0370 count = 0; 0371 items = nullptr; 0372 } 0373 }); 0374 } 0375 0376 CodeModel& CodeModel::self() 0377 { 0378 static CodeModel ret; 0379 return ret; 0380 } 0381 }