فهرست منبع

LibJS: Don't directly teach the heap about the javascript VM or Realm

Instead, smuggle it in as a `void*` private data and let Javascript
aware code cast out that pointer to a VM&.

In order to make this split, rename JS::Cell to JS::CellImpl. Once we
have a LibGC, this will become GC::Cell. CellImpl then has no specific
knowledge of the VM& and Realm&. That knowledge is instead put into
JS::Cell, which inherits from CellImpl. JS::Cell is responsible for
JavaScript's realm initialization, as well as converting of the void*
private data to what it knows should be the VM&.
Shannon Booth 7 ماه پیش
والد
کامیت
c2988a7dd5

+ 1 - 0
Libraries/LibJS/CMakeLists.txt

@@ -21,6 +21,7 @@ set(SOURCES
     CyclicModule.cpp
     Heap/BlockAllocator.cpp
     Heap/Cell.cpp
+    Heap/CellImpl.cpp
     Heap/CellAllocator.cpp
     Heap/ConservativeVector.cpp
     Heap/Handle.cpp

+ 1 - 0
Libraries/LibJS/Forward.h

@@ -159,6 +159,7 @@ class BigInt;
 class BoundFunction;
 struct CachedSourceRange;
 class Cell;
+class CellImpl;
 class CellAllocator;
 class ClassExpression;
 struct ClassFieldDefinition;

+ 1 - 9
Libraries/LibJS/Heap/Cell.cpp

@@ -5,19 +5,11 @@
  */
 
 #include <LibJS/Heap/Cell.h>
-#include <LibJS/Heap/Heap.h>
-#include <LibJS/Heap/NanBoxedValue.h>
 
 namespace JS {
 
-void JS::Cell::initialize(JS::Realm&)
+void Cell::initialize(Realm&)
 {
 }
 
-void JS::Cell::Visitor::visit(NanBoxedValue const& value)
-{
-    if (value.is_cell())
-        visit_impl(value.as_cell());
-}
-
 }

+ 4 - 187
Libraries/LibJS/Heap/Cell.h

@@ -6,200 +6,17 @@
 
 #pragma once
 
-#include <AK/Badge.h>
-#include <AK/Format.h>
-#include <AK/Forward.h>
-#include <AK/HashMap.h>
-#include <AK/Noncopyable.h>
-#include <AK/StringView.h>
-#include <AK/Weakable.h>
-#include <LibJS/Forward.h>
-#include <LibJS/Heap/GCPtr.h>
-#include <LibJS/Heap/Internals.h>
+#include <LibJS/Heap/CellImpl.h>
 
 namespace JS {
 
-// This instrumentation tells analysis tooling to ignore a potentially mis-wrapped GC-allocated member variable
-// It should only be used when the lifetime of the GC-allocated member is always longer than the object
-#if defined(AK_COMPILER_CLANG)
-#    define IGNORE_GC [[clang::annotate("serenity::ignore_gc")]]
-#else
-#    define IGNORE_GC
-#endif
-
-#define JS_CELL(class_, base_class)                \
-public:                                            \
-    using Base = base_class;                       \
-    virtual StringView class_name() const override \
-    {                                              \
-        return #class_##sv;                        \
-    }                                              \
-    friend class JS::Heap;
-
-class Cell : public Weakable<Cell> {
-    AK_MAKE_NONCOPYABLE(Cell);
-    AK_MAKE_NONMOVABLE(Cell);
+class Cell : public CellImpl {
+    JS_CELL(Cell, CellImpl);
 
 public:
     virtual void initialize(Realm&);
-    virtual ~Cell() = default;
-
-    bool is_marked() const { return m_mark; }
-    void set_marked(bool b) { m_mark = b; }
-
-    enum class State : bool {
-        Live,
-        Dead,
-    };
-
-    State state() const { return m_state; }
-    void set_state(State state) { m_state = state; }
-
-    virtual StringView class_name() const = 0;
-
-    class Visitor {
-    public:
-        void visit(Cell* cell)
-        {
-            if (cell)
-                visit_impl(*cell);
-        }
-
-        void visit(Cell& cell)
-        {
-            visit_impl(cell);
-        }
-
-        void visit(Cell const* cell)
-        {
-            visit(const_cast<Cell*>(cell));
-        }
-
-        void visit(Cell const& cell)
-        {
-            visit(const_cast<Cell&>(cell));
-        }
-
-        template<typename T>
-        void visit(GCPtr<T> cell)
-        {
-            if (cell)
-                visit_impl(const_cast<RemoveConst<T>&>(*cell.ptr()));
-        }
-
-        template<typename T>
-        void visit(NonnullGCPtr<T> cell)
-        {
-            visit_impl(const_cast<RemoveConst<T>&>(*cell.ptr()));
-        }
-
-        template<typename T>
-        void visit(ReadonlySpan<T> span)
-        {
-            for (auto& value : span)
-                visit(value);
-        }
-
-        template<typename T>
-        void visit(Span<T> span)
-        {
-            for (auto& value : span)
-                visit(value);
-        }
-
-        template<typename T>
-        void visit(Vector<T> const& vector)
-        {
-            for (auto& value : vector)
-                visit(value);
-        }
-
-        template<typename T>
-        void visit(HashTable<T> const& table)
-        {
-            for (auto& value : table)
-                visit(value);
-        }
 
-        template<typename T>
-        void visit(OrderedHashTable<T> const& table)
-        {
-            for (auto& value : table)
-                visit(value);
-        }
-
-        template<typename K, typename V, typename T>
-        void visit(HashMap<K, V, T> const& map)
-        {
-            for (auto& it : map) {
-                if constexpr (requires { visit(it.key); })
-                    visit(it.key);
-                if constexpr (requires { visit(it.value); })
-                    visit(it.value);
-            }
-        }
-
-        template<typename K, typename V, typename T>
-        void visit(OrderedHashMap<K, V, T> const& map)
-        {
-            for (auto& it : map) {
-                if constexpr (requires { visit(it.key); })
-                    visit(it.key);
-                if constexpr (requires { visit(it.value); })
-                    visit(it.value);
-            }
-        }
-
-        void visit(NanBoxedValue const& value);
-
-        // Allow explicitly ignoring a GC-allocated member in a visit_edges implementation instead
-        // of just not using it.
-        template<typename T>
-        void ignore(T const&)
-        {
-        }
-
-        virtual void visit_possible_values(ReadonlyBytes) = 0;
-
-    protected:
-        virtual void visit_impl(Cell&) = 0;
-        virtual ~Visitor() = default;
-    };
-
-    virtual void visit_edges(Visitor&) { }
-
-    // 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<Heap>) const { return m_overrides_must_survive_garbage_collection; }
-
-    ALWAYS_INLINE Heap& heap() const { return HeapBlockBase::from_cell(this)->heap(); }
-    ALWAYS_INLINE VM& vm() const { return bit_cast<HeapBase*>(&heap())->vm(); }
-
-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 };
-    bool m_overrides_must_survive_garbage_collection : 1 { false };
-    State m_state : 1 { State::Live };
+    ALWAYS_INLINE VM& vm() const { return *reinterpret_cast<VM*>(private_data()); }
 };
 
 }
-
-template<>
-struct AK::Formatter<JS::Cell> : AK::Formatter<FormatString> {
-    ErrorOr<void> format(FormatBuilder& builder, JS::Cell const* cell)
-    {
-        if (!cell)
-            return builder.put_string("Cell{nullptr}"sv);
-        return Formatter<FormatString>::format(builder, "{}({})"sv, cell->class_name(), cell);
-    }
-};

+ 1 - 1
Libraries/LibJS/Heap/CellAllocator.cpp

@@ -18,7 +18,7 @@ CellAllocator::CellAllocator(size_t cell_size, char const* class_name)
 {
 }
 
-Cell* CellAllocator::allocate_cell(Heap& heap)
+CellImpl* CellAllocator::allocate_cell(Heap& heap)
 {
     if (!m_list_node.is_in_list())
         heap.register_cell_allocator({}, *this);

+ 1 - 1
Libraries/LibJS/Heap/CellAllocator.h

@@ -28,7 +28,7 @@ public:
 
     size_t cell_size() const { return m_cell_size; }
 
-    Cell* allocate_cell(Heap&);
+    CellImpl* allocate_cell(Heap&);
 
     template<typename Callback>
     IterationDecision for_each_block(Callback callback)

+ 18 - 0
Libraries/LibJS/Heap/CellImpl.cpp

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Heap/CellImpl.h>
+#include <LibJS/Heap/NanBoxedValue.h>
+
+namespace JS {
+
+void JS::CellImpl::Visitor::visit(NanBoxedValue const& value)
+{
+    if (value.is_cell())
+        visit_impl(value.as_cell());
+}
+
+}

+ 205 - 0
Libraries/LibJS/Heap/CellImpl.h

@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Badge.h>
+#include <AK/Format.h>
+#include <AK/Forward.h>
+#include <AK/HashMap.h>
+#include <AK/Noncopyable.h>
+#include <AK/StringView.h>
+#include <AK/Weakable.h>
+#include <LibJS/Forward.h>
+#include <LibJS/Heap/GCPtr.h>
+#include <LibJS/Heap/Internals.h>
+
+namespace JS {
+
+// This instrumentation tells analysis tooling to ignore a potentially mis-wrapped GC-allocated member variable
+// It should only be used when the lifetime of the GC-allocated member is always longer than the object
+#if defined(AK_COMPILER_CLANG)
+#    define IGNORE_GC [[clang::annotate("serenity::ignore_gc")]]
+#else
+#    define IGNORE_GC
+#endif
+
+#define JS_CELL(class_, base_class)                \
+public:                                            \
+    using Base = base_class;                       \
+    virtual StringView class_name() const override \
+    {                                              \
+        return #class_##sv;                        \
+    }                                              \
+    friend class JS::Heap;
+
+class CellImpl : public Weakable<CellImpl> {
+    AK_MAKE_NONCOPYABLE(CellImpl);
+    AK_MAKE_NONMOVABLE(CellImpl);
+
+public:
+    virtual ~CellImpl() = default;
+
+    bool is_marked() const { return m_mark; }
+    void set_marked(bool b) { m_mark = b; }
+
+    enum class State : bool {
+        Live,
+        Dead,
+    };
+
+    State state() const { return m_state; }
+    void set_state(State state) { m_state = state; }
+
+    virtual StringView class_name() const = 0;
+
+    class Visitor {
+    public:
+        void visit(CellImpl* cell)
+        {
+            if (cell)
+                visit_impl(*cell);
+        }
+
+        void visit(CellImpl& cell)
+        {
+            visit_impl(cell);
+        }
+
+        void visit(CellImpl const* cell)
+        {
+            visit(const_cast<CellImpl*>(cell));
+        }
+
+        void visit(CellImpl const& cell)
+        {
+            visit(const_cast<CellImpl&>(cell));
+        }
+
+        template<typename T>
+        void visit(GCPtr<T> cell)
+        {
+            if (cell)
+                visit_impl(const_cast<RemoveConst<T>&>(*cell.ptr()));
+        }
+
+        template<typename T>
+        void visit(NonnullGCPtr<T> cell)
+        {
+            visit_impl(const_cast<RemoveConst<T>&>(*cell.ptr()));
+        }
+
+        template<typename T>
+        void visit(ReadonlySpan<T> span)
+        {
+            for (auto& value : span)
+                visit(value);
+        }
+
+        template<typename T>
+        void visit(Span<T> span)
+        {
+            for (auto& value : span)
+                visit(value);
+        }
+
+        template<typename T>
+        void visit(Vector<T> const& vector)
+        {
+            for (auto& value : vector)
+                visit(value);
+        }
+
+        template<typename T>
+        void visit(HashTable<T> const& table)
+        {
+            for (auto& value : table)
+                visit(value);
+        }
+
+        template<typename T>
+        void visit(OrderedHashTable<T> const& table)
+        {
+            for (auto& value : table)
+                visit(value);
+        }
+
+        template<typename K, typename V, typename T>
+        void visit(HashMap<K, V, T> const& map)
+        {
+            for (auto& it : map) {
+                if constexpr (requires { visit(it.key); })
+                    visit(it.key);
+                if constexpr (requires { visit(it.value); })
+                    visit(it.value);
+            }
+        }
+
+        template<typename K, typename V, typename T>
+        void visit(OrderedHashMap<K, V, T> const& map)
+        {
+            for (auto& it : map) {
+                if constexpr (requires { visit(it.key); })
+                    visit(it.key);
+                if constexpr (requires { visit(it.value); })
+                    visit(it.value);
+            }
+        }
+
+        void visit(NanBoxedValue const& value);
+
+        // Allow explicitly ignoring a GC-allocated member in a visit_edges implementation instead
+        // of just not using it.
+        template<typename T>
+        void ignore(T const&)
+        {
+        }
+
+        virtual void visit_possible_values(ReadonlyBytes) = 0;
+
+    protected:
+        virtual void visit_impl(CellImpl&) = 0;
+        virtual ~Visitor() = default;
+    };
+
+    virtual void visit_edges(Visitor&) { }
+
+    // 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<Heap>) const { return m_overrides_must_survive_garbage_collection; }
+
+    ALWAYS_INLINE Heap& heap() const { return HeapBlockBase::from_cell(this)->heap(); }
+
+protected:
+    CellImpl() = default;
+
+    ALWAYS_INLINE void* private_data() const { return bit_cast<HeapBase*>(&heap())->private_data(); }
+
+    void set_overrides_must_survive_garbage_collection(bool b) { m_overrides_must_survive_garbage_collection = b; }
+
+private:
+    bool m_mark : 1 { false };
+    bool m_overrides_must_survive_garbage_collection : 1 { false };
+    State m_state : 1 { State::Live };
+};
+
+}
+
+template<>
+struct AK::Formatter<JS::CellImpl> : AK::Formatter<FormatString> {
+    ErrorOr<void> format(FormatBuilder& builder, JS::CellImpl const* cell)
+    {
+        if (!cell)
+            return builder.put_string("Cell{nullptr}"sv);
+        return Formatter<FormatString>::format(builder, "{}({})"sv, cell->class_name(), cell);
+    }
+};

+ 1 - 1
Libraries/LibJS/Heap/ConservativeVector.h

@@ -10,7 +10,7 @@
 #include <AK/IntrusiveList.h>
 #include <AK/Vector.h>
 #include <LibJS/Forward.h>
-#include <LibJS/Heap/Cell.h>
+#include <LibJS/Heap/CellImpl.h>
 #include <LibJS/Heap/HeapRoot.h>
 
 namespace JS {

+ 2 - 2
Libraries/LibJS/Heap/Handle.cpp

@@ -4,13 +4,13 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
-#include <LibJS/Heap/Cell.h>
+#include <LibJS/Heap/CellImpl.h>
 #include <LibJS/Heap/Handle.h>
 #include <LibJS/Heap/Heap.h>
 
 namespace JS {
 
-HandleImpl::HandleImpl(Cell* cell, SourceLocation location)
+HandleImpl::HandleImpl(CellImpl* cell, SourceLocation location)
     : m_cell(cell)
     , m_location(location)
 {

+ 4 - 4
Libraries/LibJS/Heap/Handle.h

@@ -24,8 +24,8 @@ class HandleImpl : public RefCounted<HandleImpl> {
 public:
     ~HandleImpl();
 
-    Cell* cell() { return m_cell; }
-    Cell const* cell() const { return m_cell; }
+    CellImpl* cell() { return m_cell; }
+    CellImpl const* cell() const { return m_cell; }
 
     SourceLocation const& source_location() const { return m_location; }
 
@@ -33,8 +33,8 @@ private:
     template<class T>
     friend class Handle;
 
-    explicit HandleImpl(Cell*, SourceLocation location);
-    GCPtr<Cell> m_cell;
+    explicit HandleImpl(CellImpl*, SourceLocation location);
+    GCPtr<CellImpl> m_cell;
     SourceLocation m_location;
 
     IntrusiveListNode<HandleImpl> m_list_node;

+ 25 - 25
Libraries/LibJS/Heap/Heap.cpp

@@ -28,8 +28,8 @@
 
 namespace JS {
 
-Heap::Heap(VM& vm, Function<void(HashMap<Cell*, JS::HeapRoot>&)> gather_embedder_roots)
-    : HeapBase(vm)
+Heap::Heap(void* private_data, Function<void(HashMap<CellImpl*, JS::HeapRoot>&)> gather_embedder_roots)
+    : HeapBase(private_data)
     , m_gather_embedder_roots(move(gather_embedder_roots))
 {
     static_assert(HeapBlock::min_possible_cell_size <= 32, "Heap Cell tracking uses too much data!");
@@ -100,7 +100,7 @@ static void for_each_cell_among_possible_pointers(HashTable<HeapBlock*> const& a
     for (auto possible_pointer : possible_pointers.keys()) {
         if (!possible_pointer)
             continue;
-        auto* possible_heap_block = HeapBlock::from_cell(reinterpret_cast<Cell const*>(possible_pointer));
+        auto* possible_heap_block = HeapBlock::from_cell(reinterpret_cast<CellImpl const*>(possible_pointer));
         if (!all_live_heap_blocks.contains(possible_heap_block))
             continue;
         if (auto* cell = possible_heap_block->cell_from_possible_pointer(possible_pointer)) {
@@ -109,9 +109,9 @@ static void for_each_cell_among_possible_pointers(HashTable<HeapBlock*> const& a
     }
 }
 
-class GraphConstructorVisitor final : public Cell::Visitor {
+class GraphConstructorVisitor final : public CellImpl::Visitor {
 public:
-    explicit GraphConstructorVisitor(Heap& heap, HashMap<Cell*, HeapRoot> const& roots)
+    explicit GraphConstructorVisitor(Heap& heap, HashMap<CellImpl*, HeapRoot> const& roots)
         : m_heap(heap)
     {
         m_heap.find_min_and_max_block_addresses(m_min_block_address, m_max_block_address);
@@ -129,7 +129,7 @@ public:
         }
     }
 
-    virtual void visit_impl(Cell& cell) override
+    virtual void visit_impl(CellImpl& cell) override
     {
         if (m_node_being_visited)
             m_node_being_visited->edges.set(reinterpret_cast<FlatPtr>(&cell));
@@ -148,7 +148,7 @@ public:
         for (size_t i = 0; i < (bytes.size() / sizeof(FlatPtr)); ++i)
             add_possible_value(possible_pointers, raw_pointer_sized_values[i], HeapRoot { .type = HeapRoot::Type::HeapFunctionCapturedPointer }, m_min_block_address, m_max_block_address);
 
-        for_each_cell_among_possible_pointers(m_all_live_heap_blocks, possible_pointers, [&](Cell* cell, FlatPtr) {
+        for_each_cell_among_possible_pointers(m_all_live_heap_blocks, possible_pointers, [&](CellImpl* cell, FlatPtr) {
             if (m_node_being_visited)
                 m_node_being_visited->edges.set(reinterpret_cast<FlatPtr>(cell));
 
@@ -218,7 +218,7 @@ private:
     };
 
     GraphNode* m_node_being_visited { nullptr };
-    Vector<NonnullGCPtr<Cell>> m_work_queue;
+    Vector<NonnullGCPtr<CellImpl>> m_work_queue;
     HashMap<FlatPtr, GraphNode> m_graph;
 
     Heap& m_heap;
@@ -229,7 +229,7 @@ private:
 
 AK::JsonObject Heap::dump_graph()
 {
-    HashMap<Cell*, HeapRoot> roots;
+    HashMap<CellImpl*, HeapRoot> roots;
     gather_roots(roots);
     GraphConstructorVisitor visitor(*this, roots);
     visitor.visit_all_cells();
@@ -250,7 +250,7 @@ void Heap::collect_garbage(CollectionType collection_type, bool print_report)
             m_should_gc_when_deferral_ends = true;
             return;
         }
-        HashMap<Cell*, HeapRoot> roots;
+        HashMap<CellImpl*, HeapRoot> roots;
         gather_roots(roots);
         mark_live_cells(roots);
     }
@@ -258,7 +258,7 @@ void Heap::collect_garbage(CollectionType collection_type, bool print_report)
     sweep_dead_cells(print_report, collection_measurement_timer);
 }
 
-void Heap::gather_roots(HashMap<Cell*, HeapRoot>& roots)
+void Heap::gather_roots(HashMap<CellImpl*, HeapRoot>& roots)
 {
     m_gather_embedder_roots(roots);
     gather_conservative_roots(roots);
@@ -298,7 +298,7 @@ void Heap::gather_asan_fake_stack_roots(HashMap<FlatPtr, HeapRoot>&, FlatPtr, Fl
 }
 #endif
 
-NO_SANITIZE_ADDRESS void Heap::gather_conservative_roots(HashMap<Cell*, HeapRoot>& roots)
+NO_SANITIZE_ADDRESS void Heap::gather_conservative_roots(HashMap<CellImpl*, HeapRoot>& roots)
 {
     FlatPtr dummy;
 
@@ -337,8 +337,8 @@ NO_SANITIZE_ADDRESS void Heap::gather_conservative_roots(HashMap<Cell*, HeapRoot
         return IterationDecision::Continue;
     });
 
-    for_each_cell_among_possible_pointers(all_live_heap_blocks, possible_pointers, [&](Cell* cell, FlatPtr possible_pointer) {
-        if (cell->state() == Cell::State::Live) {
+    for_each_cell_among_possible_pointers(all_live_heap_blocks, possible_pointers, [&](CellImpl* cell, FlatPtr possible_pointer) {
+        if (cell->state() == CellImpl::State::Live) {
             dbgln_if(HEAP_DEBUG, "  ?-> {}", (void const*)cell);
             roots.set(cell, *possible_pointers.get(possible_pointer));
         } else {
@@ -347,9 +347,9 @@ NO_SANITIZE_ADDRESS void Heap::gather_conservative_roots(HashMap<Cell*, HeapRoot
     });
 }
 
-class MarkingVisitor final : public Cell::Visitor {
+class MarkingVisitor final : public CellImpl::Visitor {
 public:
-    explicit MarkingVisitor(Heap& heap, HashMap<Cell*, HeapRoot> const& roots)
+    explicit MarkingVisitor(Heap& heap, HashMap<CellImpl*, HeapRoot> const& roots)
         : m_heap(heap)
     {
         m_heap.find_min_and_max_block_addresses(m_min_block_address, m_max_block_address);
@@ -363,7 +363,7 @@ public:
         }
     }
 
-    virtual void visit_impl(Cell& cell) override
+    virtual void visit_impl(CellImpl& cell) override
     {
         if (cell.is_marked())
             return;
@@ -381,10 +381,10 @@ public:
         for (size_t i = 0; i < (bytes.size() / sizeof(FlatPtr)); ++i)
             add_possible_value(possible_pointers, raw_pointer_sized_values[i], HeapRoot { .type = HeapRoot::Type::HeapFunctionCapturedPointer }, m_min_block_address, m_max_block_address);
 
-        for_each_cell_among_possible_pointers(m_all_live_heap_blocks, possible_pointers, [&](Cell* cell, FlatPtr) {
+        for_each_cell_among_possible_pointers(m_all_live_heap_blocks, possible_pointers, [&](CellImpl* cell, FlatPtr) {
             if (cell->is_marked())
                 return;
-            if (cell->state() != Cell::State::Live)
+            if (cell->state() != CellImpl::State::Live)
                 return;
             cell->set_marked(true);
             m_work_queue.append(*cell);
@@ -400,13 +400,13 @@ public:
 
 private:
     Heap& m_heap;
-    Vector<NonnullGCPtr<Cell>> m_work_queue;
+    Vector<NonnullGCPtr<CellImpl>> m_work_queue;
     HashTable<HeapBlock*> m_all_live_heap_blocks;
     FlatPtr m_min_block_address;
     FlatPtr m_max_block_address;
 };
 
-void Heap::mark_live_cells(HashMap<Cell*, HeapRoot> const& roots)
+void Heap::mark_live_cells(HashMap<CellImpl*, HeapRoot> const& roots)
 {
     dbgln_if(HEAP_DEBUG, "mark_live_cells:");
 
@@ -420,7 +420,7 @@ void Heap::mark_live_cells(HashMap<Cell*, HeapRoot> const& roots)
     m_uprooted_cells.clear();
 }
 
-bool Heap::cell_must_survive_garbage_collection(Cell const& cell)
+bool Heap::cell_must_survive_garbage_collection(CellImpl const& cell)
 {
     if (!cell.overrides_must_survive_garbage_collection({}))
         return false;
@@ -430,7 +430,7 @@ bool Heap::cell_must_survive_garbage_collection(Cell const& cell)
 void Heap::finalize_unmarked_cells()
 {
     for_each_block([&](auto& block) {
-        block.template for_each_cell_in_state<Cell::State::Live>([](Cell* cell) {
+        block.template for_each_cell_in_state<CellImpl::State::Live>([](CellImpl* cell) {
             if (!cell->is_marked() && !cell_must_survive_garbage_collection(*cell))
                 cell->finalize();
         });
@@ -452,7 +452,7 @@ void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measure
     for_each_block([&](auto& block) {
         bool block_has_live_cells = false;
         bool block_was_full = block.is_full();
-        block.template for_each_cell_in_state<Cell::State::Live>([&](Cell* cell) {
+        block.template for_each_cell_in_state<CellImpl::State::Live>([&](CellImpl* cell) {
             if (!cell->is_marked() && !cell_must_survive_garbage_collection(*cell)) {
                 dbgln_if(HEAP_DEBUG, "  ~ {}", cell);
                 block.deallocate(cell);
@@ -530,7 +530,7 @@ void Heap::undefer_gc()
     }
 }
 
-void Heap::uproot_cell(Cell* cell)
+void Heap::uproot_cell(CellImpl* cell)
 {
     m_uprooted_cells.append(cell);
 }

+ 10 - 10
Libraries/LibJS/Heap/Heap.h

@@ -17,8 +17,8 @@
 #include <AK/Vector.h>
 #include <LibCore/Forward.h>
 #include <LibJS/Forward.h>
-#include <LibJS/Heap/Cell.h>
 #include <LibJS/Heap/CellAllocator.h>
+#include <LibJS/Heap/CellImpl.h>
 #include <LibJS/Heap/ConservativeVector.h>
 #include <LibJS/Heap/Handle.h>
 #include <LibJS/Heap/HeapRoot.h>
@@ -33,7 +33,7 @@ class Heap : public HeapBase {
     AK_MAKE_NONMOVABLE(Heap);
 
 public:
-    explicit Heap(VM&, Function<void(HashMap<Cell*, JS::HeapRoot>&)> gather_embedder_roots);
+    explicit Heap(void* private_data, Function<void(HashMap<CellImpl*, JS::HeapRoot>&)> gather_embedder_roots);
     ~Heap();
 
     template<typename T, typename... Args>
@@ -71,7 +71,7 @@ public:
 
     void register_cell_allocator(Badge<CellAllocator>, CellAllocator&);
 
-    void uproot_cell(Cell* cell);
+    void uproot_cell(CellImpl* cell);
 
 private:
     friend class MarkingVisitor;
@@ -81,10 +81,10 @@ private:
     void defer_gc();
     void undefer_gc();
 
-    static bool cell_must_survive_garbage_collection(Cell const&);
+    static bool cell_must_survive_garbage_collection(CellImpl const&);
 
     template<typename T>
-    Cell* allocate_cell()
+    CellImpl* allocate_cell()
     {
         will_allocate(sizeof(T));
         if constexpr (requires { T::cell_allocator.allocator.get().allocate_cell(*this); }) {
@@ -98,10 +98,10 @@ private:
     void will_allocate(size_t);
 
     void find_min_and_max_block_addresses(FlatPtr& min_address, FlatPtr& max_address);
-    void gather_roots(HashMap<Cell*, HeapRoot>&);
-    void gather_conservative_roots(HashMap<Cell*, HeapRoot>&);
+    void gather_roots(HashMap<CellImpl*, HeapRoot>&);
+    void gather_conservative_roots(HashMap<CellImpl*, HeapRoot>&);
     void gather_asan_fake_stack_roots(HashMap<FlatPtr, HeapRoot>&, FlatPtr, FlatPtr min_block_address, FlatPtr max_block_address);
-    void mark_live_cells(HashMap<Cell*, HeapRoot> const& live_cells);
+    void mark_live_cells(HashMap<CellImpl*, HeapRoot> const& live_cells);
     void finalize_unmarked_cells();
     void sweep_dead_cells(bool print_report, Core::ElapsedTimer const&);
 
@@ -139,14 +139,14 @@ private:
     ConservativeVectorBase::List m_conservative_vectors;
     WeakContainer::List m_weak_containers;
 
-    Vector<GCPtr<Cell>> m_uprooted_cells;
+    Vector<GCPtr<CellImpl>> m_uprooted_cells;
 
     size_t m_gc_deferrals { 0 };
     bool m_should_gc_when_deferral_ends { false };
 
     bool m_collecting_garbage { false };
     StackInfo m_stack_info;
-    Function<void(HashMap<Cell*, JS::HeapRoot>&)> m_gather_embedder_roots;
+    Function<void(HashMap<CellImpl*, JS::HeapRoot>&)> m_gather_embedder_roots;
 };
 
 inline void Heap::did_create_handle(Badge<HandleImpl>, HandleImpl& impl)

+ 4 - 4
Libraries/LibJS/Heap/HeapBlock.cpp

@@ -37,16 +37,16 @@ HeapBlock::HeapBlock(Heap& heap, CellAllocator& cell_allocator, size_t cell_size
     ASAN_POISON_MEMORY_REGION(m_storage, block_size - sizeof(HeapBlock));
 }
 
-void HeapBlock::deallocate(Cell* cell)
+void HeapBlock::deallocate(CellImpl* cell)
 {
     VERIFY(is_valid_cell_pointer(cell));
     VERIFY(!m_freelist || is_valid_cell_pointer(m_freelist));
-    VERIFY(cell->state() == Cell::State::Live);
+    VERIFY(cell->state() == CellImpl::State::Live);
     VERIFY(!cell->is_marked());
 
-    cell->~Cell();
+    cell->~CellImpl();
     auto* freelist_entry = new (cell) FreelistEntry();
-    freelist_entry->set_state(Cell::State::Dead);
+    freelist_entry->set_state(CellImpl::State::Dead);
     freelist_entry->next = m_freelist;
     m_freelist = freelist_entry;
 

+ 12 - 12
Libraries/LibJS/Heap/HeapBlock.h

@@ -11,7 +11,7 @@
 #include <AK/StringView.h>
 #include <AK/Types.h>
 #include <LibJS/Forward.h>
-#include <LibJS/Heap/Cell.h>
+#include <LibJS/Heap/CellImpl.h>
 #include <LibJS/Heap/Internals.h>
 
 #ifdef HAS_ADDRESS_SANITIZER
@@ -32,9 +32,9 @@ public:
     size_t cell_count() const { return (block_size - sizeof(HeapBlock)) / m_cell_size; }
     bool is_full() const { return !has_lazy_freelist() && !m_freelist; }
 
-    ALWAYS_INLINE Cell* allocate()
+    ALWAYS_INLINE CellImpl* allocate()
     {
-        Cell* allocated_cell = nullptr;
+        CellImpl* allocated_cell = nullptr;
         if (m_freelist) {
             VERIFY(is_valid_cell_pointer(m_freelist));
             allocated_cell = exchange(m_freelist, m_freelist->next);
@@ -48,7 +48,7 @@ public:
         return allocated_cell;
     }
 
-    void deallocate(Cell*);
+    void deallocate(CellImpl*);
 
     template<typename Callback>
     void for_each_cell(Callback callback)
@@ -58,7 +58,7 @@ public:
             callback(cell(i));
     }
 
-    template<Cell::State state, typename Callback>
+    template<CellImpl::State state, typename Callback>
     void for_each_cell_in_state(Callback callback)
     {
         for_each_cell([&](auto* cell) {
@@ -67,12 +67,12 @@ public:
         });
     }
 
-    static HeapBlock* from_cell(Cell const* cell)
+    static HeapBlock* from_cell(CellImpl const* cell)
     {
         return static_cast<HeapBlock*>(HeapBlockBase::from_cell(cell));
     }
 
-    Cell* cell_from_possible_pointer(FlatPtr pointer)
+    CellImpl* cell_from_possible_pointer(FlatPtr pointer)
     {
         if (pointer < reinterpret_cast<FlatPtr>(m_storage))
             return nullptr;
@@ -83,7 +83,7 @@ public:
         return cell(cell_index);
     }
 
-    bool is_valid_cell_pointer(Cell const* cell)
+    bool is_valid_cell_pointer(CellImpl const* cell)
     {
         return cell_from_possible_pointer((FlatPtr)cell);
     }
@@ -97,15 +97,15 @@ private:
 
     bool has_lazy_freelist() const { return m_next_lazy_freelist_index < cell_count(); }
 
-    struct FreelistEntry final : public Cell {
-        JS_CELL(FreelistEntry, Cell);
+    struct FreelistEntry final : public CellImpl {
+        JS_CELL(FreelistEntry, CellImpl);
 
         RawGCPtr<FreelistEntry> next;
     };
 
-    Cell* cell(size_t index)
+    CellImpl* cell(size_t index)
     {
-        return reinterpret_cast<Cell*>(&m_storage[index * cell_size()]);
+        return reinterpret_cast<CellImpl*>(&m_storage[index * cell_size()]);
     }
 
     CellAllocator& m_cell_allocator;

+ 3 - 3
Libraries/LibJS/Heap/HeapFunction.h

@@ -7,14 +7,14 @@
 #pragma once
 
 #include <AK/Function.h>
-#include <LibJS/Heap/Cell.h>
+#include <LibJS/Heap/CellImpl.h>
 #include <LibJS/Heap/Heap.h>
 
 namespace JS {
 
 template<typename T>
-class HeapFunction final : public Cell {
-    JS_CELL(HeapFunction, Cell);
+class HeapFunction final : public CellImpl {
+    JS_CELL(HeapFunction, CellImpl);
 
 public:
     static NonnullGCPtr<HeapFunction> create(Heap& heap, Function<T> function)

+ 5 - 5
Libraries/LibJS/Heap/Internals.h

@@ -17,15 +17,15 @@ class HeapBase {
     AK_MAKE_NONMOVABLE(HeapBase);
 
 public:
-    VM& vm() { return m_vm; }
+    void* private_data() { return m_private_data; }
 
 protected:
-    HeapBase(VM& vm)
-        : m_vm(vm)
+    explicit HeapBase(void* private_data)
+        : m_private_data(private_data)
     {
     }
 
-    VM& m_vm;
+    void* m_private_data;
 };
 
 class HeapBlockBase {
@@ -34,7 +34,7 @@ class HeapBlockBase {
 
 public:
     static size_t block_size;
-    static HeapBlockBase* from_cell(Cell const* cell)
+    static HeapBlockBase* from_cell(CellImpl const* cell)
     {
         return reinterpret_cast<HeapBlockBase*>(bit_cast<FlatPtr>(cell) & ~(HeapBlockBase::block_size - 1));
     }

+ 3 - 3
Libraries/LibJS/Heap/MarkedVector.h

@@ -11,14 +11,14 @@
 #include <AK/IntrusiveList.h>
 #include <AK/Vector.h>
 #include <LibJS/Forward.h>
-#include <LibJS/Heap/Cell.h>
+#include <LibJS/Heap/CellImpl.h>
 #include <LibJS/Heap/HeapRoot.h>
 
 namespace JS {
 
 class MarkedVectorBase {
 public:
-    virtual void gather_roots(HashMap<Cell*, JS::HeapRoot>&) const = 0;
+    virtual void gather_roots(HashMap<CellImpl*, JS::HeapRoot>&) const = 0;
 
 protected:
     explicit MarkedVectorBase(Heap&);
@@ -65,7 +65,7 @@ public:
         return *this;
     }
 
-    virtual void gather_roots(HashMap<Cell*, JS::HeapRoot>& roots) const override
+    virtual void gather_roots(HashMap<CellImpl*, JS::HeapRoot>& roots) const override
     {
         for (auto& value : *this) {
             if constexpr (IsSame<Value, T>) {

+ 4 - 4
Libraries/LibJS/Heap/NanBoxedValue.h

@@ -86,16 +86,16 @@ public:
         return reinterpret_cast<PointerType*>(extract_pointer_bits(m_value.encoded));
     }
 
-    Cell& as_cell()
+    CellImpl& as_cell()
     {
         VERIFY(is_cell());
-        return *extract_pointer<Cell>();
+        return *extract_pointer<CellImpl>();
     }
 
-    Cell& as_cell() const
+    CellImpl& as_cell() const
     {
         VERIFY(is_cell());
-        return *extract_pointer<Cell>();
+        return *extract_pointer<CellImpl>();
     }
 
     bool is_nan() const

+ 4 - 4
Libraries/LibJS/Runtime/VM.cpp

@@ -62,7 +62,7 @@ static constexpr auto make_single_ascii_character_strings(IndexSequence<code_poi
 static constexpr auto single_ascii_character_strings = make_single_ascii_character_strings(MakeIndexSequence<128>());
 
 VM::VM(OwnPtr<CustomData> custom_data, ErrorMessages error_messages)
-    : m_heap(*this, [this](HashMap<Cell*, JS::HeapRoot>& roots) {
+    : m_heap(this, [this](HashMap<CellImpl*, JS::HeapRoot>& roots) {
         gather_roots(roots);
     })
     , m_error_messages(move(error_messages))
@@ -204,7 +204,7 @@ Bytecode::Interpreter& VM::bytecode_interpreter()
 }
 
 struct ExecutionContextRootsCollector : public Cell::Visitor {
-    virtual void visit_impl(Cell& cell) override
+    virtual void visit_impl(CellImpl& cell) override
     {
         roots.set(&cell);
     }
@@ -214,10 +214,10 @@ struct ExecutionContextRootsCollector : public Cell::Visitor {
         VERIFY_NOT_REACHED();
     }
 
-    HashTable<GCPtr<Cell>> roots;
+    HashTable<GCPtr<CellImpl>> roots;
 };
 
-void VM::gather_roots(HashMap<Cell*, HeapRoot>& roots)
+void VM::gather_roots(HashMap<CellImpl*, HeapRoot>& roots)
 {
     roots.set(m_empty_string, HeapRoot { .type = HeapRoot::Type::VM });
     for (auto string : m_single_ascii_character_strings)

+ 1 - 1
Libraries/LibJS/Runtime/VM.h

@@ -60,7 +60,7 @@ public:
 
     void dump_backtrace() const;
 
-    void gather_roots(HashMap<Cell*, HeapRoot>&);
+    void gather_roots(HashMap<CellImpl*, HeapRoot>&);
 
 #define __JS_ENUMERATE(SymbolName, snake_name)                  \
     NonnullGCPtr<Symbol> well_known_symbol_##snake_name() const \

+ 16 - 3
Libraries/LibJS/Runtime/Value.h

@@ -19,6 +19,7 @@
 #include <AK/String.h>
 #include <AK/Types.h>
 #include <LibJS/Forward.h>
+#include <LibJS/Heap/Cell.h>
 #include <LibJS/Heap/GCPtr.h>
 #include <LibJS/Heap/Handle.h>
 #include <LibJS/Heap/NanBoxedValue.h>
@@ -256,6 +257,18 @@ public:
     {
     }
 
+    Cell& as_cell()
+    {
+        VERIFY(is_cell());
+        return *extract_pointer<Cell>();
+    }
+
+    Cell& as_cell() const
+    {
+        VERIFY(is_cell());
+        return *extract_pointer<Cell>();
+    }
+
     double as_double() const
     {
         VERIFY(is_number());
@@ -661,14 +674,14 @@ private:
     {
     }
 
-    explicit Handle(Value value, Cell* cell, SourceLocation location)
+    explicit Handle(Value value, CellImpl* cell, SourceLocation location)
         : m_value(value)
-        , m_handle(Handle<Cell>::create(cell, location))
+        , m_handle(Handle<CellImpl>::create(cell, location))
     {
     }
 
     Optional<Value> m_value;
-    Handle<Cell> m_handle;
+    Handle<CellImpl> m_handle;
 };
 
 inline Handle<Value> make_handle(Value value, SourceLocation location = SourceLocation::current())

+ 1 - 0
Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h

@@ -14,6 +14,7 @@
 #include <AK/String.h>
 #include <AK/Vector.h>
 #include <LibJS/Forward.h>
+#include <LibJS/Heap/Cell.h>
 #include <LibJS/Heap/GCPtr.h>
 #include <LibJS/Heap/Heap.h>
 #include <LibWeb/MimeSniff/MimeType.h>

+ 1 - 0
Libraries/LibWeb/HTML/AnimationFrameCallbackDriver.h

@@ -9,6 +9,7 @@
 #pragma once
 
 #include <AK/HashMap.h>
+#include <LibJS/Heap/Cell.h>
 #include <LibJS/Heap/GCPtr.h>
 #include <LibJS/Heap/HeapFunction.h>
 #include <LibWeb/WebIDL/Types.h>

+ 1 - 0
Libraries/LibWeb/WebDriver/HeapTimer.h

@@ -7,6 +7,7 @@
 #pragma once
 
 #include <LibCore/Forward.h>
+#include <LibJS/Heap/Cell.h>
 #include <LibJS/Heap/GCPtr.h>
 #include <LibJS/Heap/HeapFunction.h>
 

+ 7 - 7
Meta/Lagom/ClangPlugins/LibJSGCPluginAction.cpp

@@ -39,9 +39,9 @@ static bool record_inherits_from_cell(clang::CXXRecordDecl const& record)
     if (!record.isCompleteDefinition())
         return false;
 
-    bool inherits_from_cell = record.getQualifiedNameAsString() == "JS::Cell";
+    bool inherits_from_cell = record.getQualifiedNameAsString() == "JS::CellImpl";
     record.forallBases([&](clang::CXXRecordDecl const* base) -> bool {
-        if (base->getQualifiedNameAsString() == "JS::Cell") {
+        if (base->getQualifiedNameAsString() == "JS::CellImpl") {
             inherits_from_cell = true;
             return false;
         }
@@ -173,7 +173,7 @@ bool LibJSGCVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl* record)
     // Cell triggers a bunch of warnings for its empty visit_edges implementation, but
     // it doesn't have any members anyways so it's fine to just ignore.
     auto qualified_name = record->getQualifiedNameAsString();
-    if (qualified_name == "JS::Cell")
+    if (qualified_name == "JS::CellImpl")
         return true;
 
     auto& diag_engine = m_context.getDiagnostics();
@@ -192,7 +192,7 @@ bool LibJSGCVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl* record)
 
         if (outer_type == OuterType::Ptr || outer_type == OuterType::Ref) {
             if (base_type_inherits_from_cell) {
-                auto diag_id = diag_engine.getCustomDiagID(clang::DiagnosticsEngine::Error, "%0 to JS::Cell type should be wrapped in %1");
+                auto diag_id = diag_engine.getCustomDiagID(clang::DiagnosticsEngine::Error, "%0 to JS::CellImpl type should be wrapped in %1");
                 auto builder = diag_engine.Report(field->getLocation(), diag_id);
                 if (outer_type == OuterType::Ref) {
                     builder << "reference"
@@ -204,7 +204,7 @@ bool LibJSGCVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl* record)
             }
         } else if (outer_type == OuterType::GCPtr || outer_type == OuterType::RawGCPtr) {
             if (!base_type_inherits_from_cell) {
-                auto diag_id = diag_engine.getCustomDiagID(clang::DiagnosticsEngine::Error, "Specialization type must inherit from JS::Cell");
+                auto diag_id = diag_engine.getCustomDiagID(clang::DiagnosticsEngine::Error, "Specialization type must inherit from JS::CellImpl");
                 diag_engine.Report(field->getLocation(), diag_id);
             } else if (outer_type == OuterType::GCPtr) {
                 fields_that_need_visiting.push_back(field);
@@ -212,7 +212,7 @@ bool LibJSGCVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl* record)
         } else if (outer_type == OuterType::Handle) {
             if (record_is_cell && m_detect_invalid_function_members) {
                 // FIXME: Change this to an Error when all of the use cases get addressed and remove the plugin argument
-                auto diag_id = diag_engine.getCustomDiagID(clang::DiagnosticsEngine::Warning, "Types inheriting from JS::Cell should not have %0 fields");
+                auto diag_id = diag_engine.getCustomDiagID(clang::DiagnosticsEngine::Warning, "Types inheriting from JS::CellImpl should not have %0 fields");
                 auto builder = diag_engine.Report(field->getLocation(), diag_id);
                 builder << "JS::Handle";
             }
@@ -311,7 +311,7 @@ static std::optional<CellTypeWithOrigin> find_cell_type_with_origin(clang::CXXRe
         if (auto const* base_record = base.getType()->getAsCXXRecordDecl()) {
             auto base_name = base_record->getQualifiedNameAsString();
 
-            if (base_name == "JS::Cell")
+            if (base_name == "JS::CellImpl")
                 return CellTypeWithOrigin { *base_record, LibJSCellMacro::Type::JSCell };
 
             if (base_name == "JS::Object")

+ 10 - 10
Tests/ClangPlugins/LibJSGCTests/cell_member_not_wrapped.cpp

@@ -30,15 +30,15 @@ private:
         visitor.visit(m_object_ptr);
     }
 
-    // expected-error@+1 {{reference to JS::Cell type should be wrapped in JS::NonnullGCPtr}}
+    // expected-error@+1 {{reference to JS::CellImpl type should be wrapped in JS::NonnullGCPtr}}
     JS::Object& m_object_ref;
-    // expected-error@+1 {{pointer to JS::Cell type should be wrapped in JS::GCPtr}}
+    // expected-error@+1 {{pointer to JS::CellImpl type should be wrapped in JS::GCPtr}}
     JS::Object* m_object_ptr;
-    // expected-error@+1 {{pointer to JS::Cell type should be wrapped in JS::GCPtr}}
+    // expected-error@+1 {{pointer to JS::CellImpl type should be wrapped in JS::GCPtr}}
     Vector<JS::Object*> m_objects;
-    // expected-error@+1 {{pointer to JS::Cell type should be wrapped in JS::GCPtr}}
+    // expected-error@+1 {{pointer to JS::CellImpl type should be wrapped in JS::GCPtr}}
     NewType1* m_newtype_1;
-    // expected-error@+1 {{pointer to JS::Cell type should be wrapped in JS::GCPtr}}
+    // expected-error@+1 {{pointer to JS::CellImpl type should be wrapped in JS::GCPtr}}
     NewType2* m_newtype_2;
 };
 
@@ -50,14 +50,14 @@ public:
     }
 
 private:
-    // expected-error@+1 {{reference to JS::Cell type should be wrapped in JS::NonnullGCPtr}}
+    // expected-error@+1 {{reference to JS::CellImpl type should be wrapped in JS::NonnullGCPtr}}
     JS::Object& m_object_ref;
-    // expected-error@+1 {{pointer to JS::Cell type should be wrapped in JS::GCPtr}}
+    // expected-error@+1 {{pointer to JS::CellImpl type should be wrapped in JS::GCPtr}}
     JS::Object* m_object_ptr;
-    // expected-error@+1 {{pointer to JS::Cell type should be wrapped in JS::GCPtr}}
+    // expected-error@+1 {{pointer to JS::CellImpl type should be wrapped in JS::GCPtr}}
     Vector<JS::Object*> m_objects;
-    // expected-error@+1 {{pointer to JS::Cell type should be wrapped in JS::GCPtr}}
+    // expected-error@+1 {{pointer to JS::CellImpl type should be wrapped in JS::GCPtr}}
     NewType1* m_newtype_1;
-    // expected-error@+1 {{pointer to JS::Cell type should be wrapped in JS::GCPtr}}
+    // expected-error@+1 {{pointer to JS::CellImpl type should be wrapped in JS::GCPtr}}
     NewType2* m_newtype_2;
 };