1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 04:58:13 +00:00

LibJS/Bytecode: Simplify Bytecode::Interpreter lifetime model

The JS::VM now owns the one Bytecode::Interpreter. We no longer have
multiple bytecode interpreters, and there is no concept of a "current"
bytecode interpreter.

If you ask for VM::bytecode_interpreter_if_exists(), it will return null
if we're not running the program in "bytecode enabled" mode.

If you ask for VM::bytecode_interpreter(), it will return a bytecode
interpreter in all modes. This is used for situations where even the AST
interpreter switches to bytecode mode (generators, etc.)
This commit is contained in:
Andreas Kling 2023-06-22 15:59:18 +02:00
parent 6150960671
commit 6537ed8fff
15 changed files with 117 additions and 106 deletions

View file

@ -102,7 +102,6 @@ private:
}; };
static bool s_dump_ast = false; static bool s_dump_ast = false;
static bool s_run_bytecode = false;
static bool s_as_module = false; static bool s_as_module = false;
static bool s_print_last_result = false; static bool s_print_last_result = false;
@ -119,9 +118,8 @@ static ErrorOr<bool> parse_and_run(JS::Interpreter& interpreter, StringView sour
if (s_dump_ast) if (s_dump_ast)
script_or_module->parse_node().dump(0); script_or_module->parse_node().dump(0);
if (s_run_bytecode) { if (auto* bytecode_interpreter = g_vm->bytecode_interpreter_if_exists()) {
JS::Bytecode::Interpreter bytecode_interpreter(interpreter.realm()); result = bytecode_interpreter->run(*script_or_module);
result = bytecode_interpreter.run(*script_or_module);
} else { } else {
result = interpreter.run(*script_or_module); result = interpreter.run(*script_or_module);
} }

View file

@ -215,12 +215,10 @@ static Result<void, TestError> run_test(StringView source, StringView filepath,
if (program_or_error.is_error()) if (program_or_error.is_error())
return program_or_error.release_error(); return program_or_error.release_error();
OwnPtr<JS::Bytecode::Interpreter> bytecode_interpreter = nullptr; auto* bytecode_interpreter = vm->bytecode_interpreter_if_exists();
if (JS::Bytecode::Interpreter::enabled())
bytecode_interpreter = make<JS::Bytecode::Interpreter>(realm);
auto run_with_interpreter = [&](ScriptOrModuleProgram& program) { auto run_with_interpreter = [&](ScriptOrModuleProgram& program) {
if (JS::Bytecode::Interpreter::enabled()) if (bytecode_interpreter)
return run_program(*bytecode_interpreter, program); return run_program(*bytecode_interpreter, program);
return run_program(*ast_interpreter, program); return run_program(*ast_interpreter, program);
}; };

View file

@ -32,26 +32,15 @@ void Interpreter::set_enabled(bool enabled)
s_bytecode_interpreter_enabled = enabled; s_bytecode_interpreter_enabled = enabled;
} }
static Interpreter* s_current;
bool g_dump_bytecode = false; bool g_dump_bytecode = false;
Interpreter* Interpreter::current() Interpreter::Interpreter(VM& vm)
: m_vm(vm)
{ {
return s_current;
}
Interpreter::Interpreter(Realm& realm)
: m_vm(realm.vm())
, m_realm(realm)
{
VERIFY(!s_current);
s_current = this;
} }
Interpreter::~Interpreter() Interpreter::~Interpreter()
{ {
VERIFY(s_current == this);
s_current = nullptr;
} }
// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation // 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
@ -124,7 +113,7 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, JS::GCPtr<Envir
executable->dump(); executable->dump();
// a. Set result to the result of evaluating script. // a. Set result to the result of evaluating script.
auto result_or_error = run_and_return_frame(*executable, nullptr); auto result_or_error = run_and_return_frame(script_record.realm(), *executable, nullptr);
if (result_or_error.value.is_error()) if (result_or_error.value.is_error())
result = result_or_error.value.release_error(); result = result_or_error.value.release_error();
else else
@ -189,7 +178,7 @@ void Interpreter::set_optimizations_enabled(bool enabled)
m_optimizations_enabled = enabled; m_optimizations_enabled = enabled;
} }
Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& executable, BasicBlock const* entry_point, RegisterWindow* in_frame) Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Executable const& executable, BasicBlock const* entry_point, RegisterWindow* in_frame)
{ {
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable); dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable);
@ -201,12 +190,12 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e
ExecutionContext execution_context(vm().heap()); ExecutionContext execution_context(vm().heap());
if (vm().execution_context_stack().is_empty() || !vm().running_execution_context().lexical_environment) { if (vm().execution_context_stack().is_empty() || !vm().running_execution_context().lexical_environment) {
// The "normal" interpreter pushes an execution context without environment so in that case we also want to push one. // The "normal" interpreter pushes an execution context without environment so in that case we also want to push one.
execution_context.this_value = &m_realm->global_object(); execution_context.this_value = &realm.global_object();
static DeprecatedFlyString global_execution_context_name = "(*BC* global execution context)"; static DeprecatedFlyString global_execution_context_name = "(*BC* global execution context)";
execution_context.function_name = global_execution_context_name; execution_context.function_name = global_execution_context_name;
execution_context.lexical_environment = &m_realm->global_environment(); execution_context.lexical_environment = &realm.global_environment();
execution_context.variable_environment = &m_realm->global_environment(); execution_context.variable_environment = &realm.global_environment();
execution_context.realm = m_realm; execution_context.realm = realm;
execution_context.is_strict_mode = executable.is_strict_mode; execution_context.is_strict_mode = executable.is_strict_mode;
vm().push_execution_context(execution_context); vm().push_execution_context(execution_context);
pushed_execution_context = true; pushed_execution_context = true;
@ -395,10 +384,10 @@ ThrowCompletionOr<void> Interpreter::continue_pending_unwind(Label const& resume
return {}; return {};
} }
VM::InterpreterExecutionScope Interpreter::ast_interpreter_scope() VM::InterpreterExecutionScope Interpreter::ast_interpreter_scope(Realm& realm)
{ {
if (!m_ast_interpreter) if (!m_ast_interpreter)
m_ast_interpreter = JS::Interpreter::create_with_existing_realm(m_realm); m_ast_interpreter = JS::Interpreter::create_with_existing_realm(realm);
return { *m_ast_interpreter }; return { *m_ast_interpreter };
} }
@ -449,4 +438,29 @@ DeprecatedString Interpreter::debug_position() const
return DeprecatedString::formatted("{}:{:2}:{:4x}", m_current_executable->name, m_current_block->name(), pc()); return DeprecatedString::formatted("{}:{:2}:{:4x}", m_current_executable->name, m_current_block->name(), pc());
} }
ThrowCompletionOr<NonnullOwnPtr<Bytecode::Executable>> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name)
{
auto executable_result = Bytecode::Generator::generate(node, kind);
if (executable_result.is_error())
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
auto bytecode_executable = executable_result.release_value();
bytecode_executable->name = name;
auto& passes = Bytecode::Interpreter::optimization_pipeline();
passes.perform(*bytecode_executable);
if constexpr (JS_BYTECODE_DEBUG) {
dbgln("Optimisation passes took {}us", passes.elapsed());
dbgln("Compiled Bytecode::Block for function '{}':", name);
}
if (Bytecode::g_dump_bytecode)
bytecode_executable->dump();
return bytecode_executable;
}
Realm& Interpreter::realm()
{
return *m_vm.current_realm();
}
} }

View file

@ -11,6 +11,7 @@
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h> #include <LibJS/Heap/Cell.h>
#include <LibJS/Heap/Handle.h> #include <LibJS/Heap/Handle.h>
#include <LibJS/Runtime/FunctionKind.h>
#include <LibJS/Runtime/VM.h> #include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
@ -31,13 +32,10 @@ public:
[[nodiscard]] static bool enabled(); [[nodiscard]] static bool enabled();
static void set_enabled(bool); static void set_enabled(bool);
explicit Interpreter(Realm&); explicit Interpreter(VM&);
~Interpreter(); ~Interpreter();
// FIXME: Remove this thing once we don't need it anymore! Realm& realm();
static Interpreter* current();
Realm& realm() { return m_realm; }
VM& vm() { return m_vm; } VM& vm() { return m_vm; }
void set_optimizations_enabled(bool); void set_optimizations_enabled(bool);
@ -45,9 +43,9 @@ public:
ThrowCompletionOr<Value> run(Script&, JS::GCPtr<Environment> lexical_environment_override = nullptr); ThrowCompletionOr<Value> run(Script&, JS::GCPtr<Environment> lexical_environment_override = nullptr);
ThrowCompletionOr<Value> run(SourceTextModule&); ThrowCompletionOr<Value> run(SourceTextModule&);
ThrowCompletionOr<Value> run(Bytecode::Executable const& executable, Bytecode::BasicBlock const* entry_point = nullptr) ThrowCompletionOr<Value> run(Realm& realm, Bytecode::Executable const& executable, Bytecode::BasicBlock const* entry_point = nullptr)
{ {
auto value_and_frame = run_and_return_frame(executable, entry_point); auto value_and_frame = run_and_return_frame(realm, executable, entry_point);
return move(value_and_frame.value); return move(value_and_frame.value);
} }
@ -55,7 +53,7 @@ public:
ThrowCompletionOr<Value> value; ThrowCompletionOr<Value> value;
OwnPtr<RegisterWindow> frame; OwnPtr<RegisterWindow> frame;
}; };
ValueAndFrame run_and_return_frame(Bytecode::Executable const&, Bytecode::BasicBlock const* entry_point, RegisterWindow* = nullptr); ValueAndFrame run_and_return_frame(Realm&, Bytecode::Executable const&, Bytecode::BasicBlock const* entry_point, RegisterWindow* = nullptr);
ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); } ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); }
Value& reg(Register const& r) { return registers()[r.index()]; } Value& reg(Register const& r) { return registers()[r.index()]; }
@ -97,7 +95,7 @@ public:
}; };
static Bytecode::PassManager& optimization_pipeline(OptimizationLevel = OptimizationLevel::Default); static Bytecode::PassManager& optimization_pipeline(OptimizationLevel = OptimizationLevel::Default);
VM::InterpreterExecutionScope ast_interpreter_scope(); VM::InterpreterExecutionScope ast_interpreter_scope(Realm&);
private: private:
RegisterWindow& window() RegisterWindow& window()
@ -115,7 +113,6 @@ private:
static AK::Array<OwnPtr<PassManager>, static_cast<UnderlyingType<Interpreter::OptimizationLevel>>(Interpreter::OptimizationLevel::__Count)> s_optimization_pipelines; static AK::Array<OwnPtr<PassManager>, static_cast<UnderlyingType<Interpreter::OptimizationLevel>>(Interpreter::OptimizationLevel::__Count)> s_optimization_pipelines;
VM& m_vm; VM& m_vm;
NonnullGCPtr<Realm> m_realm;
Vector<Variant<NonnullOwnPtr<RegisterWindow>, RegisterWindow*>> m_register_windows; Vector<Variant<NonnullOwnPtr<RegisterWindow>, RegisterWindow*>> m_register_windows;
Optional<BasicBlock const*> m_pending_jump; Optional<BasicBlock const*> m_pending_jump;
BasicBlock const* m_scheduled_jump { nullptr }; BasicBlock const* m_scheduled_jump { nullptr };
@ -131,4 +128,6 @@ private:
extern bool g_dump_bytecode; extern bool g_dump_bytecode;
ThrowCompletionOr<NonnullOwnPtr<Bytecode::Executable>> compile(VM&, ASTNode const& no, JS::FunctionKind kind, DeprecatedFlyString const& name);
} }

