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 }