#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/ADT/StringRef.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_ostream.h" #include "revng/ADT/KeyedObjectContainer.h" #include "revng/ADT/ZipMapIterator.h" #include "revng/TupleTree/TupleTree.h" template struct is_std_vector : std::false_type {}; template struct is_std_vector> : std::true_type {}; template concept HasPushBack = is_std_vector::value; template concept HasInsertOrAssign = not HasPushBack; template void addToContainer(C &Container, const typename C::value_type &Value) { Container.insert_or_assign(Value); } template void addToContainer(C &Container, const typename C::value_type &Value) { Container.push_back(Value); } template struct TupleTreeDiff { struct Change { TupleTreePath Path; void *Old; void *New; }; std::vector Changes; // TODO: invalidated instances TupleTreeDiff invert() const { TupleTreeDiff Result = *this; for (Change &C : Result.Changes) { std::swap(C.Old, C.New); } return Result; } void add(const TupleTreePath &Path, void *What) { Changes.push_back({ Path, nullptr, What }); } void remove(const TupleTreePath &Path, void *What) { Changes.push_back({ Path, What, nullptr }); } void change(const TupleTreePath &Path, void *From, void *To) { Changes.push_back({ Path, From, To }); } void dump(llvm::raw_ostream &OutputStream) const; void dump() const { llvm::raw_os_ostream OutputStream(dbg); dump(OutputStream); } void apply(T &M) const; }; // // diff // namespace tupletreediff::detail { template struct Diff { TupleTreePath Stack; TupleTreeDiff Result; TupleTreeDiff diff(M &LHS, M &RHS) { diffImpl(LHS, RHS); return Result; } private: template void diffTuple(T &LHS, T &RHS) { if constexpr (I < std::tuple_size_v) { Stack.push_back(size_t(I)); diffImpl(get(LHS), get(RHS)); Stack.pop_back(); // Recur diffTuple(LHS, RHS); } } template void diffImpl(T &LHS, T &RHS) { LHS.upcast([&](auto &LHSUpcasted) { RHS.upcast([&](auto &RHSUpcasted) { using LHSType = std::remove_cvref_t; using RHSType = std::remove_cvref_t; if constexpr (std::is_same_v) { diffImpl(LHSUpcasted, RHSUpcasted); } else { Result.change(Stack, &LHS, &RHS); } }); }); } template void diffImpl(T &LHS, T &RHS) { diffTuple(LHS, RHS); } template void diffImpl(T &LHS, T &RHS) { for (auto [LHSElement, RHSElement] : zipmap_range(LHS, RHS)) { if (LHSElement == nullptr) { // Added Result.add(Stack, RHSElement); } else if (RHSElement == nullptr) { // Removed Result.remove(Stack, LHSElement); } else { // Identical using value_type = typename T::value_type; Stack.push_back(KeyedObjectTraits::key(*LHSElement)); diffImpl(*LHSElement, *RHSElement); Stack.pop_back(); } } } template void diffImpl(T &LHS, T &RHS) { if (LHS != RHS) Result.change(Stack, &LHS, &RHS); } }; } // namespace tupletreediff::detail template TupleTreeDiff diff(M &LHS, M &RHS) { return tupletreediff::detail::Diff().diff(LHS, RHS); } // // TupleTreeDiff::dump // namespace tupletreediff::detail { template void stream(T *M, S &Stream) { using namespace llvm::yaml; Output Out(Stream); EmptyContext Ctx; yamlize(Out, *M, true, Ctx); } template void stream(T *M, S &Stream) { Stream << *M; } template void dumpWithPrefixAndColor(llvm::raw_ostream &OutputStream, llvm::StringRef Prefix, llvm::raw_ostream::Colors Color, T *M) { std::string Buffer; llvm::WithColor Stream(OutputStream); Stream.changeColor(Color); { llvm::raw_string_ostream StringStream(Buffer); stream(M, StringStream); } auto [LHS, RHS] = llvm::StringRef(Buffer).split('\n'); while (RHS.size() != 0) { Stream << Prefix << LHS << "\n"; std::tie(LHS, RHS) = RHS.split('\n'); } Stream << Prefix << LHS << "\n"; } struct DumpDiffVisitor { llvm::raw_ostream &OutputStream; void *Old, *New; template void visitTupleElement() { using tuple_element = typename std::tuple_element::type; visit(); } template void visitContainerElement(KeyT Key) {} template void visit() { revng_assert((Old != nullptr) != (New != nullptr)); dump(); } template void visit() { revng_assert(Old != nullptr and New != nullptr); dump(); } template void dump() { if (Old != nullptr) { dumpWithPrefixAndColor(OutputStream, "-", llvm::raw_ostream::RED, reinterpret_cast(Old)); } if (New != nullptr) { dumpWithPrefixAndColor(OutputStream, "+", llvm::raw_ostream::GREEN, reinterpret_cast(New)); } } }; } // namespace tupletreediff::detail template inline void TupleTreeDiff::dump(llvm::raw_ostream &OutputStream) const { using namespace tupletreediff::detail; TupleTreePath LastPath; for (const Change &C : Changes) { if (LastPath != C.Path) { std::string NewPath = *pathAsString(C.Path); OutputStream << "--- " << NewPath << "\n"; OutputStream << "+++ " << NewPath << "\n"; LastPath = C.Path; } DumpDiffVisitor PV2{ OutputStream, C.Old, C.New }; callByPath(PV2, C.Path); } } // // TupleTreeDiff::apply // namespace tupletreediff::detail { // clang-format off template concept IterableAndNotStdString = Iterable and not std::is_same_v; // clang-format on template concept NotIterableOrStdString = not IterableAndNotStdString; template struct ApplyDiffVisitor { using Change = typename TupleTreeDiff::Change; const Change *C; template void visitTupleElement(K &Element) { visit(Element); } template void visitContainerElement(KeyT, K &Element) { visit(Element); } template void visit(S &M) { revng_assert((C->Old == nullptr) != (C->New == nullptr)); using value_type = typename S::value_type; using KOT = KeyedObjectTraits; using key_type = decltype(KOT::key(std::declval())); size_t OldSize = M.size(); if (C->Old != nullptr) { key_type Key = KOT::key(*reinterpret_cast(C->Old)); auto End = M.end(); auto CompareKeys = [Key](value_type &V) { return KOT::key(V) == Key; }; auto FirstToDelete = std::remove_if(M.begin(), End, CompareKeys); M.erase(FirstToDelete, End); revng_assert(OldSize == M.size() + 1); } else if (C->New != nullptr) { // TODO: assert not there already addToContainer(M, *reinterpret_cast(C->New)); revng_assert(OldSize == M.size() - 1); } else { revng_abort(); } } template void visit(S &M) { revng_assert(C->Old != nullptr and C->New != nullptr); auto *Old = reinterpret_cast(C->Old); auto *New = reinterpret_cast(C->New); revng_check(*Old == M); M = *New; } }; } // namespace tupletreediff::detail template inline void TupleTreeDiff::apply(T &M) const { TupleTreePath LastPath; for (const Change &C : Changes) { tupletreediff::detail::ApplyDiffVisitor ADV{ &C }; callByPath(ADV, C.Path, M); } }