LibGC: Share root container value gathering

Add a small helper for turning root container values into Cell pointers,
and use it from RootVector, RootHashTable, and RootHashMap.
This commit is contained in:
Shannon Booth 2026-05-22 20:23:13 +02:00 committed by Shannon Booth
parent 45f1b18263
commit 47bd8388c5
4 changed files with 53 additions and 33 deletions

View file

@ -11,6 +11,7 @@
#include <LibGC/Cell.h>
#include <LibGC/Forward.h>
#include <LibGC/HeapRoot.h>
#include <LibGC/Rootable.h>
namespace GC {
@ -49,24 +50,15 @@ public:
virtual void gather_roots(HashMap<Cell*, GC::HeapRoot>& roots) const override
{
static constexpr bool KeyIsGCType = IsBaseOf<NanBoxedValue, K> || IsConvertible<K, Cell const*>;
static constexpr bool ValueIsGCType = IsBaseOf<NanBoxedValue, V> || IsConvertible<V, Cell const*>;
static constexpr bool KeyIsGCType = Detail::RootableValueTraits<K>::is_rootable;
static constexpr bool ValueIsGCType = Detail::RootableValueTraits<V>::is_rootable;
static_assert(KeyIsGCType || ValueIsGCType,
"RootHashMap requires at least one of key or value types to be convertible to Cell const* or derive from NanBoxedValue");
for (auto& [key, value] : *this) {
if constexpr (IsBaseOf<NanBoxedValue, K>) {
if (key.is_cell())
roots.set(&const_cast<K&>(key).as_cell(), HeapRoot { .type = HeapRoot::Type::RootHashMap });
} else if constexpr (IsConvertible<K, Cell const*>) {
roots.set(const_cast<Cell*>(static_cast<Cell const*>(key)), HeapRoot { .type = HeapRoot::Type::RootHashMap });
}
if constexpr (IsBaseOf<NanBoxedValue, V>) {
if (value.is_cell())
roots.set(&const_cast<V&>(value).as_cell(), HeapRoot { .type = HeapRoot::Type::RootHashMap });
} else if constexpr (IsConvertible<V, Cell const*>) {
roots.set(const_cast<Cell*>(static_cast<Cell const*>(value)), HeapRoot { .type = HeapRoot::Type::RootHashMap });
}
if constexpr (KeyIsGCType)
Detail::gather_root(roots, key, HeapRoot::Type::RootHashMap);
if constexpr (ValueIsGCType)
Detail::gather_root(roots, value, HeapRoot::Type::RootHashMap);
}
}
};

View file

@ -12,6 +12,7 @@
#include <LibGC/Cell.h>
#include <LibGC/Forward.h>
#include <LibGC/HeapRoot.h>
#include <LibGC/Rootable.h>
namespace GC {
@ -48,16 +49,10 @@ public:
virtual void gather_roots(HashMap<Cell*, GC::HeapRoot>& roots) const override
{
static_assert(IsBaseOf<NanBoxedValue, T> || IsConvertible<T, Cell const*>,
static_assert(Detail::RootableValueTraits<T>::is_rootable,
"RootHashTable element type must be convertible to Cell const* or derive from NanBoxedValue");
for (auto& value : *this) {
if constexpr (IsBaseOf<NanBoxedValue, T>) {
if (value.is_cell())
roots.set(&const_cast<T&>(value).as_cell(), HeapRoot { .type = HeapRoot::Type::RootHashTable });
} else if constexpr (IsConvertible<T, Cell const*>) {
roots.set(const_cast<Cell*>(static_cast<Cell const*>(value)), HeapRoot { .type = HeapRoot::Type::RootHashTable });
}
}
for (auto& value : *this)
Detail::gather_root(roots, value, HeapRoot::Type::RootHashTable);
}
};

View file

@ -13,6 +13,7 @@
#include <LibGC/Cell.h>
#include <LibGC/Forward.h>
#include <LibGC/HeapRoot.h>
#include <LibGC/Rootable.h>
namespace GC {
@ -86,16 +87,10 @@ public:
virtual void gather_roots(HashMap<Cell*, GC::HeapRoot>& roots) const override
{
static_assert(IsBaseOf<NanBoxedValue, T> || IsConvertible<T, Cell const*>,
static_assert(Detail::RootableValueTraits<T>::is_rootable,
"RootVector element type must be convertible to Cell const* or derive from NanBoxedValue");
for (auto& value : *this) {
if constexpr (IsBaseOf<NanBoxedValue, T>) {
if (value.is_cell())
roots.set(&const_cast<T&>(value).as_cell(), HeapRoot { .type = HeapRoot::Type::RootVector });
} else if constexpr (IsConvertible<T, Cell const*>) {
roots.set(const_cast<Cell*>(static_cast<Cell const*>(value)), HeapRoot { .type = HeapRoot::Type::RootVector });
}
}
for (auto& value : *this)
Detail::gather_root(roots, value, HeapRoot::Type::RootVector);
}
};

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2026, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGC/Cell.h>
#include <LibGC/HeapRoot.h>
namespace GC::Detail {
template<typename T>
struct RootableValueTraits {
static constexpr bool is_rootable = IsBaseOf<NanBoxedValue, T> || IsConvertible<T, Cell const*>;
static Cell* cell(T const& value)
{
if constexpr (IsBaseOf<NanBoxedValue, T>) {
if (value.is_cell())
return &const_cast<T&>(value).as_cell();
} else if constexpr (IsConvertible<T, Cell const*>) {
return const_cast<Cell*>(static_cast<Cell const*>(value));
}
return nullptr;
}
};
template<typename T>
static void gather_root(HashMap<Cell*, GC::HeapRoot>& roots, T const& value, HeapRoot::Type root_type)
{
if (auto* cell = RootableValueTraits<T>::cell(value))
roots.set(cell, HeapRoot { .type = root_type });
}
}