LibJS: Check live blocks before pruning executable caches

Executable caches can retain weak pointers to shapes and prototypes
across collections. With incremental sweeping, a previous sweep may have
already freed the block behind one of those pointers by the time pruning
runs for weak containers.

Use the live HeapBlock registry before reading cached cells. This
matches the other weak containers updated for incremental sweeping.
This commit is contained in:
Andreas Kling
2026-05-09 19:05:31 +02:00
committed by Andreas Kling
parent 4dfed815cc
commit 37117b1cb4
Notes: github-actions[bot] 2026-05-10 08:59:26 +00:00

View File

@@ -5,6 +5,8 @@
*/
#include <AK/BinarySearch.h>
#include <LibGC/Heap.h>
#include <LibGC/HeapBlock.h>
#include <LibJS/Bytecode/BasicBlock.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/FormatOperand.h>
@@ -339,11 +341,16 @@ StaticPropertyLookupCache::StaticPropertyLookupCache()
static_property_lookup_caches().append(this);
}
static bool cell_is_dead(Cell const* cell)
{
auto* block = GC::HeapBlock::from_cell(cell);
if (!GC::Heap::the().is_live_heap_block(block))
return true;
return cell->state() != Cell::State::Live || !cell->is_marked();
}
static void clear_cache_entry_if_dead(PropertyLookupCache::Entry& entry)
{
auto cell_is_dead = [](Cell const* cell) {
return cell->state() != Cell::State::Live || !cell->is_marked();
};
if (entry.from_shape && cell_is_dead(entry.from_shape))
entry.from_shape = nullptr;
if (entry.shape && cell_is_dead(entry.shape))
@@ -373,7 +380,8 @@ void Executable::remove_dead_cells(Badge<GC::Heap>)
clear_cache_entry_if_dead(entry);
}
for (auto& cache : object_shape_caches) {
if (cache.shape && (cache.shape->state() != Cell::State::Live || !cache.shape->is_marked()))
auto* shape = cache.shape.ptr();
if (shape && cell_is_dead(shape))
cache.shape = nullptr;
}
}