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