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.
This commit is contained in:
parent
c6ae0c41d9
commit
1c43442be4
Notes:
sideshowbarker
2024-07-19 02:19:23 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/1c43442be46
13 changed files with 218 additions and 27 deletions
|
@ -37,9 +37,17 @@
|
|||
|
||||
namespace Spreadsheet {
|
||||
|
||||
static JS::VM& global_vm()
|
||||
{
|
||||
static RefPtr<JS::VM> vm;
|
||||
if (!vm)
|
||||
vm = JS::VM::create();
|
||||
return *vm;
|
||||
}
|
||||
|
||||
Workbook::Workbook(NonnullRefPtrVector<Sheet>&& sheets)
|
||||
: m_sheets(move(sheets))
|
||||
, m_interpreter(JS::Interpreter::create<JS::GlobalObject>())
|
||||
, m_interpreter(JS::Interpreter::create<JS::GlobalObject>(global_vm()))
|
||||
{
|
||||
m_workbook_object = interpreter().heap().allocate<WorkbookObject>(global_object(), *this);
|
||||
global_object().put("workbook", workbook_object());
|
||||
|
|
|
@ -72,6 +72,7 @@ set(SOURCES
|
|||
Runtime/SymbolObject.cpp
|
||||
Runtime/SymbolPrototype.cpp
|
||||
Runtime/Uint8ClampedArray.cpp
|
||||
Runtime/VM.cpp
|
||||
Runtime/Value.cpp
|
||||
Token.cpp
|
||||
)
|
||||
|
|
|
@ -119,6 +119,7 @@ class Statement;
|
|||
class Symbol;
|
||||
class Token;
|
||||
class Uint8ClampedArray;
|
||||
class VM;
|
||||
class Value;
|
||||
enum class DeclarationKind;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
85
Libraries/LibJS/Runtime/VM.cpp
Normal file
85
Libraries/LibJS/Runtime/VM.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
63
Libraries/LibJS/Runtime/VM.h
Normal file
63
Libraries/LibJS/Runtime/VM.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -408,10 +408,18 @@ Color Document::visited_link_color() const
|
|||
return frame()->page().palette().visited_link();
|
||||
}
|
||||
|
||||
static JS::VM& main_thread_vm()
|
||||
{
|
||||
static RefPtr<JS::VM> vm;
|
||||
if (!vm)
|
||||
vm = JS::VM::create();
|
||||
return *vm;
|
||||
}
|
||||
|
||||
JS::Interpreter& Document::interpreter()
|
||||
{
|
||||
if (!m_interpreter)
|
||||
m_interpreter = JS::Interpreter::create<Bindings::WindowObject>(*m_window);
|
||||
m_interpreter = JS::Interpreter::create<Bindings::WindowObject>(main_thread_vm(), *m_window);
|
||||
return *m_interpreter;
|
||||
}
|
||||
|
||||
|
|
|
@ -552,6 +552,7 @@ int main(int argc, char** argv)
|
|||
|
||||
bool syntax_highlight = !disable_syntax_highlight;
|
||||
|
||||
auto vm = JS::VM::create();
|
||||
OwnPtr<JS::Interpreter> 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<ReplObject>();
|
||||
interpreter = JS::Interpreter::create<ReplObject>(*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<JS::GlobalObject>();
|
||||
interpreter = JS::Interpreter::create<JS::GlobalObject>(*vm);
|
||||
ReplConsoleClient console_client(interpreter->console());
|
||||
interpreter->console().set_client(console_client);
|
||||
interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation);
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
|
||||
#define TOP_LEVEL_TEST_NAME "__$$TOP_LEVEL$$__"
|
||||
|
||||
RefPtr<JS::VM> 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<TestRunnerGlobalObject>();
|
||||
auto interpreter = JS::Interpreter::create<TestRunnerGlobalObject>(*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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue