#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_ostream.h" #include "revng/ADT/Concepts.h" #include "revng/ADT/STLExtras.h" #include "revng/Pipeline/Target.h" #include "revng/Storage/Path.h" #include "revng/Support/Assert.h" #include "revng/Support/YAMLTraits.h" namespace pipeline { template concept HasID = requires { { T::ID } -> convertible_to; }; class ContainerTypeInfoBase { public: virtual llvm::StringRef getMIMEType() const = 0; virtual const char *getID() const = 0; virtual ~ContainerTypeInfoBase() = default; virtual std::vector getPossibleKinds() const = 0; }; template class ContainerTypeInfo : public ContainerTypeInfoBase { public: llvm::StringRef getMIMEType() const override { return T::MIMEType; } const char *getID() const override { return &T::ID; } ~ContainerTypeInfo() override = default; std::vector getPossibleKinds() const override { return T::possibleKinds(); } }; class ContainerBase { private: template friend class Container; const char *ID; std::string Name; /// Boolean indicating if the container has mutated from the last time it has /// been serialized to permanent storage. Note that this class automatically /// sets this to `true` when executing any mutating operation (e.g. /// `mergeBack`, `remove`) but *does not* reset it when running e.g. /// serialize. Downstream users must signal that it is so by calling /// resetDirtyness. bool Dirty = true; using RegistryType = std::vector>; static RegistryType &getTypeRegistryImpl() { static std::vector> V; return V; } public: static const RegistryType &getTypeRegistry() { return getTypeRegistryImpl(); } virtual std::vector getPossibleKinds() const = 0; public: ContainerBase(llvm::StringRef Name, char const *ID) : ID(ID), Name(Name.str()) {} public: static bool classof(const ContainerBase *) { return true; } public: const char *getTypeID() const { return ID; } const std::string &name() const { return Name; } public: virtual ~ContainerBase() = default; virtual llvm::StringRef mimeType() const = 0; /// The implementation of cloneFiltered must return a copy of the current /// container and a invocation of enumerate on such container must be /// exactly equal to Targets virtual std::unique_ptr cloneFiltered(const TargetsList &Targets) const = 0; /// The implementation of this method must ensure that after the execution /// this->enumerate() == before(Other).enumerate().merge(this->enumerate()) /// /// In other words the whole content of Other must be transferred to this. virtual void mergeBackBaseImpl(ContainerBase &&Other) = 0; void mergeBack(ContainerBase &&Other) { Dirty = true; mergeBackBaseImpl(std::move(Other)); } /// This method is the method used the pipeline system to understand which /// targets are currently available inside the current container. virtual TargetsList enumerate() const = 0; /// The implementation must ensure that /// not after(this)->enumerate().contains(Targets); /// /// returns false if nothing was removed virtual bool removeImpl(const TargetsList &Targets) = 0; bool remove(const TargetsList &Targets) { Dirty = true; return removeImpl(Targets); } /// The implementation for a Type T that extends ContainerBase must ensure /// that a new instance of T, on which deserialize has been invoked with the /// serialized content of a old instance, must be equal to the old instance. virtual llvm::Error serialize(llvm::raw_ostream &OS) const = 0; /// same as serialize virtual llvm::Error deserializeImpl(const llvm::MemoryBuffer &Buffer) = 0; llvm::Error deserialize(const llvm::MemoryBuffer &Buffer) { Dirty = true; return deserializeImpl(Buffer); } /// Must reset the state of the container to the just built state virtual void clearImpl() = 0; void clear() { Dirty = true; clearImpl(); } /// The implementation must ensure that there exists a file at the provided /// path that contains the serialized version of this object. virtual llvm::Error store(const revng::FilePath &Path) const; /// The implementation must ensure that the content of this file will be /// loaded from the provided path. virtual llvm::Error loadImpl(const revng::FilePath &Path); llvm::Error load(const revng::FilePath &Path) { Dirty = true; return loadImpl(Path); } /// Checks that the content of the this container is valid. virtual llvm::Error verify() const { return enumerate().verify(*this); } /// Return the serialized content of the specified non * target virtual llvm::Error extractOne(llvm::raw_ostream &OS, const Target &Target) const = 0; public: bool isDirty() const { return Dirty; } void resetDirtiness() { Dirty = false; } public: void dump() const debug_function { cantFail(serialize(llvm::dbgs())); } void dumpToDisk(const char *Path) const debug_function { std::error_code EC; llvm::raw_fd_stream Output(Path, EC); cantFail(serialize(Output)); } }; /// CRTP class to be extended to implement a pipeline container. /// /// The methods that must be implemented are those shown in ContainerBase. template class Container : public ContainerBase { private: static int registerType() { auto &Registry = ContainerBase::getTypeRegistryImpl(); revng_assert(llvm::find(Registry, &Derived::ID) == Registry.end(), "cannot register same container type twice"); Registry.push_back(std::make_unique>()); return 0; } inline static const int ForceRegister = registerType(); public: /// This is a template to force the evaluation of the concept from the class /// definition to this class objects instantiation, otherwise the derived /// type would not be fully defined yet and the constraints would fail. template Container(llvm::StringRef Name, const char *ID = &Derived::ID) : ContainerBase(Name, ID) {} ~Container() override = default; public: static bool classof(const ContainerBase *Base) { return Base->getTypeID() == &Derived::ID; } llvm::StringRef mimeType() const override { return Derived::MIMEType; } static std::vector getWrittenFiles(const revng::FilePath &Path) { return { Path }; } public: void mergeBackBaseImpl(ContainerBase &&Container) final { mergeBackImpl(std::move(llvm::cast(Container))); } public: Derived *self() { return static_cast(this); } const Derived *self() const { return static_cast(this); } protected: virtual void mergeBackImpl(Derived &&Container) = 0; std::vector getPossibleKinds() const final { return Derived::possibleKinds(); } }; } // namespace pipeline