diff --git a/Applications/Spreadsheet/Workbook.cpp b/Applications/Spreadsheet/Workbook.cpp index 27e839e4eaf..941d8d356ac 100644 --- a/Applications/Spreadsheet/Workbook.cpp +++ b/Applications/Spreadsheet/Workbook.cpp @@ -37,9 +37,17 @@ namespace Spreadsheet { +static JS::VM& global_vm() +{ + static RefPtr vm; + if (!vm) + vm = JS::VM::create(); + return *vm; +} + Workbook::Workbook(NonnullRefPtrVector&& sheets) : m_sheets(move(sheets)) - , m_interpreter(JS::Interpreter::create()) + , m_interpreter(JS::Interpreter::create(global_vm())) { m_workbook_object = interpreter().heap().allocate(global_object(), *this); global_object().put("workbook", workbook_object()); diff --git a/Libraries/LibJS/CMakeLists.txt b/Libraries/LibJS/CMakeLists.txt index 7b121cd1460..c51da6bf75b 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 407743e9b0b..ad26ba6b087 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 f615a43a261..9f9cb02c154 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& 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 75aebb58602..b1381a8dae8 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 @@ -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> m_blocks; HashTable m_handles; diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 3e2fdc44f17..3165c8dd479 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, HashTable& 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(*m_global_object); + return static_cast(*m_global_object.cell()); } const GlobalObject& Interpreter::global_object() const { - return static_cast(*m_global_object); + return static_cast(*m_global_object.cell()); } String Interpreter::join_arguments() const diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index a9e3eeaac31..b356adfbede 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -34,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include namespace JS { @@ -75,11 +77,13 @@ typedef Vector ArgumentVector; class Interpreter : public Weakable { public: template - static NonnullOwnPtr create(Args&&... args) + static NonnullOwnPtr create(VM& vm, Args&&... args) { - auto interpreter = adopt_own(*new Interpreter); - interpreter->m_global_object = interpreter->heap().allocate_without_global_object(forward(args)...); - static_cast(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(interpreter->heap().allocate_without_global_object(forward(args)...))); + static_cast(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); - Heap m_heap; + NonnullRefPtr m_vm; Value m_last_value; Vector m_scope_stack; Vector m_call_stack; - Object* m_global_object { nullptr }; + Handle m_global_object; Exception* m_exception { nullptr }; diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp index 58e50972759..da2024b4a5e 100644 --- a/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -26,6 +26,7 @@ */ #include +#include #include #include #include @@ -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(*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 00000000000..41b1e46ec45 --- /dev/null +++ b/Libraries/LibJS/Runtime/VM.cpp @@ -0,0 +1,85 @@ +/* + * 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 { + +NonnullRefPtr 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 00000000000..d2623982197 --- /dev/null +++ b/Libraries/LibJS/Runtime/VM.h @@ -0,0 +1,63 @@ +/* + * 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 + +namespace JS { + +class VM : public RefCounted { +public: + static NonnullRefPtr 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 m_interpreters; +}; + +} diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 1fa66a1ac4c..d4814297b2e 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -408,10 +408,18 @@ Color Document::visited_link_color() const return frame()->page().palette().visited_link(); } +static JS::VM& main_thread_vm() +{ + static RefPtr vm; + if (!vm) + vm = JS::VM::create(); + return *vm; +} + JS::Interpreter& Document::interpreter() { if (!m_interpreter) - m_interpreter = JS::Interpreter::create(*m_window); + m_interpreter = JS::Interpreter::create(main_thread_vm(), *m_window); return *m_interpreter; } diff --git a/Userland/js.cpp b/Userland/js.cpp index f08b336184e..08271c95d60 100644 --- a/Userland/js.cpp +++ b/Userland/js.cpp @@ -552,6 +552,7 @@ int main(int argc, char** argv) bool syntax_highlight = !disable_syntax_highlight; + auto vm = JS::VM::create(); OwnPtr interpreter; interrupt_interpreter = [&] { @@ -561,7 +562,7 @@ int main(int argc, char** argv) if (script_path == nullptr) { s_print_last_result = true; - interpreter = JS::Interpreter::create(); + interpreter = JS::Interpreter::create(*vm); ReplConsoleClient console_client(interpreter->console()); interpreter->console().set_client(console_client); interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation); @@ -842,7 +843,7 @@ int main(int argc, char** argv) s_editor->on_tab_complete = move(complete); repl(*interpreter); } else { - interpreter = JS::Interpreter::create(); + interpreter = JS::Interpreter::create(*vm); ReplConsoleClient console_client(interpreter->console()); interpreter->console().set_client(console_client); interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation); diff --git a/Userland/test-js.cpp b/Userland/test-js.cpp index 3a944aedaca..84d77276510 100644 --- a/Userland/test-js.cpp +++ b/Userland/test-js.cpp @@ -43,6 +43,8 @@ #define TOP_LEVEL_TEST_NAME "__$$TOP_LEVEL$$__" +RefPtr vm; + static bool collect_on_every_allocation = false; static String currently_running_test; @@ -273,7 +275,10 @@ JSFileResult TestRunner::run_file_test(const String& test_path) currently_running_test = test_path; double start_time = get_time_in_ms(); - auto interpreter = JS::Interpreter::create(); + auto interpreter = JS::Interpreter::create(*vm); + + // FIXME: This is a hack while we're refactoring Interpreter/VM stuff. + JS::VM::InterpreterScope scope(*interpreter); interpreter->heap().set_should_collect_on_every_allocation(collect_on_every_allocation); @@ -603,6 +608,8 @@ int main(int argc, char** argv) DebugLogStream::set_enabled(false); } + vm = JS::VM::create(); + #ifdef __serenity__ TestRunner("/home/anon/js-tests", print_times).run(); #else @@ -614,5 +621,7 @@ int main(int argc, char** argv) TestRunner(String::format("%s/Libraries/LibJS/Tests", serenity_root), print_times).run(); #endif + vm = nullptr; + return 0; }