File indexing completed on 2024-05-19 05:42:11
0001 // ct_lvtmdb_objectstore.cpp -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtmdb_objectstore.h> 0021 0022 #include <ct_lvtmdb_componentobject.h> 0023 #include <ct_lvtmdb_errorobject.h> 0024 #include <ct_lvtmdb_fieldobject.h> 0025 #include <ct_lvtmdb_fileobject.h> 0026 #include <ct_lvtmdb_functionobject.h> 0027 #include <ct_lvtmdb_methodobject.h> 0028 #include <ct_lvtmdb_namespaceobject.h> 0029 #include <ct_lvtmdb_packageobject.h> 0030 #include <ct_lvtmdb_repositoryobject.h> 0031 #include <ct_lvtmdb_typeobject.h> 0032 #include <ct_lvtmdb_variableobject.h> 0033 0034 #include <cassert> 0035 #include <iostream> 0036 #include <unordered_map> 0037 0038 namespace { 0039 0040 template<class... ARGS> 0041 typename std::unordered_map<ARGS...>::mapped_type lookup(const std::unordered_map<ARGS...>& cache, 0042 const typename std::unordered_map<ARGS...>::key_type& key) 0043 // Attempt a lookup of a pointer-like thing in an unordered_map, if it isn't 0044 // found, return nullptr 0045 { 0046 try { 0047 return cache.at(key); 0048 } catch (const std::out_of_range&) { 0049 return nullptr; 0050 } 0051 } 0052 0053 template<class OBJECT> 0054 OBJECT *lookupByQName(const std::unordered_map<std::string, std::unique_ptr<OBJECT>>& map, const std::string& key) 0055 // It would be really cool if we could implement this in terms of lookup() 0056 // but we can't copy construct a unique_ptr. Returning a reference doesn't 0057 // work because the nullptr return would have nothing to refer to 0058 { 0059 try { 0060 return map.at(key).get(); 0061 } catch (const std::out_of_range&) { 0062 return nullptr; 0063 } 0064 } 0065 0066 template<class OBJECT> 0067 std::vector<OBJECT *> getAll(const std::unordered_map<std::string, std::unique_ptr<OBJECT>>& map) 0068 { 0069 std::vector<OBJECT *> out; 0070 out.reserve(map.size()); 0071 0072 for (const auto& [qualifiedName, ptr] : map) { 0073 (void) qualifiedName; 0074 out.push_back(ptr.get()); 0075 } 0076 0077 return out; 0078 } 0079 0080 template<class OBJECT> 0081 OBJECT *add(std::unordered_map<std::string, std::unique_ptr<OBJECT>>& map, 0082 std::string qualifiedName, 0083 std::unique_ptr<OBJECT>&& val) 0084 { 0085 auto [it, _] = map.emplace(std::move(qualifiedName), std::move(val)); 0086 return it->second.get(); 0087 } 0088 0089 } // namespace 0090 0091 namespace Codethink::lvtmdb { 0092 0093 struct ObjectStore::Private { 0094 template<class OBJECT> 0095 struct Maps { 0096 std::unordered_map<std::string, std::unique_ptr<OBJECT>> store; 0097 }; 0098 0099 Maps<ComponentObject> components; 0100 Maps<ErrorObject> errors; 0101 Maps<FieldObject> fields; 0102 Maps<FileObject> files; 0103 Maps<FunctionObject> functions; 0104 Maps<MethodObject> methods; 0105 Maps<NamespaceObject> namespaces; 0106 Maps<RepositoryObject> repositories; 0107 Maps<PackageObject> packages; 0108 Maps<TypeObject> types; 0109 Maps<VariableObject> variables; 0110 0111 ObjectStore::State state = ObjectStore::State::NoneReady; 0112 }; 0113 0114 void ObjectStore::clear() 0115 { 0116 assertWritable(); 0117 d->state = State::NoneReady; 0118 d->files.store.clear(); 0119 d->components.store.clear(); 0120 d->packages.store.clear(); 0121 d->errors.store.clear(); 0122 d->namespaces.store.clear(); 0123 d->variables.store.clear(); 0124 d->functions.store.clear(); 0125 d->types.store.clear(); 0126 d->fields.store.clear(); 0127 d->methods.store.clear(); 0128 } 0129 0130 ObjectStore::ObjectStore(): d(std::make_unique<Private>()) 0131 { 0132 } 0133 0134 ObjectStore::~ObjectStore() noexcept = default; 0135 0136 void ObjectStore::setState(State state) 0137 { 0138 d->state = state; 0139 } 0140 0141 ObjectStore::State ObjectStore::state() const 0142 { 0143 return d->state; 0144 } 0145 0146 FileObject *ObjectStore::getFile(const std::string& qualifiedName) const 0147 { 0148 assertReadable(); 0149 return lookupByQName(d->files.store, qualifiedName); 0150 } 0151 0152 ComponentObject *ObjectStore::getComponent(const std::string& qualifiedName) const 0153 { 0154 assertReadable(); 0155 return lookupByQName(d->components.store, qualifiedName); 0156 } 0157 0158 RepositoryObject *ObjectStore::getRepository(const std::string& qualifiedName) const 0159 { 0160 assertReadable(); 0161 return lookupByQName(d->repositories.store, qualifiedName); 0162 } 0163 0164 PackageObject *ObjectStore::getPackage(const std::string& qualifiedName) const 0165 { 0166 assertReadable(); 0167 return lookupByQName(d->packages.store, qualifiedName); 0168 } 0169 0170 ErrorObject *ObjectStore::getError(const std::string& qualifiedName, 0171 const std::string& errorMessage, 0172 const std::string& fileName) const 0173 { 0174 assertReadable(); 0175 return lookupByQName(d->errors.store, ErrorObject::getStorageKey(qualifiedName, errorMessage, fileName)); 0176 } 0177 0178 NamespaceObject *ObjectStore::getNamespace(const std::string& qualifiedName) const 0179 { 0180 assertReadable(); 0181 return lookupByQName(d->namespaces.store, qualifiedName); 0182 } 0183 0184 VariableObject *ObjectStore::getVariable(const std::string& qualifiedName) const 0185 { 0186 assertReadable(); 0187 return lookupByQName(d->variables.store, qualifiedName); 0188 } 0189 0190 FunctionObject *ObjectStore::getFunction(const std::string& qualifiedName, 0191 const std::string& signature, 0192 const std::string& templateParameters, 0193 const std::string& returnType) const 0194 { 0195 assertReadable(); 0196 return lookupByQName(d->functions.store, 0197 FunctionObject::getStorageKey(qualifiedName, signature, templateParameters, returnType)); 0198 } 0199 0200 TypeObject *ObjectStore::getType(const std::string& qualifiedName) const 0201 { 0202 assertReadable(); 0203 return lookupByQName(d->types.store, qualifiedName); 0204 } 0205 0206 FieldObject *ObjectStore::getField(const std::string& qualifiedName) const 0207 { 0208 assertReadable(); 0209 return lookupByQName(d->fields.store, qualifiedName); 0210 } 0211 0212 MethodObject *ObjectStore::getMethod(const std::string& qualifiedName, 0213 const std::string& signature, 0214 const std::string& templateParameters, 0215 const std::string& returnType) const 0216 { 0217 assertReadable(); 0218 return lookupByQName(d->methods.store, 0219 MethodObject::getStorageKey(qualifiedName, signature, templateParameters, returnType)); 0220 } 0221 0222 std::vector<PackageObject *> ObjectStore::getAllPackages() const 0223 { 0224 assertReadable(); 0225 return getAll(d->packages.store); 0226 } 0227 0228 std::vector<FileObject *> ObjectStore::getAllFiles() const 0229 { 0230 assertReadable(); 0231 return getAll(d->files.store); 0232 } 0233 0234 FileObject *ObjectStore::getOrAddFile(const std::string& qualifiedName, 0235 std::string name, 0236 bool isHeader, 0237 std::string hash, 0238 PackageObject *package, 0239 ComponentObject *component) 0240 { 0241 assertWritable(); 0242 if (FileObject *ret = getFile(qualifiedName)) { 0243 return ret; 0244 } 0245 0246 return add( 0247 d->files.store, 0248 qualifiedName, 0249 std::make_unique<FileObject>(qualifiedName, std::move(name), isHeader, std::move(hash), package, component)); 0250 } 0251 0252 ComponentObject * 0253 ObjectStore::getOrAddComponent(const std::string& qualifiedName, std::string name, PackageObject *package) 0254 { 0255 assertWritable(); 0256 if (ComponentObject *ret = getComponent(qualifiedName)) { 0257 return ret; 0258 } 0259 0260 return add(d->components.store, 0261 qualifiedName, 0262 std::make_unique<ComponentObject>(qualifiedName, std::move(name), package)); 0263 } 0264 0265 RepositoryObject *ObjectStore::getOrAddRepository(const std::string& name, const std::string& diskPath) 0266 { 0267 assertWritable(); 0268 if (auto *ret = getRepository(name)) { 0269 return ret; 0270 } 0271 0272 return add(d->repositories.store, name, std::make_unique<RepositoryObject>(name, name, diskPath)); 0273 } 0274 0275 PackageObject *ObjectStore::getOrAddPackage(const std::string& qualifiedName, 0276 std::string name, 0277 std::string diskPath, 0278 PackageObject *parent, 0279 RepositoryObject *repository) 0280 { 0281 assertWritable(); 0282 if (PackageObject *ret = getPackage(qualifiedName)) { 0283 return ret; 0284 } 0285 0286 return add( 0287 d->packages.store, 0288 qualifiedName, 0289 std::make_unique<PackageObject>(qualifiedName, std::move(name), std::move(diskPath), parent, repository)); 0290 } 0291 0292 ErrorObject *ObjectStore::getOrAddError(lvtmdb::MdbUtil::ErrorKind errorKind, 0293 std::string qualifiedName, 0294 std::string errorMessage, 0295 std::string fileName) 0296 { 0297 assertWritable(); 0298 std::string key = ErrorObject::getStorageKey(qualifiedName, errorMessage, fileName); 0299 0300 if (ErrorObject *error = lookupByQName(d->errors.store, key)) { 0301 return error; 0302 } 0303 0304 return add(d->errors.store, 0305 std::move(key), 0306 std::make_unique<ErrorObject>(errorKind, 0307 std::move(qualifiedName), 0308 std::move(errorMessage), 0309 std::move(fileName))); 0310 } 0311 0312 NamespaceObject * 0313 ObjectStore::getOrAddNamespace(const std::string& qualifiedName, std::string name, NamespaceObject *parent) 0314 { 0315 assertWritable(); 0316 if (NamespaceObject *ret = getNamespace(qualifiedName)) { 0317 return ret; 0318 } 0319 0320 return add(d->namespaces.store, 0321 qualifiedName, 0322 std::make_unique<NamespaceObject>(qualifiedName, std::move(name), parent)); 0323 } 0324 0325 VariableObject *ObjectStore::getOrAddVariable( 0326 const std::string& qualifiedName, std::string name, std::string signature, bool isGlobal, NamespaceObject *parent) 0327 { 0328 assertWritable(); 0329 if (VariableObject *ret = getVariable(qualifiedName)) { 0330 return ret; 0331 } 0332 0333 return add( 0334 d->variables.store, 0335 qualifiedName, 0336 std::make_unique<VariableObject>(qualifiedName, std::move(name), std::move(signature), isGlobal, parent)); 0337 } 0338 0339 FunctionObject *ObjectStore::getOrAddFunction(std::string qualifiedName, 0340 std::string name, 0341 std::string signature, 0342 std::string returnType, 0343 std::string templateParameters, 0344 NamespaceObject *parent) 0345 { 0346 assertWritable(); 0347 std::string key = FunctionObject::getStorageKey(qualifiedName, signature, templateParameters, returnType); 0348 if (FunctionObject *fn = lookupByQName(d->functions.store, key)) { 0349 return fn; 0350 } 0351 0352 return add(d->functions.store, 0353 std::move(key), 0354 std::make_unique<FunctionObject>(std::move(qualifiedName), 0355 std::move(name), 0356 std::move(signature), 0357 std::move(returnType), 0358 std::move(templateParameters), 0359 parent)); 0360 } 0361 0362 TypeObject *ObjectStore::getOrAddType(const std::string& qualifiedName, 0363 std::string name, 0364 lvtshr::UDTKind kind, 0365 lvtshr::AccessSpecifier access, 0366 NamespaceObject *nmspc, 0367 PackageObject *pkg, 0368 TypeObject *parent) 0369 { 0370 assertWritable(); 0371 if (TypeObject *ret = getType(qualifiedName)) { 0372 return ret; 0373 } 0374 0375 return add(d->types.store, 0376 qualifiedName, 0377 std::make_unique<TypeObject>(qualifiedName, std::move(name), kind, access, nmspc, pkg, parent)); 0378 } 0379 0380 FieldObject *ObjectStore::getOrAddField(const std::string& qualifiedName, 0381 std::string name, 0382 std::string signature, 0383 lvtshr::AccessSpecifier access, 0384 bool isStatic, 0385 TypeObject *parent) 0386 { 0387 assertWritable(); 0388 if (FieldObject *ret = getField(qualifiedName)) { 0389 return ret; 0390 } 0391 0392 return add( 0393 d->fields.store, 0394 qualifiedName, 0395 std::make_unique<FieldObject>(qualifiedName, std::move(name), std::move(signature), access, isStatic, parent)); 0396 } 0397 0398 MethodObject *ObjectStore::getOrAddMethod(std::string qualifiedName, 0399 std::string name, 0400 std::string signature, 0401 std::string returnType, 0402 std::string templateParameters, 0403 lvtshr::AccessSpecifier access, 0404 bool isVirtual, 0405 bool isPure, 0406 bool isStatic, 0407 bool isConst, 0408 TypeObject *parent) 0409 { 0410 assertWritable(); 0411 std::string key = MethodObject::getStorageKey(qualifiedName, signature, templateParameters, returnType); 0412 if (MethodObject *method = lookupByQName(d->methods.store, key)) { 0413 return method; 0414 } 0415 0416 return add(d->methods.store, 0417 std::move(key), 0418 std::make_unique<MethodObject>(std::move(qualifiedName), 0419 std::move(name), 0420 std::move(signature), 0421 std::move(returnType), 0422 std::move(templateParameters), 0423 access, 0424 isVirtual, 0425 isPure, 0426 isStatic, 0427 isConst, 0428 parent)); 0429 } 0430 0431 void ObjectStore::removeMethod(MethodObject *method) 0432 { 0433 if (!method) { 0434 return; 0435 } 0436 0437 std::string key; 0438 method->withROLock([&] { 0439 key = method->storageKey(); 0440 }); 0441 d->methods.store.erase(key); 0442 } 0443 0444 void ObjectStore::removeField(FieldObject *field) 0445 { 0446 if (!field) { 0447 return; 0448 } 0449 0450 std::string key; 0451 field->withROLock([&] { 0452 key = field->qualifiedName(); 0453 }); 0454 d->fields.store.erase(key); 0455 } 0456 0457 void ObjectStore::removeType(TypeObject *type) 0458 { 0459 if (!type) { 0460 return; 0461 } 0462 0463 std::string key; 0464 type->withROLock([&] { 0465 NamespaceObject *parentNamespace = type->parentNamespace(); 0466 if (parentNamespace) { 0467 parentNamespace->withRWLock([&] { 0468 parentNamespace->removeType(type); 0469 }); 0470 } 0471 0472 PackageObject *parentPkg = type->package(); 0473 if (parentPkg) { 0474 parentPkg->withRWLock([&] { 0475 parentPkg->removeType(type); 0476 }); 0477 } 0478 0479 // Type will be locked when it recurses here. 0480 for (TypeObject *children : type->children()) { 0481 removeType(children); 0482 } 0483 0484 for (MethodObject *method : type->methods()) { 0485 removeMethod(method); 0486 } 0487 0488 for (FieldObject *field : type->fields()) { 0489 removeField(field); 0490 } 0491 0492 key = type->qualifiedName(); 0493 }); 0494 0495 d->types.store.erase(key); 0496 } 0497 0498 void ObjectStore::removeFunction(FunctionObject *func) 0499 { 0500 if (!func) { 0501 return; 0502 } 0503 0504 NamespaceObject *parent = nullptr; 0505 std::string key; 0506 0507 func->withROLock([&] { 0508 parent = func->parent(); 0509 key = func->storageKey(); 0510 }); 0511 0512 if (parent) { 0513 parent->removeFunction(func); 0514 } 0515 0516 d->functions.store.erase(key); 0517 } 0518 0519 void ObjectStore::removeVariable(VariableObject *var) 0520 { 0521 if (!var) { 0522 return; 0523 } 0524 0525 NamespaceObject *parent = nullptr; 0526 std::string key; 0527 0528 var->withROLock([&] { 0529 parent = var->parent(); 0530 key = var->qualifiedName(); 0531 }); 0532 0533 if (parent) { 0534 parent->removeVariable(var); 0535 } 0536 0537 d->variables.store.erase(key); 0538 } 0539 0540 void ObjectStore::removeNamespace(NamespaceObject *nmspc) 0541 { 0542 if (!nmspc) { 0543 return; 0544 } 0545 0546 NamespaceObject *parent = nullptr; 0547 std::string key; 0548 nmspc->withROLock([&] { 0549 parent = nmspc->parent(); 0550 key = nmspc->qualifiedName(); 0551 0552 for (auto *udt : nmspc->typeChildren()) { 0553 removeType(udt); 0554 } 0555 0556 for (auto *func : nmspc->functions()) { 0557 removeFunction(func); 0558 } 0559 0560 for (auto *var : nmspc->variables()) { 0561 removeVariable(var); 0562 } 0563 }); 0564 0565 // no lock here as the recursion will lock it. 0566 if (parent) { 0567 parent->removeChild(nmspc); 0568 } 0569 0570 d->namespaces.store.erase(key); 0571 } 0572 0573 void ObjectStore::removeComponent(ComponentObject *comp) 0574 { 0575 if (!comp) { 0576 return; 0577 } 0578 0579 PackageObject *pkg = nullptr; 0580 std::string key; 0581 comp->withROLock([&] { 0582 pkg = comp->package(); 0583 key = comp->qualifiedName(); 0584 }); 0585 0586 if (pkg) { 0587 bool empty = false; 0588 pkg->withRWLock([&] { 0589 pkg->removeComponent(comp); 0590 empty = pkg->components().empty(); 0591 }); 0592 0593 // TODO: Aparently removing all components from a 0594 // package still keeps the package alive, and removing it 0595 // is considered to be a bug - We need to check if that's 0596 // indeed the case.' 0597 // 0598 // See ct_lvtclp_fileupdatemrg_physical.t.cpp 0599 // 0600 // if (empty) { 0601 // removePackage(pkg); 0602 //} 0603 } 0604 0605 // for (auto *relation : comp->forwardDependencies()) { 0606 // // TODO: 0607 // } 0608 0609 d->components.store.erase(key); 0610 } 0611 0612 void ObjectStore::removePackage(PackageObject *pkg, std::set<intptr_t>& removed) 0613 { 0614 if (!pkg) { 0615 return; 0616 } 0617 0618 if (removed.count(reinterpret_cast<intptr_t>(pkg)) != 0) { 0619 return; 0620 } 0621 0622 removed.insert(reinterpret_cast<intptr_t>(pkg)); 0623 0624 std::string key; 0625 PackageObject *parent = nullptr; 0626 pkg->withROLock([&] { 0627 parent = pkg->parent(); 0628 key = pkg->qualifiedName(); 0629 }); 0630 0631 if (parent) { 0632 bool empty = false; 0633 parent->withRWLock([&] { 0634 parent->removeChild(pkg); 0635 empty = parent->children().empty(); 0636 }); 0637 0638 if (empty) { 0639 removePackage(parent, removed); 0640 } 0641 } 0642 0643 d->packages.store.erase(key); 0644 } 0645 0646 QList<std::string> ObjectStore::removeFile(FileObject *file, std::set<intptr_t>& removed) 0647 { 0648 if (!file) { 0649 return {}; 0650 } 0651 if (removed.count(reinterpret_cast<intptr_t>(file)) != 0) { 0652 return {}; 0653 } 0654 0655 removed.insert(reinterpret_cast<intptr_t>(file)); 0656 0657 std::string key; 0658 std::vector<NamespaceObject *> namespaces; 0659 std::vector<TypeObject *> types; 0660 std::vector<FileObject *> files; 0661 ComponentObject *component = nullptr; 0662 0663 file->withROLock([&] { 0664 key = file->qualifiedName(); 0665 namespaces = file->namespaces(); 0666 types = file->types(); 0667 component = file->component(); 0668 files = file->reverseIncludes(); 0669 }); 0670 0671 QList<std::string> res; 0672 0673 for (auto *nmspc : namespaces) { 0674 bool empty = false; 0675 nmspc->withRWLock([&] { 0676 nmspc->removeFile(file); 0677 empty = nmspc->files().empty(); 0678 }); 0679 0680 if (empty) { 0681 removeNamespace(nmspc); 0682 } 0683 } 0684 0685 for (auto *type : types) { 0686 bool empty = false; 0687 type->withRWLock([&] { 0688 type->removeFile(file); 0689 empty = type->files().empty(); 0690 }); 0691 0692 if (empty) { 0693 removeType(type); 0694 } 0695 } 0696 0697 // We need to remove all the files that uses this file as include 0698 // from the object store, to force a re-parse. this has nothing to do 0699 // with a file that's removed from disk, but as a way to force a parse. 0700 for (auto& includeRelationship : files) { 0701 assert(includeRelationship != file); 0702 res += removeFile(includeRelationship, removed); 0703 } 0704 0705 if (component) { 0706 bool empty = false; 0707 std::string comp_key; 0708 component->withRWLock([&] { 0709 component->removeFile(file); 0710 empty = component->files().empty(); 0711 comp_key = component->qualifiedName(); 0712 }); 0713 0714 if (empty) { 0715 removeComponent(component); 0716 } 0717 } 0718 0719 d->files.store.erase(key); 0720 res.push_back(key); 0721 return res; 0722 } 0723 0724 std::unordered_map<std::string, std::unique_ptr<ComponentObject>>& ObjectStore::components() const 0725 { 0726 return d->components.store; 0727 } 0728 0729 std::unordered_map<std::string, std::unique_ptr<ErrorObject>>& ObjectStore::errors() const 0730 { 0731 return d->errors.store; 0732 } 0733 0734 std::unordered_map<std::string, std::unique_ptr<FieldObject>>& ObjectStore::fields() const 0735 { 0736 return d->fields.store; 0737 } 0738 0739 std::unordered_map<std::string, std::unique_ptr<FileObject>>& ObjectStore::files() const 0740 { 0741 return d->files.store; 0742 } 0743 0744 std::unordered_map<std::string, std::unique_ptr<FunctionObject>>& ObjectStore::functions() const 0745 { 0746 return d->functions.store; 0747 } 0748 0749 std::unordered_map<std::string, std::unique_ptr<MethodObject>>& ObjectStore::methods() const 0750 { 0751 return d->methods.store; 0752 } 0753 0754 std::unordered_map<std::string, std::unique_ptr<NamespaceObject>>& ObjectStore::namespaces() const 0755 { 0756 return d->namespaces.store; 0757 } 0758 0759 std::unordered_map<std::string, std::unique_ptr<RepositoryObject>>& ObjectStore::repositories() const 0760 { 0761 return d->repositories.store; 0762 } 0763 0764 std::unordered_map<std::string, std::unique_ptr<PackageObject>>& ObjectStore::packages() const 0765 { 0766 return d->packages.store; 0767 } 0768 0769 std::unordered_map<std::string, std::unique_ptr<TypeObject>>& ObjectStore::types() const 0770 { 0771 return d->types.store; 0772 } 0773 0774 std::unordered_map<std::string, std::unique_ptr<VariableObject>>& ObjectStore::variables() const 0775 { 0776 return d->variables.store; 0777 } 0778 0779 } // namespace Codethink::lvtmdb