/* * Copyright (c) 2020-2024, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace GC { template struct IsVisitable; #if defined(AK_COMPILER_CLANG) # define GC_ALLOW_CELL_DESTRUCTOR [[clang::annotate("ladybird::allow_cell_destructor")]] #else # define GC_ALLOW_CELL_DESTRUCTOR #endif #define GC_CELL(class_, base_class) \ public: \ using Base = base_class; \ virtual StringView class_name() const override \ { \ return #class_##sv; \ } \ friend class GC::Heap; #define GC_CELL_WITH_CUSTOM_CLASS_NAME(class_, base_class) \ public: \ using Base = base_class; \ friend class GC::Heap; class GC_API Cell { AK_MAKE_NONCOPYABLE(Cell); AK_MAKE_NONMOVABLE(Cell); public: static constexpr bool OVERRIDES_MUST_SURVIVE_GARBAGE_COLLECTION = false; static constexpr bool OVERRIDES_FINALIZE = false; virtual ~Cell() = default; bool is_marked() const { return m_mark; } void set_marked(bool b) { m_mark = b; } enum class State : bool { Live, Dead, }; State state() const { return m_state; } void set_state(State state) { m_state = state; } virtual StringView class_name() const = 0; class GC_API Visitor { public: void visit(Cell* cell) { if (cell) visit_impl(*cell); } void visit(Cell& cell) { visit_impl(cell); } void visit(Cell const* cell) { visit(const_cast(cell)); } void visit(Cell const& cell) { visit(const_cast(cell)); } template void visit(Ptr cell) { if (cell) visit_impl(const_cast&>(*cell.ptr())); } template void visit(Ref cell) { visit_impl(const_cast&>(*cell.ptr())); } template void visit(ReadonlySpan span) requires(!IsBaseOf && IsVisitable::value) { for (auto& value : span) visit(value); } template void visit(ReadonlySpan span) requires(IsBaseOf) { visit_impl(ReadonlySpan(span.data(), span.size())); } template void visit(Span span) requires(!IsBaseOf && IsVisitable::value) { for (auto& value : span) visit(value); } template void visit(Span span) requires(IsBaseOf) { visit_impl(ReadonlySpan(span.data(), span.size())); } template void visit(Vector const& vector) requires(!IsBaseOf && IsVisitable::value) { for (auto& value : vector) visit(value); } template void visit(Vector const& vector) requires(IsBaseOf) { visit_impl(ReadonlySpan(vector.span().data(), vector.size())); } template void visit(HashTable const& table) requires(IsVisitable::value) { for (auto& value : table) visit(value); } template void visit(OrderedHashTable const& table) requires(IsVisitable::value) { for (auto& value : table) visit(value); } template void visit(HashMap const& map) { for (auto& it : map) { if constexpr (requires { visit(it.key); }) visit(it.key); if constexpr (requires { visit(it.value); }) visit(it.value); } } template void visit(OrderedHashMap const& map) { for (auto& it : map) { if constexpr (requires { visit(it.key); }) visit(it.key); if constexpr (requires { visit(it.value); }) visit(it.value); } } template void visit(Optional const& optional) requires(IsVisitable::value) { if (optional.has_value()) visit(optional.value()); } void visit(NanBoxedValue const& value); template void visit(Variant const& variant) requires((IsVisitable::value || ...)) { variant.visit([&](auto const& value) { if constexpr (requires { visit(value); }) visit(value); }); } // Allow explicitly ignoring a GC-allocated member in a visit_edges implementation instead // of just not using it. template void ignore(T const&) { } virtual void visit_possible_values(ReadonlyBytes) = 0; protected: virtual void visit_impl(Cell&) = 0; virtual void visit_impl(ReadonlySpan) = 0; virtual ~Visitor() = default; }; MUST_UPCALL virtual void visit_edges(Visitor&) { } // This will be called on unmarked objects by the garbage collector in a separate pass before destruction. MUST_UPCALL virtual void finalize() { } virtual size_t external_memory_size() const { return 0; } // This allows cells to survive GC by choice, even if nothing points to them. // It's used to implement special rules in the web platform. // NOTE: Cell types must have OVERRIDES_MUST_SURVIVE_GARBAGE_COLLECTION set for this to be called. virtual bool must_survive_garbage_collection() const { return false; } ALWAYS_INLINE Heap& heap() const { return HeapBlockBase::from_cell(this)->heap(); } protected: Cell() = default; private: bool m_mark { false }; State m_state { State::Live }; }; template struct IsVisitable { static constexpr bool value = requires(Cell::Visitor& visitor, T const& value) { visitor.visit(value); }; }; } template<> struct AK::Formatter : AK::Formatter { ErrorOr format(FormatBuilder& builder, GC::Cell const* cell) { if (!cell) return builder.put_string("Cell{nullptr}"sv); return Formatter::format(builder, "{}({})"sv, cell->class_name(), cell); } };