View file

@ -1081,7 +1081,7 @@ ThrowCompletionOr<void> IteratorResultValue::execute_impl(Bytecode::Interpreter&
ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interpreter) const ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interpreter) const
{ {
auto name = m_class_expression.name(); auto name = m_class_expression.name();
auto scope = interpreter.ast_interpreter_scope(); auto scope = interpreter.ast_interpreter_scope(interpreter.realm());
auto& ast_interpreter = scope.interpreter(); auto& ast_interpreter = scope.interpreter();
auto* class_object = TRY(m_class_expression.class_definition_evaluation(ast_interpreter, name, name.is_null() ? ""sv : name)); auto* class_object = TRY(m_class_expression.class_definition_evaluation(ast_interpreter, name, name.is_null() ? ""sv : name));

View file

@ -701,7 +701,7 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
// 29. If result.[[Type]] is normal, then // 29. If result.[[Type]] is normal, then
// a. Set result to the result of evaluating body. // a. Set result to the result of evaluating body.
if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) { if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) {
auto executable_result = Bytecode::Generator::generate(program); auto executable_result = Bytecode::Generator::generate(program);
if (executable_result.is_error()) if (executable_result.is_error())
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string())); return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
@ -710,7 +710,7 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
executable->name = "eval"sv; executable->name = "eval"sv;
if (Bytecode::g_dump_bytecode) if (Bytecode::g_dump_bytecode)
executable->dump(); executable->dump();
auto result_or_error = bytecode_interpreter->run_and_return_frame(*executable, nullptr); auto result_or_error = bytecode_interpreter->run_and_return_frame(eval_realm, *executable, nullptr);
if (result_or_error.value.is_error()) if (result_or_error.value.is_error())
return result_or_error.value.release_error(); return result_or_error.value.release_error();

