LibJS: Add ConservativeVector<T>

This works very similarly to MarkedVector<T>, but instead of expecting
T to be Value or a GC-allocated pointer type, T can be anything.
Every pointer-sized value in the vector's storage will be checked during
conservative root scanning.

In other words, this allows you to put something like this in a
ConservativeVector<Foo> and it will be protected from GC:

    struct Foo {
        i64 number;
        Value some_value;
        GCPtr<Object> some_object;
    };
This commit is contained in:
Andreas Kling 2024-02-22 13:18:31 +01:00
parent cbecd096c2
commit 4bbb0a5c35
8 changed files with 126 additions and 1 deletions

View file

@ -44,6 +44,7 @@ shared_library("LibJS") {
"Heap/BlockAllocator.cpp",
"Heap/Cell.cpp",
"Heap/CellAllocator.cpp",
"Heap/ConservativeVector.cpp",
"Heap/Handle.cpp",
"Heap/Heap.cpp",
"Heap/HeapBlock.cpp",

View file

@ -20,6 +20,7 @@ set(SOURCES
Heap/BlockAllocator.cpp
Heap/Cell.cpp
Heap/CellAllocator.cpp
Heap/ConservativeVector.cpp
Heap/Handle.cpp
Heap/Heap.cpp
Heap/HeapBlock.cpp

View file

@ -302,6 +302,9 @@ class ThrowCompletionOr;
template<class T>
class Handle;
template<class T, size_t inline_capacity = 0>
class ConservativeVector;
template<class T, size_t inline_capacity = 0>
class MarkedVector;

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2024, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Heap/ConservativeVector.h>
#include <LibJS/Heap/Heap.h>
namespace JS {
ConservativeVectorBase::ConservativeVectorBase(Heap& heap)
: m_heap(&heap)
{
m_heap->did_create_conservative_vector({}, *this);
}
ConservativeVectorBase::~ConservativeVectorBase()
{
m_heap->did_destroy_conservative_vector({}, *this);
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2024, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashMap.h>
#include <AK/IntrusiveList.h>
#include <AK/Vector.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/Heap/HeapRoot.h>
namespace JS {
class ConservativeVectorBase {
public:
virtual ReadonlySpan<FlatPtr> possible_values() const = 0;
protected:
explicit ConservativeVectorBase(Heap&);
~ConservativeVectorBase();
ConservativeVectorBase& operator=(ConservativeVectorBase const&);
Heap* m_heap { nullptr };
IntrusiveListNode<ConservativeVectorBase> m_list_node;
public:
using List = IntrusiveList<&ConservativeVectorBase::m_list_node>;
};
template<typename T, size_t inline_capacity>
class ConservativeVector final
: public ConservativeVectorBase
, public Vector<T, inline_capacity> {
public:
explicit ConservativeVector(Heap& heap)
: ConservativeVectorBase(heap)
{
}
virtual ~ConservativeVector() = default;
ConservativeVector(ConservativeVector const& other)
: ConservativeVectorBase(*other.m_heap)
, Vector<T, inline_capacity>(other)
{
}
ConservativeVector(ConservativeVector&& other)
: ConservativeVectorBase(*other.m_heap)
, Vector<T, inline_capacity>(move(static_cast<Vector<T, inline_capacity>&>(other)))
{
}
ConservativeVector& operator=(ConservativeVector const& other)
{
Vector<T, inline_capacity>::operator=(other);
ConservativeVectorBase::operator=(other);
return *this;
}
virtual ReadonlySpan<FlatPtr> possible_values() const override
{
return ReadonlySpan<FlatPtr> { reinterpret_cast<FlatPtr const*>(this->data()), this->size() };
}
};
}

View file

@ -369,6 +369,12 @@ __attribute__((no_sanitize("address"))) void Heap::gather_conservative_roots(Has
}
}
for (auto& vector : m_conservative_vectors) {
for (auto possible_value : vector.possible_values()) {
add_possible_value(possible_pointers, possible_value, HeapRoot { .type = HeapRoot::Type::ConservativeVector }, min_block_address, max_block_address);
}
}
HashTable<HeapBlock*> all_live_heap_blocks;
for_each_block([&](auto& block) {
all_live_heap_blocks.set(&block);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2023, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2024, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -17,6 +17,7 @@
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/Heap/CellAllocator.h>
#include <LibJS/Heap/ConservativeVector.h>
#include <LibJS/Heap/Handle.h>
#include <LibJS/Heap/HeapRoot.h>
#include <LibJS/Heap/Internals.h>
@ -74,6 +75,9 @@ public:
void did_create_marked_vector(Badge<MarkedVectorBase>, MarkedVectorBase&);
void did_destroy_marked_vector(Badge<MarkedVectorBase>, MarkedVectorBase&);
void did_create_conservative_vector(Badge<ConservativeVectorBase>, ConservativeVectorBase&);
void did_destroy_conservative_vector(Badge<ConservativeVectorBase>, ConservativeVectorBase&);
void did_create_weak_container(Badge<WeakContainer>, WeakContainer&);
void did_destroy_weak_container(Badge<WeakContainer>, WeakContainer&);
@ -147,6 +151,7 @@ private:
HandleImpl::List m_handles;
MarkedVectorBase::List m_marked_vectors;
ConservativeVectorBase::List m_conservative_vectors;
WeakContainer::List m_weak_containers;
Vector<GCPtr<Cell>> m_uprooted_cells;
@ -181,6 +186,18 @@ inline void Heap::did_destroy_marked_vector(Badge<MarkedVectorBase>, MarkedVecto
m_marked_vectors.remove(vector);
}
inline void Heap::did_create_conservative_vector(Badge<ConservativeVectorBase>, ConservativeVectorBase& vector)
{
VERIFY(!m_conservative_vectors.contains(vector));
m_conservative_vectors.append(vector);
}
inline void Heap::did_destroy_conservative_vector(Badge<ConservativeVectorBase>, ConservativeVectorBase& vector)
{
VERIFY(m_conservative_vectors.contains(vector));
m_conservative_vectors.remove(vector);
}
inline void Heap::did_create_weak_container(Badge<WeakContainer>, WeakContainer& set)
{
VERIFY(!m_weak_containers.contains(set));

View file

@ -15,6 +15,7 @@ struct HeapRoot {
HeapFunctionCapturedPointer,
Handle,
MarkedVector,
ConservativeVector,
RegisterPointer,
SafeFunction,
StackPointer,