Переглянути джерело

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;
    };
Andreas Kling 1 рік тому
батько
коміт
4bbb0a5c35

+ 1 - 0
Meta/gn/secondary/Userland/Libraries/LibJS/BUILD.gn

@@ -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",

+ 1 - 0
Userland/Libraries/LibJS/CMakeLists.txt

@@ -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

+ 3 - 0
Userland/Libraries/LibJS/Forward.h

@@ -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;
 

+ 23 - 0
Userland/Libraries/LibJS/Heap/ConservativeVector.cpp

@@ -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);
+}
+
+}

+ 73 - 0
Userland/Libraries/LibJS/Heap/ConservativeVector.h

@@ -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() };
+    }
+};
+
+}

+ 6 - 0
Userland/Libraries/LibJS/Heap/Heap.cpp

@@ -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);

+ 18 - 1
Userland/Libraries/LibJS/Heap/Heap.h

@@ -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));

+ 1 - 0
Userland/Libraries/LibJS/Heap/HeapRoot.h

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