diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 49bc943ec4..b6862f594c 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -39,7 +39,7 @@ Value ScopeNode::execute(Interpreter& interpreter) const Value FunctionDeclaration::execute(Interpreter& interpreter) const { - auto* function = new Function(name(), body()); + auto* function = interpreter.heap().allocate(name(), body()); interpreter.global_object().put(m_name, Value(function)); return Value(function); } diff --git a/Libraries/LibJS/Cell.cpp b/Libraries/LibJS/Cell.cpp new file mode 100644 index 0000000000..3aedb7cb66 --- /dev/null +++ b/Libraries/LibJS/Cell.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace JS { + +const LogStream& operator<<(const LogStream& stream, const Cell* cell) +{ + if (!cell) + return stream << "Cell{nullptr}"; + return stream << cell->class_name() << '{' << static_cast(cell) << '}'; +} + +} diff --git a/Libraries/LibJS/Cell.h b/Libraries/LibJS/Cell.h new file mode 100644 index 0000000000..31347f761b --- /dev/null +++ b/Libraries/LibJS/Cell.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace JS { + +class Cell { +public: + virtual ~Cell() {} + + bool is_marked() const { return m_mark; } + void set_marked(bool b) { m_mark = b; } + + bool is_live() const { return m_live; } + void set_live(bool b) { m_live = b; } + + virtual const char* class_name() const = 0; + + class Visitor { + public: + virtual void did_visit(Cell*) = 0; + }; + + virtual void visit_graph(Visitor& visitor) + { + visitor.did_visit(this); + } + +private: + bool m_mark { false }; + bool m_live { true }; +}; + +const LogStream& operator<<(const LogStream&, const Cell*); + +} diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index d7ca20d622..ee5494da5d 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -29,6 +29,9 @@ namespace JS { class ASTNode; +class Cell; +class Heap; +class HeapBlock; class Interpreter; class Object; class ScopeNode; diff --git a/Libraries/LibJS/Heap.cpp b/Libraries/LibJS/Heap.cpp new file mode 100644 index 0000000000..e3a53da2f9 --- /dev/null +++ b/Libraries/LibJS/Heap.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#define HEAP_DEBUG + +namespace JS { + +Heap::Heap(Interpreter& interpreter) + : m_interpreter(interpreter) +{ +} + +Heap::~Heap() +{ +} + +Cell* Heap::allocate_cell(size_t size) +{ + for (auto& block : m_blocks) { + if (size > block->cell_size()) + continue; + if (auto* cell = block->allocate()) + return cell; + } + + auto* block = (HeapBlock*)malloc(HeapBlock::block_size); + new (block) HeapBlock(size); + m_blocks.append(NonnullOwnPtr(NonnullOwnPtr::Adopt, *block)); + return block->allocate(); +} + +void Heap::collect_garbage() +{ + HashTable roots; + collect_roots(roots); + + HashTable live_cells; + visit_live_cells(roots, live_cells); + + clear_all_mark_bits(); + mark_live_cells(live_cells); + sweep_dead_cells(); +} + +void Heap::collect_roots(HashTable& roots) +{ + roots.set(&m_interpreter.global_object()); + +#ifdef HEAP_DEBUG + dbg() << "collect_roots:"; + for (auto* root : roots) { + dbg() << " + " << root; + } +#endif +} + +class LivenessVisitor final : public Cell::Visitor { +public: + LivenessVisitor(HashTable& live_cells) + : m_live_cells(live_cells) + { + } + + virtual void did_visit(Cell* cell) override + { + m_live_cells.set(cell); + } + +private: + HashTable& m_live_cells; +}; + +void Heap::visit_live_cells(const HashTable& roots, HashTable& live_cells) +{ + LivenessVisitor visitor(live_cells); + for (auto* root : roots) { + root->visit_graph(visitor); + } + +#ifdef HEAP_DEBUG + dbg() << "visit_live_cells:"; + for (auto* cell : live_cells) { + dbg() << " @ " << cell; + } +#endif +} + +void Heap::clear_all_mark_bits() +{ + for (auto& block : m_blocks) { + block->for_each_cell([&](Cell* cell) { + cell->set_marked(false); + }); + } +} + +void Heap::mark_live_cells(const HashTable& live_cells) +{ +#ifdef HEAP_DEBUG + dbg() << "mark_live_cells:"; +#endif + for (auto& block : m_blocks) { + block->for_each_cell([&](Cell* cell) { + if (live_cells.contains(cell)) { +#ifdef HEAP_DEBUG + dbg() << " ! " << cell; +#endif + cell->set_marked(true); + } + }); + } +} + +void Heap::sweep_dead_cells() +{ +#ifdef HEAP_DEBUG + dbg() << "sweep_dead_cells:"; +#endif + for (auto& block : m_blocks) { + block->for_each_cell([&](Cell* cell) { + if (cell->is_live() && !cell->is_marked()) { +#ifdef HEAP_DEBUG + dbg() << " ~ " << cell; +#endif + block->deallocate(cell); + } + }); + } +} + +} diff --git a/Libraries/LibJS/Heap.h b/Libraries/LibJS/Heap.h new file mode 100644 index 0000000000..3e29a452ea --- /dev/null +++ b/Libraries/LibJS/Heap.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace JS { + +class Heap { + AK_MAKE_NONCOPYABLE(Heap); + AK_MAKE_NONMOVABLE(Heap); + +public: + explicit Heap(Interpreter&); + ~Heap(); + + template + T* allocate(Args&&... args) + { + auto* memory = allocate_cell(sizeof(T)); + new (memory) T(forward(args)...); + return static_cast(memory); + } + + void collect_garbage(); + +private: + Cell* allocate_cell(size_t); + + void collect_roots(HashTable&); + void visit_live_cells(const HashTable& roots, HashTable& live_cells); + void clear_all_mark_bits(); + void mark_live_cells(const HashTable& live_cells); + void sweep_dead_cells(); + + Interpreter& m_interpreter; + Vector> m_blocks; +}; + +} diff --git a/Libraries/LibJS/HeapBlock.cpp b/Libraries/LibJS/HeapBlock.cpp new file mode 100644 index 0000000000..016726e90b --- /dev/null +++ b/Libraries/LibJS/HeapBlock.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace JS { + +HeapBlock::HeapBlock(size_t cell_size) + : m_cell_size(cell_size) +{ + for (size_t i = 0; i < cell_count(); ++i) { + auto* freelist_entry = static_cast(cell(i)); + freelist_entry->set_live(false); + if (i == cell_count() - 1) + freelist_entry->next = nullptr; + else + freelist_entry->next = static_cast(cell(i + 1)); + } + m_freelist = static_cast(cell(0)); +} + +Cell* HeapBlock::allocate() +{ + if (!m_freelist) + return nullptr; + return exchange(m_freelist, m_freelist->next); +} + +void HeapBlock::deallocate(Cell* cell) +{ + ASSERT(cell->is_live()); + ASSERT(!cell->is_marked()); + cell->~Cell(); + auto* freelist_entry = static_cast(cell); + freelist_entry->set_live(false); + freelist_entry->next = m_freelist; + m_freelist = freelist_entry; +} + +} diff --git a/Libraries/LibJS/HeapBlock.h b/Libraries/LibJS/HeapBlock.h new file mode 100644 index 0000000000..d2bd5c42de --- /dev/null +++ b/Libraries/LibJS/HeapBlock.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include + +namespace JS { + +class HeapBlock { +public: + static constexpr size_t block_size = 16 * KB; + + explicit HeapBlock(size_t cell_size); + + size_t cell_size() const { return m_cell_size; } + size_t cell_count() const { return (block_size - sizeof(HeapBlock)) / m_cell_size; } + + Cell* cell(size_t index) { return reinterpret_cast(&m_storage[index * cell_size()]); } + + Cell* allocate(); + void deallocate(Cell*); + + template + void for_each_cell(Callback callback) + { + for (size_t i = 0; i < cell_count(); ++i) + callback(cell(i)); + } + +private: + struct FreelistEntry : public Cell { + FreelistEntry* next; + }; + + size_t m_cell_size { 0 }; + FreelistEntry* m_freelist { nullptr }; + u8 m_storage[]; +}; + +} diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index aabac0771a..102ee10ed1 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -32,8 +32,9 @@ namespace JS { Interpreter::Interpreter() + : m_heap(*this) { - m_global_object = new Object; + m_global_object = heap().allocate(); } Interpreter::~Interpreter() diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index cdc4bda5a7..3de8e0a99e 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -28,6 +28,7 @@ #include #include +#include namespace JS { @@ -45,12 +46,16 @@ public: Object& global_object() { return *m_global_object; } const Object& global_object() const { return *m_global_object; } + Heap& heap() { return m_heap; } + void do_return(); private: void enter_scope(const ScopeNode&); void exit_scope(const ScopeNode&); + Heap m_heap; + Vector m_scope_stack; Object* m_global_object { nullptr }; diff --git a/Libraries/LibJS/Makefile b/Libraries/LibJS/Makefile index af5a558be4..de00553990 100644 --- a/Libraries/LibJS/Makefile +++ b/Libraries/LibJS/Makefile @@ -1,6 +1,9 @@ OBJS = \ AST.o \ + Cell.o \ Function.o \ + Heap.o \ + HeapBlock.o \ Interpreter.o \ Object.o \ Value.o diff --git a/Libraries/LibJS/Object.cpp b/Libraries/LibJS/Object.cpp index e7359807be..ae96485d00 100644 --- a/Libraries/LibJS/Object.cpp +++ b/Libraries/LibJS/Object.cpp @@ -30,6 +30,14 @@ namespace JS { +Object::Object() +{ +} + +Object::~Object() +{ +} + Value Object::get(String property_name) const { return m_properties.get(property_name).value_or(js_undefined()); @@ -40,4 +48,13 @@ void Object::put(String property_name, Value value) m_properties.set(property_name, move(value)); } +void Object::visit_graph(Cell::Visitor& visitor) +{ + Cell::visit_graph(visitor); + for (auto& it : m_properties) { + if (it.value.is_object()) + it.value.as_object()->visit_graph(visitor); + } +} + } diff --git a/Libraries/LibJS/Object.h b/Libraries/LibJS/Object.h index 7c1697b8c8..1f9ce7e018 100644 --- a/Libraries/LibJS/Object.h +++ b/Libraries/LibJS/Object.h @@ -27,21 +27,24 @@ #pragma once #include +#include #include +#include namespace JS { -class Object { +class Object : public Cell { public: - Object() {} - virtual ~Object() {} + Object(); + virtual ~Object(); Value get(String property_name) const; void put(String property_name, Value); virtual bool is_function() const { return false; } - virtual const char* class_name() const { return "Object"; } + virtual const char* class_name() const override { return "Object"; } + virtual void visit_graph(Cell::Visitor&) override; private: HashMap m_properties;