Files
Giacomo Vercesi 40da49548b Add Binaries list to the model
Add to the model an entry detailing the list of input binaries under
`Binaries`. This will be referenced by `Segments` when needed.
2025-10-16 17:48:45 +02:00

319 lines
8.8 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <array>
#include <memory>
#include <optional>
#include <set>
#include <type_traits>
#include <variant>
#include <vector>
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include "revng/ADT/Concepts.h"
#include "revng/ADT/KeyedObjectContainer.h"
#include "revng/ADT/UpcastablePointer.h"
#include "revng/Support/Assert.h"
#include "revng/Support/Debug.h"
#include "revng/Support/YAMLTraits.h"
#include "revng/TupleTree/Tracking.h"
#include "revng/TupleTree/TupleTreeCompatible.h"
#include "revng/TupleTree/TupleTreePath.h"
#include "revng/TupleTree/TupleTreeReference.h"
#include "revng/TupleTree/Visits.h"
template<typename T>
struct DisableTracking {
const T *TrackedObject = nullptr;
public:
DisableTracking(const T &TrackedObject) : TrackedObject(&TrackedObject) {
// Since the model classes may have been generated either with or without
// tracking, DisableTracking should do nothing if the concept returns false.
if constexpr (T::HasTracking)
revng::Tracking::push(*this->TrackedObject);
}
DisableTracking(const DisableTracking &Other) = delete;
DisableTracking &operator=(const DisableTracking &Other) = delete;
DisableTracking(DisableTracking &&Other) {
TrackedObject = Other.TrackedObject;
Other.TrackedObject = nullptr;
}
DisableTracking &operator=(DisableTracking &&Other) {
if (this == &Other) {
return *this;
}
onDestruction();
TrackedObject = Other.TrackedObject;
Other.TrackedObject = nullptr;
return *this;
}
~DisableTracking() { onDestruction(); }
private:
void onDestruction() {
if constexpr (T::HasTracking) {
if (TrackedObject != nullptr) {
revng::Tracking::pop(*TrackedObject);
}
}
TrackedObject = nullptr;
}
};
template<TupleTreeCompatible T>
class TupleTree {
private:
std::unique_ptr<T> Root;
bool AllReferencesAreCached = false;
public:
TupleTree() : Root(new T), AllReferencesAreCached(false) {}
// Allow expensive copy
TupleTree(const TupleTree &Other) : Root(std::make_unique<T>()) {
*this = Other;
}
TupleTree &operator=(const TupleTree &Other) {
if (Other.get() == nullptr) {
Root = nullptr;
AllReferencesAreCached = false;
return *this;
}
if (this != &Other) {
*Root = *Other.Root;
AllReferencesAreCached = false;
initializeUncachedReferences();
}
return *this;
}
// Moving is fine
TupleTree(TupleTree &&Other) { *this = std::move(Other); }
TupleTree &operator=(TupleTree &&Other) {
if (Other.get() == nullptr) {
Root = nullptr;
AllReferencesAreCached = false;
Other.Root.reset();
Other.AllReferencesAreCached = false;
return *this;
}
if (this != &Other) {
Root = std::move(Other.Root);
AllReferencesAreCached = Other.AllReferencesAreCached;
Other.Root.reset();
Other.AllReferencesAreCached = false;
}
return *this;
}
template<StrictSpecializationOf<TupleTreeReference> TTR>
void replaceReferences(const std::map<TTR, TTR> &Map) {
auto Visitor = [&Map]<typename TTRA>(TTRA &Reference) {
// Here TTRA can be any TupleTreeReference<X, Binary>, actually check
// that it is of the type we want to replace
if constexpr (std::is_same_v<TTRA, TTR>) {
auto It = Map.find(Reference);
if (It != Map.end())
Reference = It->second;
}
};
visitReferences(Visitor);
evictCachedReferences();
}
template<StrictSpecializationOf<TupleTreeReference> TTR,
std::predicate<const TTR &> PredicateType>
void replaceReferencesIf(const TTR &NewReference, PredicateType &&Predicate) {
auto Visitor = [&Predicate, &NewReference]<typename TTRA>(TTRA &Reference) {
if constexpr (std::is_same_v<TTR, TTRA>) {
if (Predicate(Reference))
Reference = NewReference;
}
};
visitReferences(Visitor);
evictCachedReferences();
}
public:
static llvm::Expected<TupleTree> fromString(llvm::StringRef YAMLString) {
TupleTree Result{};
auto MaybeRoot = revng::detail::fromStringImpl<T>(YAMLString);
if (not MaybeRoot)
return MaybeRoot.takeError();
*Result.Root = std::move(*MaybeRoot);
// Update references to root
Result.initializeReferences();
return Result;
}
static llvm::Expected<TupleTree>
fromFileOrSTDIN(const llvm::StringRef &Path) {
auto MaybeBuffer = llvm::MemoryBuffer::getFileOrSTDIN(Path);
if (not MaybeBuffer)
return llvm::errorCodeToError(MaybeBuffer.getError());
return fromString((*MaybeBuffer)->getBuffer());
}
static llvm::Expected<TupleTree> fromFile(const llvm::StringRef &Path) {
auto MaybeBuffer = llvm::MemoryBuffer::getFile(Path);
if (not MaybeBuffer)
return llvm::errorCodeToError(MaybeBuffer.getError());
return fromString((*MaybeBuffer)->getBuffer());
}
llvm::Error toFile(const llvm::StringRef &Path) const {
return ::serializeToFile(*Root, Path);
}
public:
template<typename S>
void serialize(S &Stream) const {
revng_assert(Root);
::serialize(Stream, *Root);
}
void serialize(std::string &Buffer) const {
llvm::raw_string_ostream Stream(Buffer);
serialize(Stream);
}
public:
const T *get() const noexcept { return Root.get(); }
T *get() noexcept {
revng_assert(not AllReferencesAreCached);
return Root.get();
}
const T &operator*() const { return *Root; }
T &operator*() {
revng_assert(not AllReferencesAreCached);
return *Root;
}
const T *operator->() const noexcept { return Root.operator->(); }
T *operator->() noexcept {
revng_assert(not AllReferencesAreCached);
return Root.operator->();
}
public:
bool verify() const debug_function { return verifyReferences(false); }
void assertValid() const { verifyReferences(true); }
private:
void initializeUncachedReferences() {
DisableTracking Guard(*Root);
visitReferences([this](auto &Element) {
Element.setRoot(Root.get());
Element.evictCachedTarget();
});
AllReferencesAreCached = false;
}
public:
void initializeReferences() {
DisableTracking Guard(*Root);
revng_assert(not AllReferencesAreCached);
visitReferences([this](auto &Element) { Element.setRoot(Root.get()); });
}
void cacheReferences() {
DisableTracking Guard(*Root);
if (not AllReferencesAreCached)
visitReferencesInternal([](auto &Element) { Element.cacheTarget(); });
AllReferencesAreCached = true;
}
void evictCachedReferences() {
DisableTracking Guard(*Root);
if (AllReferencesAreCached)
visitReferencesInternal([](auto &E) { E.evictCachedTarget(); });
AllReferencesAreCached = false;
}
template<typename Pre, typename Post>
void visit(Pre PreCallable, Post PostCallable) const {
using PreVisitor = typename TupleTreeVisitor<T>::template ConstVisitor<Pre>;
PreVisitor PreInstance(PreCallable);
using PostVisitor = typename TupleTreeVisitor<T>::template ConstVisitor<
Post>;
PostVisitor PostInstance(PostCallable);
visitImpl(PreInstance, PostInstance);
}
template<typename Pre, typename Post>
void visit(Pre PreCallable, Post PostCallable) {
using PreVisitor = typename TupleTreeVisitor<T>::template Visitor<Pre>;
PreVisitor PreInstance(PreCallable);
using PostVisitor = typename TupleTreeVisitor<T>::template Visitor<Post>;
PostVisitor PostInstance(PostCallable);
visitImpl(PreInstance, PostInstance);
}
private:
void visitImpl(typename TupleTreeVisitor<T>::ConstVisitorBase &Pre,
typename TupleTreeVisitor<T>::ConstVisitorBase &Post) const;
void visitImpl(typename TupleTreeVisitor<T>::VisitorBase &Pre,
typename TupleTreeVisitor<T>::VisitorBase &Post);
template<typename L>
void visitReferencesInternal(L &&InnerVisitor) {
auto Visitor = [&InnerVisitor](auto &Element) {
using type = std::remove_cvref_t<decltype(Element)>;
if constexpr (StrictSpecializationOf<type, TupleTreeReference>)
std::invoke(std::forward<L>(InnerVisitor), Element);
};
visit(Visitor, [](auto &) {});
}
public:
template<typename L>
void visitReferences(L &&InnerVisitor) {
revng_assert(not AllReferencesAreCached);
visitReferencesInternal(std::forward<L>(InnerVisitor));
}
template<typename L>
void visitReferences(L &&InnerVisitor) const {
auto Visitor = [&InnerVisitor](const auto &Element) {
using type = std::remove_cvref_t<decltype(Element)>;
if constexpr (StrictSpecializationOf<type, TupleTreeReference>)
std::invoke(std::forward<L>(InnerVisitor), Element);
};
visit(Visitor, [](auto) {});
}
private:
bool verifyReferences(bool Assert) const;
};