diff options
author | Andreas Kling <kling@serenityos.org> | 2020-09-20 19:24:44 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-20 19:24:44 +0200 |
commit | 1c43442be46926a572059d4aad6d8f3a7e95d3b2 (patch) | |
tree | d7c58db53782d143eec803f07657b9ca758c6ecd /Libraries/LibJS | |
parent | c6ae0c41d908ae59a858a28a737b4382cceaeb6c (diff) | |
download | serenity-1c43442be46926a572059d4aad6d8f3a7e95d3b2.zip |
LibJS+Clients: Add JS::VM object, separate Heap from Interpreter
Taking a big step towards a world of multiple global object, this patch
adds a new JS::VM object that houses the JS::Heap.
This means that the Heap moves out of Interpreter, and the same Heap
can now be used by multiple Interpreters, and can also outlive them.
The VM keeps a stack of Interpreter pointers. We push/pop on this
stack when entering/exiting execution with a given Interpreter.
This allows us to make this change without disturbing too much of
the existing code.
There is still a 1-to-1 relationship between Interpreter and the
global object. This will change in the future.
Ultimately, the goal here is to make Interpreter a transient object
that only needs to exist while you execute some code. Getting there
will take a lot more work though. :^)
Note that in LibWeb, the global JS::VM is called main_thread_vm(),
to distinguish it from future worker VM's.
Diffstat (limited to 'Libraries/LibJS')
-rw-r--r-- | Libraries/LibJS/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Forward.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Heap/Heap.cpp | 12 | ||||
-rw-r--r-- | Libraries/LibJS/Heap/Heap.h | 7 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.cpp | 13 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.h | 21 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/GlobalObject.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/VM.cpp | 85 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/VM.h | 63 |
9 files changed, 187 insertions, 22 deletions
diff --git a/Libraries/LibJS/CMakeLists.txt b/Libraries/LibJS/CMakeLists.txt index 7b121cd146..c51da6bf75 100644 --- a/Libraries/LibJS/CMakeLists.txt +++ b/Libraries/LibJS/CMakeLists.txt @@ -72,6 +72,7 @@ set(SOURCES Runtime/SymbolObject.cpp Runtime/SymbolPrototype.cpp Runtime/Uint8ClampedArray.cpp + Runtime/VM.cpp Runtime/Value.cpp Token.cpp ) diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 407743e9b0..ad26ba6b08 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -119,6 +119,7 @@ class Statement; class Symbol; class Token; class Uint8ClampedArray; +class VM; class Value; enum class DeclarationKind; diff --git a/Libraries/LibJS/Heap/Heap.cpp b/Libraries/LibJS/Heap/Heap.cpp index f615a43a26..9f9cb02c15 100644 --- a/Libraries/LibJS/Heap/Heap.cpp +++ b/Libraries/LibJS/Heap/Heap.cpp @@ -47,8 +47,8 @@ namespace JS { -Heap::Heap(Interpreter& interpreter) - : m_interpreter(interpreter) +Heap::Heap(VM& vm) + : m_vm(vm) { } @@ -57,6 +57,11 @@ Heap::~Heap() collect_garbage(CollectionType::CollectEverything); } +Interpreter& Heap::interpreter() +{ + return vm().interpreter(); +} + Cell* Heap::allocate_cell(size_t size) { if (should_collect_on_every_allocation()) { @@ -100,7 +105,8 @@ void Heap::collect_garbage(CollectionType collection_type, bool print_report) void Heap::gather_roots(HashTable<Cell*>& roots) { - m_interpreter.gather_roots({}, roots); + if (auto* interpreter = vm().interpreter_if_exists()) + interpreter->gather_roots({}, roots); gather_conservative_roots(roots); diff --git a/Libraries/LibJS/Heap/Heap.h b/Libraries/LibJS/Heap/Heap.h index 75aebb5860..b1381a8dae 100644 --- a/Libraries/LibJS/Heap/Heap.h +++ b/Libraries/LibJS/Heap/Heap.h @@ -43,7 +43,7 @@ class Heap { AK_MAKE_NONMOVABLE(Heap); public: - explicit Heap(Interpreter&); + explicit Heap(VM&); ~Heap(); template<typename T, typename... Args> @@ -71,7 +71,8 @@ public: void collect_garbage(CollectionType = CollectionType::CollectGarbage, bool print_report = false); - Interpreter& interpreter() { return m_interpreter; } + Interpreter& interpreter(); + VM& vm() { return m_vm; } bool should_collect_on_every_allocation() const { return m_should_collect_on_every_allocation; } void set_should_collect_on_every_allocation(bool b) { m_should_collect_on_every_allocation = b; } @@ -100,7 +101,7 @@ private: bool m_should_collect_on_every_allocation { false }; - Interpreter& m_interpreter; + VM& m_vm; Vector<NonnullOwnPtr<HeapBlock>> m_blocks; HashTable<HandleImpl*> m_handles; diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 3e2fdc44f1..3165c8dd47 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -44,8 +44,8 @@ namespace JS { -Interpreter::Interpreter() - : m_heap(*this) +Interpreter::Interpreter(VM& vm) + : m_vm(vm) , m_console(*this) { #define __JS_ENUMERATE(SymbolName, snake_name) \ @@ -60,6 +60,8 @@ Interpreter::~Interpreter() Value Interpreter::run(GlobalObject& global_object, const Program& program) { + VM::InterpreterScope scope(*this); + ASSERT(!exception()); if (m_call_stack.is_empty()) { @@ -223,7 +225,6 @@ Symbol* Interpreter::get_global_symbol(const String& description) void Interpreter::gather_roots(Badge<Heap>, HashTable<Cell*>& roots) { - roots.set(m_global_object); roots.set(m_exception); if (m_last_value.is_cell()) @@ -252,6 +253,8 @@ Value Interpreter::call_internal(Function& function, Value this_value, Optional< { ASSERT(!exception()); + VM::InterpreterScope scope(*this); + auto& call_frame = push_call_frame(); call_frame.function_name = function.name(); call_frame.this_value = function.bound_this().value_or(this_value); @@ -348,12 +351,12 @@ void Interpreter::throw_exception(Exception* exception) GlobalObject& Interpreter::global_object() { - return static_cast<GlobalObject&>(*m_global_object); + return static_cast<GlobalObject&>(*m_global_object.cell()); } const GlobalObject& Interpreter::global_object() const { - return static_cast<const GlobalObject&>(*m_global_object); + return static_cast<const GlobalObject&>(*m_global_object.cell()); } String Interpreter::join_arguments() const diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index a9e3eeaac3..b356adfbed 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -34,11 +34,13 @@ #include <LibJS/AST.h> #include <LibJS/Console.h> #include <LibJS/Forward.h> +#include <LibJS/Heap/DeferGC.h> #include <LibJS/Heap/Heap.h> #include <LibJS/Runtime/ErrorTypes.h> #include <LibJS/Runtime/Exception.h> #include <LibJS/Runtime/LexicalEnvironment.h> #include <LibJS/Runtime/MarkedValueList.h> +#include <LibJS/Runtime/VM.h> #include <LibJS/Runtime/Value.h> namespace JS { @@ -75,11 +77,13 @@ typedef Vector<Argument, 8> ArgumentVector; class Interpreter : public Weakable<Interpreter> { public: template<typename GlobalObjectType, typename... Args> - static NonnullOwnPtr<Interpreter> create(Args&&... args) + static NonnullOwnPtr<Interpreter> create(VM& vm, Args&&... args) { - auto interpreter = adopt_own(*new Interpreter); - interpreter->m_global_object = interpreter->heap().allocate_without_global_object<GlobalObjectType>(forward<Args>(args)...); - static_cast<GlobalObjectType*>(interpreter->m_global_object)->initialize(); + DeferGC defer_gc(vm.heap()); + auto interpreter = adopt_own(*new Interpreter(vm)); + VM::InterpreterScope scope(*interpreter); + interpreter->m_global_object = make_handle(static_cast<Object*>(interpreter->heap().allocate_without_global_object<GlobalObjectType>(forward<Args>(args)...))); + static_cast<GlobalObjectType*>(interpreter->m_global_object.cell())->initialize(); return interpreter; } @@ -107,7 +111,8 @@ public: GlobalObject& global_object(); const GlobalObject& global_object() const; - Heap& heap() { return m_heap; } + VM& vm() { return *m_vm; } + Heap& heap() { return vm().heap(); } void unwind(ScopeType type, FlyString label = {}) { @@ -234,18 +239,18 @@ public: #undef __JS_ENUMERATE private: - Interpreter(); + explicit Interpreter(VM&); [[nodiscard]] Value call_internal(Function&, Value this_value, Optional<MarkedValueList>); - Heap m_heap; + NonnullRefPtr<VM> m_vm; Value m_last_value; Vector<ScopeFrame> m_scope_stack; Vector<CallFrame> m_call_stack; - Object* m_global_object { nullptr }; + Handle<Object> m_global_object; Exception* m_exception { nullptr }; diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index 58e5097275..da2024b4a5 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -26,6 +26,7 @@ */ #include <AK/LogStream.h> +#include <LibJS/Heap/DeferGC.h> #include <LibJS/Interpreter.h> #include <LibJS/Runtime/ArrayConstructor.h> #include <LibJS/Runtime/ArrayIteratorPrototype.h> @@ -87,13 +88,12 @@ void GlobalObject::initialize() JS_ENUMERATE_BUILTIN_TYPES #undef __JS_ENUMERATE -#define __JS_ENUMERATE(ClassName, snake_name) \ - if (!m_##snake_name##_prototype) \ +#define __JS_ENUMERATE(ClassName, snake_name) \ + if (!m_##snake_name##_prototype) \ m_##snake_name##_prototype = heap().allocate<ClassName##Prototype>(*this, *this); JS_ENUMERATE_ITERATOR_PROTOTYPES #undef __JS_ENUMERATE - u8 attr = Attribute::Writable | Attribute::Configurable; define_native_function("gc", gc, 0, attr); define_native_function("isNaN", is_nan, 1, attr); diff --git a/Libraries/LibJS/Runtime/VM.cpp b/Libraries/LibJS/Runtime/VM.cpp new file mode 100644 index 0000000000..41b1e46ec4 --- /dev/null +++ b/Libraries/LibJS/Runtime/VM.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * 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 <LibJS/Interpreter.h> +#include <LibJS/Runtime/VM.h> + +namespace JS { + +NonnullRefPtr<VM> VM::create() +{ + return adopt(*new VM); +} + +VM::VM() + : m_heap(*this) +{ +} + +VM::~VM() +{ +} + +Interpreter& VM::interpreter() +{ + if (m_interpreters.is_empty()) { + asm volatile("ud2"); + } +// ASSERT(!m_interpreters.is_empty()); + return *m_interpreters.last(); +} + +Interpreter* VM::interpreter_if_exists() +{ + if (m_interpreters.is_empty()) + return nullptr; + return m_interpreters.last(); +} + +void VM::push_interpreter(Interpreter& interpreter) +{ + m_interpreters.append(&interpreter); +} + +void VM::pop_interpreter(Interpreter& interpreter) +{ + ASSERT(!m_interpreters.is_empty()); + auto* popped_interpreter = m_interpreters.take_last(); + ASSERT(popped_interpreter == &interpreter); +} + +VM::InterpreterScope::InterpreterScope(Interpreter& interpreter) + : m_interpreter(interpreter) +{ + m_interpreter.vm().push_interpreter(m_interpreter); +} + +VM::InterpreterScope::~InterpreterScope() +{ + m_interpreter.vm().pop_interpreter(m_interpreter); +} + +} diff --git a/Libraries/LibJS/Runtime/VM.h b/Libraries/LibJS/Runtime/VM.h new file mode 100644 index 0000000000..d262398219 --- /dev/null +++ b/Libraries/LibJS/Runtime/VM.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * 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 <AK/RefCounted.h> +#include <LibJS/Heap/Heap.h> + +namespace JS { + +class VM : public RefCounted<VM> { +public: + static NonnullRefPtr<VM> create(); + ~VM(); + + Heap& heap() { return m_heap; } + const Heap& heap() const { return m_heap; } + + Interpreter& interpreter(); + Interpreter* interpreter_if_exists(); + + void push_interpreter(Interpreter&); + void pop_interpreter(Interpreter&); + + class InterpreterScope { + public: + InterpreterScope(Interpreter&); + ~InterpreterScope(); + private: + Interpreter& m_interpreter; + }; + +private: + VM(); + + Heap m_heap; + Vector<Interpreter*> m_interpreters; +}; + +} |