diff --git a/Userland/Libraries/LibJS/Heap/Cell.h b/Userland/Libraries/LibJS/Heap/Cell.h index d3820616ac..94d219e181 100644 --- a/Userland/Libraries/LibJS/Heap/Cell.h +++ b/Userland/Libraries/LibJS/Heap/Cell.h @@ -1,11 +1,12 @@ /* - * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include #include #include @@ -80,15 +81,25 @@ public: // This will be called on unmarked objects by the garbage collector in a separate pass before destruction. virtual void finalize() { } + // 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: Cells must call set_overrides_must_survive_garbage_collection() for this to be honored. + virtual bool must_survive_garbage_collection() const { return false; } + + bool overrides_must_survive_garbage_collection(Badge) const { return m_overrides_must_survive_garbage_collection; } + Heap& heap() const; VM& vm() const; protected: Cell() = default; + void set_overrides_must_survive_garbage_collection(bool b) { m_overrides_must_survive_garbage_collection = b; } + private: bool m_mark : 1 { false }; - State m_state : 7 { State::Live }; + bool m_overrides_must_survive_garbage_collection { false }; + State m_state : 1 { State::Live }; }; } diff --git a/Userland/Libraries/LibJS/Heap/Heap.cpp b/Userland/Libraries/LibJS/Heap/Heap.cpp index e7664761f6..a2c95203b6 100644 --- a/Userland/Libraries/LibJS/Heap/Heap.cpp +++ b/Userland/Libraries/LibJS/Heap/Heap.cpp @@ -232,11 +232,18 @@ void Heap::mark_live_cells(HashTable const& roots) m_uprooted_cells.clear(); } +bool Heap::cell_must_survive_garbage_collection(Cell const& cell) +{ + if (!cell.overrides_must_survive_garbage_collection({})) + return false; + return cell.must_survive_garbage_collection(); +} + void Heap::finalize_unmarked_cells() { for_each_block([&](auto& block) { block.template for_each_cell_in_state([](Cell* cell) { - if (!cell->is_marked()) + if (!cell->is_marked() && !cell_must_survive_garbage_collection(*cell)) cell->finalize(); }); return IterationDecision::Continue; @@ -258,7 +265,7 @@ void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measure bool block_has_live_cells = false; bool block_was_full = block.is_full(); block.template for_each_cell_in_state([&](Cell* cell) { - if (!cell->is_marked()) { + if (!cell->is_marked() && !cell_must_survive_garbage_collection(*cell)) { dbgln_if(HEAP_DEBUG, " ~ {}", cell); block.deallocate(cell); ++collected_cells; diff --git a/Userland/Libraries/LibJS/Heap/Heap.h b/Userland/Libraries/LibJS/Heap/Heap.h index f8e43177d4..da894067d1 100644 --- a/Userland/Libraries/LibJS/Heap/Heap.h +++ b/Userland/Libraries/LibJS/Heap/Heap.h @@ -79,6 +79,8 @@ public: void uproot_cell(Cell* cell); private: + static bool cell_must_survive_garbage_collection(Cell const&); + Cell* allocate_cell(size_t); void gather_roots(HashTable&);