Files
revng-revng/include/revng/Pipeline/ContainerEnumerator.h
Alessandro Di Federico e2d9c50cf5 tmp
2022-11-15 16:46:41 +01:00

153 lines
5.0 KiB
C++

#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<typename Container>
class ContainerEnumerator {
private:
Kind *K;
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 Context &Ctx, const Container &ToInspect) const = 0;
/// \return must return true if it was possible to remove the provided target
virtual bool remove(const Context &Ctx,
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<typename Derived>
class EnumerableContainer : public Container<Derived> {
friend class ContainerEnumerator<Derived>;
private:
using StaticContainer = llvm::SmallVector<ContainerEnumerator<Derived> *, 4>;
static StaticContainer &getRegisteredInspectors() {
static StaticContainer List;
return List;
}
protected:
Context *Ctx;
public:
EnumerableContainer(Context &Ctx, llvm::StringRef Name) :
Container<Derived>(Name), Ctx(&Ctx) {}
EnumerableContainer(Context &Ctx, llvm::StringRef Name, const char *ID) :
Container<Derived>(Name, ID), Ctx(&Ctx) {}
public:
const Context &getContext() const { return *Ctx; }
Context &getContext() { return *Ctx; }
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(*Ctx, *this->self()));
return ToReturn;
}
/// \return true if all targets to remove have been removed
bool remove(const TargetsList &Targets) override {
bool RemovedAll = true;
for (const auto *Inspector : getRegisteredInspectors())
RemovedAll = Inspector->remove(*Ctx,
Targets.filter(Inspector->getKind()),
*this->self())
and RemovedAll;
return RemovedAll;
}
static std::vector<Kind *> possibleKinds() {
std::vector<Kind *> ToReturn;
for (auto *Inspector : getRegisteredInspectors()) {
ToReturn.push_back(&Inspector->getKind());
}
return ToReturn;
}
};
template<typename Container>
class KindForContainer : public Kind, public ContainerEnumerator<Container> {
public:
template<RankSpecialization BaseRank>
KindForContainer(llvm::StringRef Name, llvm::StringRef Doc, const BaseRank &Rank) :
Kind(Name, Doc, Rank, {}, {}),
ContainerEnumerator<Container>(*static_cast<Kind *>(this)) {}
template<RankSpecialization BaseRank>
KindForContainer(llvm::StringRef Name, llvm::StringRef Doc, Kind &Parent, const BaseRank &Rank) :
Kind(Name, Doc, Parent, Rank, {}, {}),
ContainerEnumerator<Container>(*static_cast<Kind *>(this)) {}
public:
~KindForContainer() override = default;
};
} // namespace pipeline