View file

@ -455,8 +455,8 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
} else if (i < execution_context_arguments.size() && !execution_context_arguments[i].is_undefined()) { } else if (i < execution_context_arguments.size() && !execution_context_arguments[i].is_undefined()) {
argument_value = execution_context_arguments[i]; argument_value = execution_context_arguments[i];
} else if (parameter.default_value) { } else if (parameter.default_value) {
if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) { if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) {
auto value_and_frame = bytecode_interpreter->run_and_return_frame(*m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr); auto value_and_frame = bytecode_interpreter->run_and_return_frame(realm, *m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr);
if (value_and_frame.value.is_error()) if (value_and_frame.value.is_error())
return value_and_frame.value.release_error(); return value_and_frame.value.release_error();
// Resulting value is in the accumulator. // Resulting value is in the accumulator.
@ -751,9 +751,19 @@ void async_block_start(VM& vm, NonnullRefPtr<Statement const> const& async_body,
auto& running_context = vm.running_execution_context(); auto& running_context = vm.running_execution_context();
// 3. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed: // 3. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed:
auto execution_steps = NativeFunction::create(realm, "", [&async_body, &promise_capability, &async_context](auto& vm) -> ThrowCompletionOr<Value> { auto execution_steps = NativeFunction::create(realm, "", [&realm, &async_body, &promise_capability, &async_context](auto& vm) -> ThrowCompletionOr<Value> {
// a. Let result be the result of evaluating asyncBody. // a. Let result be the result of evaluating asyncBody.
auto result = async_body->execute(vm.interpreter()); Completion result;
if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) {
// FIXME: Cache this executable somewhere.
auto maybe_executable = Bytecode::compile(vm, async_body, FunctionKind::Async, "AsyncBlockStart"sv);
if (maybe_executable.is_error())
result = maybe_executable.release_error();
else
result = bytecode_interpreter->run_and_return_frame(realm, *maybe_executable.value(), nullptr).value;
} else {
result = async_body->execute(vm.interpreter());
}
// b. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. // b. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.
@ -817,7 +827,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
if (m_kind == FunctionKind::AsyncGenerator) if (m_kind == FunctionKind::AsyncGenerator)
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, "Async Generator function execution"); return vm.throw_completion<InternalError>(ErrorType::NotImplemented, "Async Generator function execution");
auto* bytecode_interpreter = Bytecode::Interpreter::current(); auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists();
// The bytecode interpreter can execute generator functions while the AST interpreter cannot. // The bytecode interpreter can execute generator functions while the AST interpreter cannot.
// This simply makes it create a new bytecode interpreter when one doesn't exist when executing a generator function. // This simply makes it create a new bytecode interpreter when one doesn't exist when executing a generator function.
@ -826,32 +836,11 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
// However, this does cause an awkward situation with features not supported in bytecode, where features that work outside of generators with AST // However, this does cause an awkward situation with features not supported in bytecode, where features that work outside of generators with AST
// suddenly stop working inside of generators. // suddenly stop working inside of generators.
// This is a stop gap until bytecode mode becomes the default. // This is a stop gap until bytecode mode becomes the default.
OwnPtr<Bytecode::Interpreter> temp_bc_interpreter;
if (m_kind == FunctionKind::Generator && !bytecode_interpreter) { if (m_kind == FunctionKind::Generator && !bytecode_interpreter) {
temp_bc_interpreter = make<Bytecode::Interpreter>(realm); bytecode_interpreter = &vm.bytecode_interpreter();
bytecode_interpreter = temp_bc_interpreter.ptr();
} }
if (bytecode_interpreter) { if (bytecode_interpreter) {
auto compile = [&](auto& node, auto kind, auto name) -> ThrowCompletionOr<NonnullOwnPtr<Bytecode::Executable>> {
auto executable_result = Bytecode::Generator::generate(node, kind);
if (executable_result.is_error())
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
auto bytecode_executable = executable_result.release_value();
bytecode_executable->name = name;
auto& passes = Bytecode::Interpreter::optimization_pipeline();
passes.perform(*bytecode_executable);
if constexpr (JS_BYTECODE_DEBUG) {
dbgln("Optimisation passes took {}us", passes.elapsed());
dbgln("Compiled Bytecode::Block for function '{}':", m_name);
}
if (Bytecode::g_dump_bytecode)
bytecode_executable->dump();
return bytecode_executable;
};
// NOTE: There's a subtle ordering issue here: // NOTE: There's a subtle ordering issue here:
// - We have to compile the default parameter values before instantiating the function. // - We have to compile the default parameter values before instantiating the function.
// - We have to instantiate the function before compiling the function body. // - We have to instantiate the function before compiling the function body.
@ -864,7 +853,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
for (auto& parameter : m_formal_parameters) { for (auto& parameter : m_formal_parameters) {
if (!parameter.default_value) if (!parameter.default_value)
continue; continue;
auto executable = TRY(compile(*parameter.default_value, FunctionKind::Normal, DeprecatedString::formatted("default parameter #{} for {}", default_parameter_index, m_name))); auto executable = TRY(Bytecode::compile(vm, *parameter.default_value, FunctionKind::Normal, DeprecatedString::formatted("default parameter #{} for {}", default_parameter_index, m_name)));
m_default_parameter_bytecode_executables.append(move(executable)); m_default_parameter_bytecode_executables.append(move(executable));
} }
} }
@ -872,10 +861,10 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
TRY(function_declaration_instantiation(nullptr)); TRY(function_declaration_instantiation(nullptr));
if (!m_bytecode_executable) { if (!m_bytecode_executable) {
m_bytecode_executable = TRY(compile(*m_ecmascript_code, m_kind, m_name)); m_bytecode_executable = TRY(Bytecode::compile(vm, *m_ecmascript_code, m_kind, m_name));
} }
auto result_and_frame = bytecode_interpreter->run_and_return_frame(*m_bytecode_executable, nullptr); auto result_and_frame = bytecode_interpreter->run_and_return_frame(realm, *m_bytecode_executable, nullptr);
VERIFY(result_and_frame.frame != nullptr); VERIFY(result_and_frame.frame != nullptr);
if (result_and_frame.value.is_error()) if (result_and_frame.value.is_error())

View file

@ -104,18 +104,7 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
completion_object->define_direct_property(vm.names.type, Value(to_underlying(completion.type())), default_attributes); completion_object->define_direct_property(vm.names.type, Value(to_underlying(completion.type())), default_attributes);
completion_object->define_direct_property(vm.names.value, completion.value().value(), default_attributes); completion_object->define_direct_property(vm.names.value, completion.value().value(), default_attributes);
auto* bytecode_interpreter = Bytecode::Interpreter::current(); auto& bytecode_interpreter = vm.bytecode_interpreter();
// If we're coming from a context which has no bytecode interpreter, e.g. from AST mode calling Generate.prototype.next,
// we need to make one to be able to continue, as generators are only supported in bytecode mode.
// See also ECMAScriptFunctionObject::ordinary_call_evaluate_body where this is done as well.
OwnPtr<Bytecode::Interpreter> temp_bc_interpreter;
if (!bytecode_interpreter) {
temp_bc_interpreter = make<Bytecode::Interpreter>(realm);
bytecode_interpreter = temp_bc_interpreter.ptr();
}
VERIFY(bytecode_interpreter);
auto const* next_block = generated_continuation(m_previous_value); auto const* next_block = generated_continuation(m_previous_value);
@ -131,9 +120,9 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
if (frame) if (frame)
frame->registers[0] = completion_object; frame->registers[0] = completion_object;
else else
bytecode_interpreter->accumulator() = completion_object; bytecode_interpreter.accumulator() = completion_object;
auto next_result = bytecode_interpreter->run_and_return_frame(*m_generating_function->bytecode_executable(), next_block, frame); auto next_result = bytecode_interpreter.run_and_return_frame(realm, *m_generating_function->bytecode_executable(), next_block, frame);
vm.pop_execution_context(); vm.pop_execution_context();

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/Interpreter.h>
#include <LibJS/Interpreter.h> #include <LibJS/Interpreter.h>
#include <LibJS/Lexer.h> #include <LibJS/Lexer.h>
#include <LibJS/Parser.h> #include <LibJS/Parser.h>

View file

@ -14,6 +14,7 @@
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <LibFileSystem/FileSystem.h> #include <LibFileSystem/FileSystem.h>
#include <LibJS/AST.h> #include <LibJS/AST.h>
#include <LibJS/Bytecode/Interpreter.h>
#include <LibJS/Interpreter.h> #include <LibJS/Interpreter.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
@ -65,6 +66,8 @@ VM::VM(OwnPtr<CustomData> custom_data, ErrorMessages error_messages)
, m_error_messages(move(error_messages)) , m_error_messages(move(error_messages))
, m_custom_data(move(custom_data)) , m_custom_data(move(custom_data))
{ {
m_bytecode_interpreter = make<Bytecode::Interpreter>(*this);
m_empty_string = m_heap.allocate_without_realm<PrimitiveString>(String {}); m_empty_string = m_heap.allocate_without_realm<PrimitiveString>(String {});
for (size_t i = 0; i < single_ascii_character_strings.size(); ++i) for (size_t i = 0; i < single_ascii_character_strings.size(); ++i)
@ -166,6 +169,8 @@ VM::VM(OwnPtr<CustomData> custom_data, ErrorMessages error_messages)
}; };
} }
VM::~VM() = default;
String const& VM::error_message(ErrorMessage type) const String const& VM::error_message(ErrorMessage type) const
{ {
VERIFY(type < ErrorMessage::__Count); VERIFY(type < ErrorMessage::__Count);
@ -196,6 +201,18 @@ Interpreter* VM::interpreter_if_exists()
return m_interpreters.last(); return m_interpreters.last();
} }
Bytecode::Interpreter& VM::bytecode_interpreter()
{
return *m_bytecode_interpreter;
}
Bytecode::Interpreter* VM::bytecode_interpreter_if_exists()
{
if (!Bytecode::Interpreter::enabled())
return nullptr;
return m_bytecode_interpreter;
}
void VM::push_interpreter(Interpreter& interpreter) void VM::push_interpreter(Interpreter& interpreter)
{ {
m_interpreters.append(&interpreter); m_interpreters.append(&interpreter);

View file

@ -39,11 +39,14 @@ public:
}; };
static ErrorOr<NonnullRefPtr<VM>> create(OwnPtr<CustomData> = {}); static ErrorOr<NonnullRefPtr<VM>> create(OwnPtr<CustomData> = {});
~VM() = default; ~VM();
Heap& heap() { return m_heap; } Heap& heap() { return m_heap; }
Heap const& heap() const { return m_heap; } Heap const& heap() const { return m_heap; }
Bytecode::Interpreter& bytecode_interpreter();
Bytecode::Interpreter* bytecode_interpreter_if_exists();
Interpreter& interpreter(); Interpreter& interpreter();
Interpreter* interpreter_if_exists(); Interpreter* interpreter_if_exists();
@ -332,6 +335,8 @@ private:
u32 m_execution_generation { 0 }; u32 m_execution_generation { 0 };
OwnPtr<CustomData> m_custom_data; OwnPtr<CustomData> m_custom_data;
OwnPtr<Bytecode::Interpreter> m_bytecode_interpreter;
}; };
ALWAYS_INLINE Heap& Cell::heap() const ALWAYS_INLINE Heap& Cell::heap() const

