mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 23:57:34 +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:
parent
6150960671
commit
6537ed8fff
15 changed files with 117 additions and 106 deletions
|
@ -701,7 +701,7 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
|
|||
|
||||
// 29. If result.[[Type]] is normal, then
|
||||
// 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);
|
||||
if (executable_result.is_error())
|
||||
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;
|
||||
if (Bytecode::g_dump_bytecode)
|
||||
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())
|
||||
return result_or_error.value.release_error();
|
||||
|
||||
|
|
|
@ -455,8 +455,8 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
|
|||
} else if (i < execution_context_arguments.size() && !execution_context_arguments[i].is_undefined()) {
|
||||
argument_value = execution_context_arguments[i];
|
||||
} else if (parameter.default_value) {
|
||||
if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) {
|
||||
auto value_and_frame = bytecode_interpreter->run_and_return_frame(*m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr);
|
||||
if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) {
|
||||
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())
|
||||
return value_and_frame.value.release_error();
|
||||
// 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();
|
||||
|
||||
// 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.
|
||||
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.
|
||||
|
||||
|
@ -817,7 +827,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
|
|||
if (m_kind == FunctionKind::AsyncGenerator)
|
||||
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.
|
||||
// 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
|
||||
// suddenly stop working inside of generators.
|
||||
// This is a stop gap until bytecode mode becomes the default.
|
||||
OwnPtr<Bytecode::Interpreter> temp_bc_interpreter;
|
||||
if (m_kind == FunctionKind::Generator && !bytecode_interpreter) {
|
||||
temp_bc_interpreter = make<Bytecode::Interpreter>(realm);
|
||||
bytecode_interpreter = temp_bc_interpreter.ptr();
|
||||
bytecode_interpreter = &vm.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:
|
||||
// - We have to compile the default parameter values before instantiating the function.
|
||||
// - 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) {
|
||||
if (!parameter.default_value)
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -872,10 +861,10 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
|
|||
TRY(function_declaration_instantiation(nullptr));
|
||||
|
||||
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);
|
||||
if (result_and_frame.value.is_error())
|
||||
|
|
|
@ -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.value, completion.value().value(), default_attributes);
|
||||
|
||||
auto* bytecode_interpreter = Bytecode::Interpreter::current();
|
||||
|
||||
// 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& bytecode_interpreter = vm.bytecode_interpreter();
|
||||
|
||||
auto const* next_block = generated_continuation(m_previous_value);
|
||||
|
||||
|
@ -131,9 +120,9 @@ ThrowCompletionOr<Value> GeneratorObject::execute(VM& vm, Completion const& comp
|
|||
if (frame)
|
||||
frame->registers[0] = completion_object;
|
||||
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();
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Lexer.h>
|
||||
#include <LibJS/Parser.h>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <AK/StringBuilder.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.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_custom_data(move(custom_data))
|
||||
{
|
||||
m_bytecode_interpreter = make<Bytecode::Interpreter>(*this);
|
||||
|
||||
m_empty_string = m_heap.allocate_without_realm<PrimitiveString>(String {});
|
||||
|
||||
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
|
||||
{
|
||||
VERIFY(type < ErrorMessage::__Count);
|
||||
|
@ -196,6 +201,18 @@ Interpreter* VM::interpreter_if_exists()
|
|||
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)
|
||||
{
|
||||
m_interpreters.append(&interpreter);
|
||||
|
|
|
@ -39,11 +39,14 @@ public:
|
|||
};
|
||||
|
||||
static ErrorOr<NonnullRefPtr<VM>> create(OwnPtr<CustomData> = {});
|
||||
~VM() = default;
|
||||
~VM();
|
||||
|
||||
Heap& heap() { return m_heap; }
|
||||
Heap const& heap() const { return m_heap; }
|
||||
|
||||
Bytecode::Interpreter& bytecode_interpreter();
|
||||
Bytecode::Interpreter* bytecode_interpreter_if_exists();
|
||||
|
||||
Interpreter& interpreter();
|
||||
Interpreter* interpreter_if_exists();
|
||||
|
||||
|
@ -332,6 +335,8 @@ private:
|
|||
u32 m_execution_generation { 0 };
|
||||
|
||||
OwnPtr<CustomData> m_custom_data;
|
||||
|
||||
OwnPtr<Bytecode::Interpreter> m_bytecode_interpreter;
|
||||
};
|
||||
|
||||
ALWAYS_INLINE Heap& Cell::heap() const
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue