mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
238 lines
6.8 KiB
C++
238 lines
6.8 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <compare>
|
|
#include <optional>
|
|
#include <type_traits>
|
|
#include <variant>
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "revng/ADT/Concepts.h"
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/Support/YAMLTraits.h"
|
|
#include "revng/TupleTree/TupleTreePath.h"
|
|
#include "revng/TupleTree/Visits.h"
|
|
|
|
template<typename T, typename RootType>
|
|
class TupleTreeReference {
|
|
public:
|
|
using RootT = RootType;
|
|
using RootVariant = std::variant<RootT *, const RootT *>;
|
|
using TargetVariant = std::variant<T *, const T *>;
|
|
|
|
private:
|
|
RootVariant Root = static_cast<RootT *>(nullptr);
|
|
TupleTreePath Path;
|
|
TargetVariant CachedTarget = static_cast<T *>(nullptr);
|
|
|
|
public:
|
|
TupleTreeReference() = default;
|
|
TupleTreeReference(ConstOrNot<RootT> auto *R, const TupleTreePath &P) :
|
|
Root{ RootVariant{ R } },
|
|
Path{ P },
|
|
CachedTarget{ static_cast<T *>(nullptr) } {}
|
|
|
|
TupleTreeReference(const TupleTreeReference &) = default;
|
|
TupleTreeReference &operator=(const TupleTreeReference &) = default;
|
|
TupleTreeReference(TupleTreeReference &) = default;
|
|
TupleTreeReference &operator=(TupleTreeReference &) = default;
|
|
|
|
public:
|
|
const RootT *getRoot() const {
|
|
const auto GetPtrToConstRoot =
|
|
[](const auto &RootPointer) -> const RootT * { return RootPointer; };
|
|
return std::visit(GetPtrToConstRoot, Root);
|
|
}
|
|
|
|
void setRoot(ConstOrNot<RootT> auto *NewRoot) {
|
|
evictCachedTarget();
|
|
Root = NewRoot;
|
|
}
|
|
|
|
private:
|
|
// Friend class that is allowed to manage the cached pointer to the target
|
|
template<TupleTreeCompatible>
|
|
friend class TupleTree;
|
|
|
|
T *getCached() {
|
|
if (std::holds_alternative<RootT *>(Root)) {
|
|
revng_assert(std::holds_alternative<T *>(CachedTarget));
|
|
return std::get<T *>(CachedTarget);
|
|
} else if (std::holds_alternative<const RootT *>(Root)) {
|
|
revng_abort("Called getCached() with const Root, use getCachedConst!");
|
|
} else {
|
|
revng_abort("Invalid root variant!");
|
|
}
|
|
}
|
|
|
|
const T *getCachedConst() const {
|
|
const auto GetConstPtrVisitor = [](const auto &Cached) -> const T * {
|
|
return Cached;
|
|
};
|
|
return std::visit(GetConstPtrVisitor, CachedTarget);
|
|
}
|
|
|
|
bool isCached() const { return getCachedConst() != nullptr; }
|
|
|
|
bool cacheTarget() {
|
|
if (isValid()) {
|
|
if (std::holds_alternative<const RootT *>(Root)) {
|
|
CachedTarget = getConst();
|
|
} else if (std::holds_alternative<RootT *>(Root)) {
|
|
CachedTarget = get();
|
|
} else {
|
|
revng_abort("Invalid root variant!");
|
|
}
|
|
}
|
|
return isCached();
|
|
}
|
|
|
|
void evictCachedTarget() { CachedTarget = static_cast<T *>(nullptr); }
|
|
|
|
public:
|
|
static TupleTreeReference
|
|
fromString(ConstOrNot<TupleTreeReference::RootT> auto *Root,
|
|
llvm::StringRef Path) {
|
|
std::optional<TupleTreePath> OptionalPath = stringAsPath<RootT>(Path);
|
|
if (not OptionalPath.has_value())
|
|
return TupleTreeReference{};
|
|
return TupleTreeReference{ Root, *OptionalPath };
|
|
}
|
|
|
|
bool operator==(const TupleTreeReference &Other) const {
|
|
// The paths are the same even if they are referred to different roots
|
|
if (isCached() and Other.isCached())
|
|
return getCachedConst() == Other.getCachedConst();
|
|
return Path == Other.Path;
|
|
}
|
|
|
|
std::strong_ordering operator<=>(const TupleTreeReference &Other) const {
|
|
// The paths are the same even if they are referred to different roots
|
|
if (isCached() and Other.isCached())
|
|
return getCachedConst() <=> Other.getCachedConst();
|
|
return Path <=> Other.Path;
|
|
}
|
|
|
|
public:
|
|
std::string toString() const { return *pathAsString<RootT>(Path); }
|
|
|
|
void dump() const debug_function { dbg << toString() << "\n"; }
|
|
|
|
const TupleTreePath &path() const { return Path; }
|
|
|
|
private:
|
|
bool hasNullRoot() const {
|
|
const auto IsNullVisitor = [](const auto &Pointer) {
|
|
return Pointer == nullptr;
|
|
};
|
|
return std::visit(IsNullVisitor, Root);
|
|
}
|
|
|
|
bool canGet() const {
|
|
return Root.index() != std::variant_npos and not hasNullRoot();
|
|
}
|
|
|
|
public:
|
|
const T *get() const {
|
|
revng_assert(canGet());
|
|
|
|
if (isCached())
|
|
return getCachedConst();
|
|
|
|
if (Path.size() == 0)
|
|
return nullptr;
|
|
|
|
const auto GetByPathVisitor = [&Path = Path](const auto &RootPointer) {
|
|
return static_cast<const T *>(getByPath<T>(Path, *RootPointer));
|
|
};
|
|
|
|
return std::visit(GetByPathVisitor, Root);
|
|
}
|
|
|
|
T *get() {
|
|
revng_assert(canGet());
|
|
|
|
if (isCached())
|
|
return getCached();
|
|
|
|
if (Path.size() == 0)
|
|
return nullptr;
|
|
|
|
if (std::holds_alternative<RootT *>(Root)) {
|
|
return getByPath<T>(Path, *std::get<RootT *>(Root));
|
|
} else if (std::holds_alternative<const RootT *>(Root)) {
|
|
revng_abort("Called get() with const root, use getConst!");
|
|
} else {
|
|
revng_abort("Invalid root variant!");
|
|
}
|
|
}
|
|
|
|
const T *getConst() const {
|
|
revng_assert(canGet());
|
|
|
|
if (isCached())
|
|
return getCachedConst();
|
|
|
|
if (Path.size() == 0)
|
|
return nullptr;
|
|
|
|
if (std::holds_alternative<const RootT *>(Root)) {
|
|
return getByPath<T>(Path, *std::get<const RootT *>(Root));
|
|
} else if (std::holds_alternative<RootT *>(Root)) {
|
|
return getByPath<T>(Path, *std::get<RootT *>(Root));
|
|
} else {
|
|
revng_abort("Invalid root variant!");
|
|
}
|
|
}
|
|
|
|
bool isConst() const { return std::holds_alternative<const RootT *>(Root); }
|
|
|
|
bool empty() const debug_function { return Path.empty(); }
|
|
|
|
bool isValid() const debug_function {
|
|
if (not canGet() or Path.empty())
|
|
return false;
|
|
const T *TargetPointer = getConst();
|
|
const T *CachedPointer = getCachedConst();
|
|
if (not CachedPointer)
|
|
return TargetPointer;
|
|
return TargetPointer and TargetPointer == CachedPointer;
|
|
}
|
|
};
|
|
|
|
template<StrictSpecializationOf<TupleTreeReference> T>
|
|
struct llvm::yaml::ScalarTraits<T> {
|
|
|
|
static void output(const T &Obj, void *, llvm::raw_ostream &Out) {
|
|
Out << Obj.toString();
|
|
}
|
|
|
|
static llvm::StringRef input(llvm::StringRef Path, void *, T &Obj) {
|
|
// We temporarily initialize Root to nullptr, a post-processing phase will
|
|
// take care of fixup these
|
|
Obj = T::fromString(static_cast<typename T::RootT *>(nullptr), Path);
|
|
return {};
|
|
}
|
|
|
|
static auto mustQuote(llvm::StringRef) {
|
|
return llvm::yaml::QuotingType::Double;
|
|
}
|
|
};
|
|
|
|
/// Specialization for the std::variant we have in TupleTreeReference
|
|
template<bool X, typename T>
|
|
inline void writeToLog(Logger<X> &This,
|
|
const std::variant<T *, const T *> &Var,
|
|
int Ignored) {
|
|
if (Var.index() == std::variant_npos)
|
|
writeToLog(This, llvm::StringRef("std::variant_npos"), Ignored);
|
|
else if (std::holds_alternative<T *>(Var))
|
|
writeToLog(This, std::get<T *>(Var), Ignored);
|
|
else if (std::holds_alternative<const T *>(Var))
|
|
writeToLog(This, std::get<const T *>(Var), Ignored);
|
|
}
|