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&.
This commit is contained in:
Shannon Booth 2024-11-14 20:22:33 +13:00 committed by Andreas Kling
parent ae6d105f41
commit c2988a7dd5
Notes: github-actions[bot] 2024-11-14 14:39:37 +00:00
27 changed files with 346 additions and 296 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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