View file

@ -126,7 +126,6 @@ static consteval size_t __testjs_last()
static constexpr auto TOP_LEVEL_TEST_NAME = "__$$TOP_LEVEL$$__"; static constexpr auto TOP_LEVEL_TEST_NAME = "__$$TOP_LEVEL$$__";
extern RefPtr<JS::VM> g_vm; extern RefPtr<JS::VM> g_vm;
extern bool g_collect_on_every_allocation; extern bool g_collect_on_every_allocation;
extern bool g_run_bytecode;
extern DeprecatedString g_currently_running_test; extern DeprecatedString g_currently_running_test;
struct FunctionWithLength { struct FunctionWithLength {
JS::ThrowCompletionOr<JS::Value> (*function)(JS::VM&); JS::ThrowCompletionOr<JS::Value> (*function)(JS::VM&);
@ -360,10 +359,9 @@ inline JSFileResult TestRunner::run_file_test(DeprecatedString const& test_path)
} }
auto test_script = result.release_value(); auto test_script = result.release_value();
if (g_run_bytecode) { if (auto* bytecode_interpreter = g_vm->bytecode_interpreter_if_exists()) {
g_vm->push_execution_context(global_execution_context); g_vm->push_execution_context(global_execution_context);
JS::Bytecode::Interpreter bytecode_interpreter(interpreter->realm()); MUST(bytecode_interpreter->run(*test_script));
MUST(bytecode_interpreter.run(*test_script));
g_vm->pop_execution_context(); g_vm->pop_execution_context();
} else { } else {
g_vm->push_execution_context(global_execution_context); g_vm->push_execution_context(global_execution_context);
@ -376,9 +374,8 @@ inline JSFileResult TestRunner::run_file_test(DeprecatedString const& test_path)
if (file_script.is_error()) if (file_script.is_error())
return { test_path, file_script.error() }; return { test_path, file_script.error() };
g_vm->push_execution_context(global_execution_context); g_vm->push_execution_context(global_execution_context);
if (g_run_bytecode) { if (auto* bytecode_interpreter = g_vm->bytecode_interpreter_if_exists()) {
JS::Bytecode::Interpreter bytecode_interpreter(interpreter->realm()); top_level_result = bytecode_interpreter->run(file_script.value());
top_level_result = bytecode_interpreter.run(file_script.value());
} else { } else {
top_level_result = interpreter->run(file_script.value()); top_level_result = interpreter->run(file_script.value());
} }

View file

@ -20,7 +20,6 @@ namespace JS {
RefPtr<::JS::VM> g_vm; RefPtr<::JS::VM> g_vm;
bool g_collect_on_every_allocation = false; bool g_collect_on_every_allocation = false;
bool g_run_bytecode = false;
DeprecatedString g_currently_running_test; DeprecatedString g_currently_running_test;
HashMap<DeprecatedString, FunctionWithLength> s_exposed_global_functions; HashMap<DeprecatedString, FunctionWithLength> s_exposed_global_functions;
Function<void()> g_main_hook; Function<void()> g_main_hook;
@ -94,6 +93,7 @@ int main(int argc, char** argv)
#endif #endif
bool print_json = false; bool print_json = false;
bool per_file = false; bool per_file = false;
bool use_bytecode = false;
StringView specified_test_root; StringView specified_test_root;
DeprecatedString common_path; DeprecatedString common_path;
DeprecatedString test_glob; DeprecatedString test_glob;
@ -115,10 +115,11 @@ int main(int argc, char** argv)
return true; return true;
}, },
}); });
args_parser.add_option(print_json, "Show results as JSON", "json", 'j'); args_parser.add_option(print_json, "Show results as JSON", "json", 'j');
args_parser.add_option(per_file, "Show detailed per-file results as JSON (implies -j)", "per-file", 0); args_parser.add_option(per_file, "Show detailed per-file results as JSON (implies -j)", "per-file", 0);
args_parser.add_option(g_collect_on_every_allocation, "Collect garbage after every allocation", "collect-often", 'g'); args_parser.add_option(g_collect_on_every_allocation, "Collect garbage after every allocation", "collect-often", 'g');
args_parser.add_option(g_run_bytecode, "Use the bytecode interpreter", "run-bytecode", 'b'); args_parser.add_option(use_bytecode, "Use the bytecode interpreter", "run-bytecode", 'b');
args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd'); args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd');
args_parser.add_option(test_glob, "Only run tests matching the given glob", "filter", 'f', "glob"); args_parser.add_option(test_glob, "Only run tests matching the given glob", "filter", 'f', "glob");
for (auto& entry : g_extra_args) for (auto& entry : g_extra_args)
@ -136,11 +137,13 @@ int main(int argc, char** argv)
AK::set_debug_enabled(false); AK::set_debug_enabled(false);
} }
if (JS::Bytecode::g_dump_bytecode && !g_run_bytecode) { if (JS::Bytecode::g_dump_bytecode && !use_bytecode) {
warnln("--dump-bytecode can only be used when --run-bytecode is specified."); warnln("--dump-bytecode can only be used when --run-bytecode is specified.");
return 1; return 1;
} }
JS::Bytecode::Interpreter::set_enabled(use_bytecode);
DeprecatedString test_root; DeprecatedString test_root;
if (!specified_test_root.is_empty()) { if (!specified_test_root.is_empty()) {

View file

@ -95,9 +95,8 @@ JS::Completion ClassicScript::run(RethrowErrors rethrow_errors, JS::GCPtr<JS::En
auto timer = Core::ElapsedTimer::start_new(); auto timer = Core::ElapsedTimer::start_new();
// 6. Otherwise, set evaluationStatus to ScriptEvaluation(script's record). // 6. Otherwise, set evaluationStatus to ScriptEvaluation(script's record).
if (JS::Bytecode::Interpreter::enabled()) { if (auto* bytecode_interpreter = vm().bytecode_interpreter_if_exists()) {
auto interpreter = JS::Bytecode::Interpreter(m_script_record->realm()); evaluation_status = bytecode_interpreter->run(*m_script_record, lexical_environment_override);
evaluation_status = interpreter.run(*m_script_record, lexical_environment_override);
} else { } else {
auto interpreter = JS::Interpreter::create_with_existing_realm(m_script_record->realm()); auto interpreter = JS::Interpreter::create_with_existing_realm(m_script_record->realm());
evaluation_status = interpreter->run(*m_script_record, lexical_environment_override); evaluation_status = interpreter->run(*m_script_record, lexical_environment_override);

View file

@ -71,7 +71,6 @@ private:
}; };
static bool s_dump_ast = false; static bool s_dump_ast = false;
static bool s_run_bytecode = false;
static bool s_opt_bytecode = false; static bool s_opt_bytecode = false;
static bool s_as_module = false; static bool s_as_module = false;
static bool s_print_last_result = false; static bool s_print_last_result = false;
@ -212,10 +211,9 @@ static ErrorOr<bool> parse_and_run(JS::Interpreter& interpreter, StringView sour
if (s_dump_ast) if (s_dump_ast)
script_or_module->parse_node().dump(0); script_or_module->parse_node().dump(0);
if (s_run_bytecode) { if (auto* bytecode_interpreter = g_vm->bytecode_interpreter_if_exists()) {
JS::Bytecode::Interpreter bytecode_interpreter(interpreter.realm()); bytecode_interpreter->set_optimizations_enabled(s_opt_bytecode);
bytecode_interpreter.set_optimizations_enabled(s_opt_bytecode); result = bytecode_interpreter->run(*script_or_module);
result = bytecode_interpreter.run(*script_or_module);
} else { } else {
result = interpreter.run(*script_or_module); result = interpreter.run(*script_or_module);
} }
@ -579,12 +577,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
bool use_test262_global = false; bool use_test262_global = false;
StringView evaluate_script; StringView evaluate_script;
Vector<StringView> script_paths; Vector<StringView> script_paths;
bool use_bytecode = false;
Core::ArgsParser args_parser; Core::ArgsParser args_parser;
args_parser.set_general_help("This is a JavaScript interpreter."); args_parser.set_general_help("This is a JavaScript interpreter.");
args_parser.add_option(s_dump_ast, "Dump the AST", "dump-ast", 'A'); args_parser.add_option(s_dump_ast, "Dump the AST", "dump-ast", 'A');
args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd'); args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd');
args_parser.add_option(s_run_bytecode, "Run the bytecode", "run-bytecode", 'b'); args_parser.add_option(use_bytecode, "Run the bytecode", "run-bytecode", 'b');
args_parser.add_option(s_opt_bytecode, "Optimize the bytecode", "optimize-bytecode", 'p'); args_parser.add_option(s_opt_bytecode, "Optimize the bytecode", "optimize-bytecode", 'p');
args_parser.add_option(s_as_module, "Treat as module", "as-module", 'm'); args_parser.add_option(s_as_module, "Treat as module", "as-module", 'm');
args_parser.add_option(s_print_last_result, "Print last result", "print-last-result", 'l'); args_parser.add_option(s_print_last_result, "Print last result", "print-last-result", 'l');
@ -598,6 +597,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
args_parser.add_positional_argument(script_paths, "Path to script files", "scripts", Core::ArgsParser::Required::No); args_parser.add_positional_argument(script_paths, "Path to script files", "scripts", Core::ArgsParser::Required::No);
args_parser.parse(arguments); args_parser.parse(arguments);
JS::Bytecode::Interpreter::set_enabled(use_bytecode);
bool syntax_highlight = !disable_syntax_highlight; bool syntax_highlight = !disable_syntax_highlight;
AK::set_debug_enabled(!disable_debug_printing); AK::set_debug_enabled(!disable_debug_printing);