File indexing completed on 2024-04-21 15:24:16

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