#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ManagedStatic.h" #include "revng/Pipeline/ContainerSet.h" #include "revng/Pipeline/Context.h" #include "revng/Pipeline/Target.h" namespace pipeline { /// We sometime wish to allow a downstream library to extend what Kinds can be /// contained in a container. An example is a LLVMContainer, it contains a /// module which is composed of global objects, a downstream library may wish to /// add a particular meaning of a global object. If that is the case then /// ContainerEnumerator and EnumerableContainers are what you need. /// /// If a downstream user of revng wishes to extend the enumeration and removal /// rule from a container it can do so by extending this class, specialized with /// the type of the container in question. /// /// The enumerate and removeTarget methods must be implemented with the /// appropriate rules, that is, enumerate must return Targets with the same kind /// as the one pointed by K. template class ContainerEnumerator { private: Kind *K = nullptr; public: ContainerEnumerator(Kind &K) : K(&K) { Container::getRegisteredInspectors().push_back(this); } public: Kind &getKind() const { return *K; } public: virtual ~ContainerEnumerator() = default; virtual TargetsList enumerate(const Container &ToInspect) const = 0; /// \return must return true if it was possible to remove the provided target virtual bool remove(const TargetsList &Targets, Container &ToInspect) const = 0; }; /// An inspectable container is a container that delegates the enumeration and /// removal of its contents to inspectors, which can be dynamically registered /// at runtime. /// /// This facility is needed to be able to decouple the Kind declaration from the /// containers declaration, since if it was always containers to name which /// kinds can be contained in them, then they would not be able to name kinds /// declared downstream libraries. Similarly, should be always kind to be able /// to enumerate the context of a container, then any containers declared in /// downstream libraries would not be able to use kinds declared upstream. /// /// You should declare a new inspectable container if and only if you think that /// downstream user may wish to extend how the enumeration of this container /// happens. /// Otherwise simply declare a new Container Type, the difference is that a /// normal container must provide a enumeration method of its own rather than /// leaving it up to inspectors. template class EnumerableContainer : public Container { friend class ContainerEnumerator; private: using StaticContainer = llvm::SmallVector *, 4>; static StaticContainer &getRegisteredInspectors() { static StaticContainer List; return List; } protected: Context *TheContext = nullptr; public: EnumerableContainer(Context &Context, llvm::StringRef Name) : Container(Name), TheContext(&Context) {} EnumerableContainer(Context &Context, llvm::StringRef Name, const char *ID) : Container(Name, ID), TheContext(&Context) {} public: const Context &getContext() const { return *TheContext; } Context &getContext() { return *TheContext; } bool contains(const Target &Target) const { return enumerate().contains(Target); } public: ~EnumerableContainer() override = default; TargetsList enumerate() const override { TargetsList ToReturn; for (const auto *Inspector : getRegisteredInspectors()) ToReturn.merge(Inspector->enumerate(*this->self())); return ToReturn; } /// \return true if all targets to remove have been removed bool removeImpl(const TargetsList &Targets) override { bool RemovedAll = true; for (const auto *Inspector : getRegisteredInspectors()) { RemovedAll = Inspector->remove(Targets.filter(Inspector->getKind()), *this->self()) and RemovedAll; } return RemovedAll; } static std::vector possibleKinds() { std::vector ToReturn; for (auto *Inspector : getRegisteredInspectors()) { ToReturn.push_back(&Inspector->getKind()); } return ToReturn; } }; template class KindForContainer : public Kind, public ContainerEnumerator { public: template KindForContainer(llvm::StringRef Name, const BaseRank &Rank) : Kind(Name, Rank, {}, {}), ContainerEnumerator(*static_cast(this)) {} template KindForContainer(llvm::StringRef Name, Kind &Parent, const BaseRank &Rank) : Kind(Name, Parent, Rank, {}, {}), ContainerEnumerator(*static_cast(this)) {} public: ~KindForContainer() override = default; }; } // namespace pipeline