summaryrefslogtreecommitdiff
path: root/Libraries/LibJS
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-09-20 19:24:44 +0200
committerAndreas Kling <kling@serenityos.org>2020-09-20 19:24:44 +0200
commit1c43442be46926a572059d4aad6d8f3a7e95d3b2 (patch)
treed7c58db53782d143eec803f07657b9ca758c6ecd /Libraries/LibJS
parentc6ae0c41d908ae59a858a28a737b4382cceaeb6c (diff)
downloadserenity-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.txt1
-rw-r--r--Libraries/LibJS/Forward.h1
-rw-r--r--Libraries/LibJS/Heap/Heap.cpp12
-rw-r--r--Libraries/LibJS/Heap/Heap.h7
-rw-r--r--Libraries/LibJS/Interpreter.cpp13
-rw-r--r--Libraries/LibJS/Interpreter.h21
-rw-r--r--Libraries/LibJS/Runtime/GlobalObject.cpp6
-rw-r--r--Libraries/LibJS/Runtime/VM.cpp85
-rw-r--r--Libraries/LibJS/Runtime/VM.h63
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;
+};
+
+}