From 865e651a7ded49468b477d6605fd4ee313e8a0b2 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Wed, 1 May 2024 19:33:49 +0200 Subject: [PATCH] LibJS: Merge CallFrame into ExecutionContext Before this change both ExecutionContext and CallFrame were created before executing function/module/script with a couple exceptions: - executable created for default function argument evaluation has to run in function's execution context. - `execute_ast_node()` where executable compiled for ASTNode has to be executed in running execution context. This change moves all members previously owned by CallFrame into ExecutionContext, and makes two exceptions where an executable that does not have a corresponding execution context saves and restores registers before running. Now, all execution state lives in a single entity, which makes it a bit easier to reason about and opens opportunities for optimizations, such as moving registers and local variables into a single array. --- .../LibJS/Bytecode/CommonImplementations.h | 2 +- .../Libraries/LibJS/Bytecode/Interpreter.cpp | 108 ++++++------------ .../Libraries/LibJS/Bytecode/Interpreter.h | 69 +++-------- Userland/Libraries/LibJS/Heap/Heap.cpp | 3 - .../LibJS/Runtime/AbstractOperations.cpp | 4 +- .../LibJS/Runtime/AsyncGenerator.cpp | 19 +-- .../Libraries/LibJS/Runtime/AsyncGenerator.h | 3 +- .../Runtime/ECMAScriptFunctionObject.cpp | 31 +++-- .../LibJS/Runtime/ExecutionContext.cpp | 9 ++ .../LibJS/Runtime/ExecutionContext.h | 5 + .../LibJS/Runtime/GeneratorObject.cpp | 19 +-- .../Libraries/LibJS/Runtime/GeneratorObject.h | 3 +- .../Libraries/LibJS/Runtime/ShadowRealm.cpp | 8 +- Userland/Libraries/LibJS/Runtime/VM.cpp | 17 ++- Userland/Libraries/LibJS/SourceTextModule.cpp | 8 +- 15 files changed, 121 insertions(+), 187 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h index 7ab35fd0b1d..3aa5caaa565 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -647,7 +647,7 @@ inline ThrowCompletionOr new_class(VM& vm, Value supe // NOTE: NewClass expects classEnv to be active lexical environment auto* class_environment = vm.lexical_environment(); - vm.running_execution_context().lexical_environment = interpreter.saved_lexical_environment_stack().take_last(); + vm.running_execution_context().lexical_environment = vm.running_execution_context().saved_lexical_environments.take_last(); Optional binding_name; DeprecatedFlyString class_name; diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 90a6d77dff3..a7465a790c1 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -106,18 +106,6 @@ static ByteString format_value_list(StringView name, ReadonlySpan values) return builder.to_byte_string(); } -NonnullOwnPtr CallFrame::create(size_t register_count) -{ - size_t allocation_size = sizeof(CallFrame) + sizeof(Value) * register_count; - auto* memory = malloc(allocation_size); - VERIFY(memory); - auto call_frame = adopt_own(*new (memory) CallFrame); - call_frame->register_count = register_count; - for (auto i = 0u; i < register_count; ++i) - new (&call_frame->register_values[i]) Value(); - return call_frame; -} - ALWAYS_INLINE static ThrowCompletionOr loosely_inequals(VM& vm, Value src1, Value src2) { if (src1.tag() == src2.tag()) { @@ -163,13 +151,6 @@ Interpreter::~Interpreter() { } -void Interpreter::visit_edges(Cell::Visitor& visitor) -{ - for (auto& frame : m_call_frames) { - frame.visit([&](auto& value) { value->visit_edges(visitor); }); - } -} - ALWAYS_INLINE Value Interpreter::get(Operand op) const { switch (op.type()) { @@ -263,11 +244,11 @@ ThrowCompletionOr Interpreter::run(Script& script_record, JS::GCPtrdump(); // a. Set result to the result of evaluating script. - auto result_or_error = run_and_return_frame(*executable, nullptr); + auto result_or_error = run_executable(*executable, nullptr); if (result_or_error.value.is_error()) result = result_or_error.value.release_error(); else - result = result_or_error.frame->registers()[0]; + result = result_or_error.return_register_value; } } @@ -384,7 +365,8 @@ void Interpreter::run_bytecode() do_return(saved_return_value()); break; } - auto const* old_scheduled_jump = call_frame().previously_scheduled_jumps.take_last(); + auto& running_execution_context = vm().running_execution_context(); + auto const* old_scheduled_jump = running_execution_context.previously_scheduled_jumps.take_last(); if (m_scheduled_jump) { // FIXME: If we `break` or `continue` in the finally, we need to clear // this field @@ -418,7 +400,8 @@ void Interpreter::run_bytecode() if (!handler && !finalizer) return; - auto& unwind_context = unwind_contexts().last(); + auto& running_execution_context = vm().running_execution_context(); + auto& unwind_context = running_execution_context.unwind_contexts.last(); VERIFY(unwind_context.executable == m_current_executable); if (handler) { @@ -453,7 +436,8 @@ void Interpreter::run_bytecode() } if (auto const* finalizer = m_current_block->finalizer(); finalizer && !will_yield) { - auto& unwind_context = unwind_contexts().last(); + auto& running_execution_context = vm().running_execution_context(); + auto& unwind_context = running_execution_context.unwind_contexts.last(); VERIFY(unwind_context.executable == m_current_executable); reg(Register::saved_return_value()) = reg(Register::return_value()); reg(Register::return_value()) = {}; @@ -470,7 +454,7 @@ void Interpreter::run_bytecode() } } -Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable& executable, BasicBlock const* entry_point, CallFrame* in_frame) +Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& executable, BasicBlock const* entry_point) { dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable); @@ -484,12 +468,13 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable& executa TemporaryChange restore_current_block { m_current_block, entry_point ?: executable.basic_blocks.first() }; - if (in_frame) - push_call_frame(in_frame); - else - push_call_frame(CallFrame::create(executable.number_of_registers)); + auto& running_execution_context = vm().running_execution_context(); + if (running_execution_context.registers.size() < executable.number_of_registers) + running_execution_context.registers.resize(executable.number_of_registers); - vm().execution_context_stack().last()->executable = &executable; + reg(Register::return_value()) = {}; + + running_execution_context.executable = &executable; run_bytecode(); @@ -513,48 +498,39 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable& executa return_value = reg(Register::saved_return_value()); auto exception = reg(Register::exception()); - auto frame = pop_call_frame(); - - // NOTE: The return value from a called function is put into $0 in the caller context. - if (!m_call_frames.is_empty()) - call_frame().registers()[0] = return_value; - // At this point we may have already run any queued promise jobs via on_call_stack_emptied, // in which case this is a no-op. vm().run_queued_promise_jobs(); vm().finish_execution_generation(); - if (!exception.is_empty()) { - if (auto* call_frame = frame.get_pointer>()) - return { throw_completion(exception), move(*call_frame) }; - return { throw_completion(exception), nullptr }; - } - - if (auto* call_frame = frame.get_pointer>()) - return { return_value, move(*call_frame) }; - return { return_value, nullptr }; + if (!exception.is_empty()) + return { throw_completion(exception), vm().running_execution_context().registers[0] }; + return { return_value, vm().running_execution_context().registers[0] }; } void Interpreter::enter_unwind_context() { - unwind_contexts().empend( + auto& running_execution_context = vm().running_execution_context(); + running_execution_context.unwind_contexts.empend( m_current_executable, vm().running_execution_context().lexical_environment); - call_frame().previously_scheduled_jumps.append(m_scheduled_jump); + running_execution_context.previously_scheduled_jumps.append(m_scheduled_jump); m_scheduled_jump = nullptr; } void Interpreter::leave_unwind_context() { - unwind_contexts().take_last(); + auto& running_execution_context = vm().running_execution_context(); + running_execution_context.unwind_contexts.take_last(); } void Interpreter::catch_exception(Operand dst) { set(dst, reg(Register::exception())); reg(Register::exception()) = {}; - auto& context = unwind_contexts().last(); + auto& running_execution_context = vm().running_execution_context(); + auto& context = running_execution_context.unwind_contexts.last(); VERIFY(!context.handler_called); VERIFY(context.executable == ¤t_executable()); context.handler_called = true; @@ -563,9 +539,10 @@ void Interpreter::catch_exception(Operand dst) void Interpreter::enter_object_environment(Object& object) { - auto& old_environment = vm().running_execution_context().lexical_environment; - saved_lexical_environment_stack().append(old_environment); - vm().running_execution_context().lexical_environment = new_object_environment(object, true, old_environment); + auto& running_execution_context = vm().running_execution_context(); + auto& old_environment = running_execution_context.lexical_environment; + running_execution_context.saved_lexical_environments.append(old_environment); + running_execution_context.lexical_environment = new_object_environment(object, true, old_environment); } ThrowCompletionOr> compile(VM& vm, ASTNode const& node, ReadonlySpan parameters, FunctionKind kind, DeprecatedFlyString const& name) @@ -583,20 +560,6 @@ ThrowCompletionOr> compile(VM& vm, ASTNode co return bytecode_executable; } -void Interpreter::push_call_frame(Variant, CallFrame*> frame) -{ - m_call_frames.append(move(frame)); - m_current_call_frame = this->call_frame().registers(); - reg(Register::return_value()) = {}; -} - -Variant, CallFrame*> Interpreter::pop_call_frame() -{ - auto frame = m_call_frames.take_last(); - m_current_call_frame = m_call_frames.is_empty() ? Span {} : this->call_frame().registers(); - return frame; -} - } namespace JS::Bytecode { @@ -1039,7 +1002,8 @@ ThrowCompletionOr CreateLexicalEnvironment::execute_impl(Bytecode::Interpr swap(old_environment, environment); return environment; }; - interpreter.saved_lexical_environment_stack().append(make_and_swap_envs(interpreter.vm().running_execution_context().lexical_environment)); + auto& running_execution_context = interpreter.vm().running_execution_context(); + running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment)); return {}; } @@ -1453,7 +1417,8 @@ ThrowCompletionOr ScheduleJump::execute_impl(Bytecode::Interpreter&) const ThrowCompletionOr LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.vm().running_execution_context().lexical_environment = interpreter.saved_lexical_environment_stack().take_last(); + auto& running_execution_context = interpreter.vm().running_execution_context(); + running_execution_context.lexical_environment = running_execution_context.saved_lexical_environments.take_last(); return {}; } @@ -1643,9 +1608,10 @@ ThrowCompletionOr BlockDeclarationInstantiation::execute_impl(Bytecode::In { auto& vm = interpreter.vm(); auto old_environment = vm.running_execution_context().lexical_environment; - interpreter.saved_lexical_environment_stack().append(old_environment); - vm.running_execution_context().lexical_environment = new_declarative_environment(*old_environment); - m_scope_node.block_declaration_instantiation(vm, vm.running_execution_context().lexical_environment); + auto& running_execution_context = vm.running_execution_context(); + running_execution_context.saved_lexical_environments.append(old_environment); + running_execution_context.lexical_environment = new_declarative_environment(*old_environment); + m_scope_node.block_declaration_instantiation(vm, running_execution_context.lexical_environment); return {}; } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index 7e39464eb90..f92b4d5d886 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -19,31 +19,6 @@ namespace JS::Bytecode { class InstructionStreamIterator; -struct CallFrame { - static NonnullOwnPtr create(size_t register_count); - - void operator delete(void* ptr) { free(ptr); } - - void visit_edges(Cell::Visitor& visitor) - { - visitor.visit(registers()); - visitor.visit(saved_lexical_environments); - for (auto& context : unwind_contexts) { - visitor.visit(context.lexical_environment); - } - } - - Vector> saved_lexical_environments; - Vector unwind_contexts; - Vector previously_scheduled_jumps; - - Span registers() { return { register_values, register_count }; } - ReadonlySpan registers() const { return { register_values, register_count }; } - - size_t register_count { 0 }; - Value register_values[]; -}; - class Interpreter { public: explicit Interpreter(VM&); @@ -60,27 +35,30 @@ public: ThrowCompletionOr run(Bytecode::Executable& executable, Bytecode::BasicBlock const* entry_point = nullptr) { - auto value_and_frame = run_and_return_frame(executable, entry_point); - return move(value_and_frame.value); + auto result_and_return_register = run_executable(executable, entry_point); + return move(result_and_return_register.value); } - struct ValueAndFrame { + struct ResultAndReturnRegister { ThrowCompletionOr value; - OwnPtr frame; + Value return_register_value; }; - ValueAndFrame run_and_return_frame(Bytecode::Executable&, Bytecode::BasicBlock const* entry_point, CallFrame* = nullptr); + ResultAndReturnRegister run_executable(Bytecode::Executable&, Bytecode::BasicBlock const* entry_point); ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); } ALWAYS_INLINE Value& saved_return_value() { return reg(Register::saved_return_value()); } - Value& reg(Register const& r) { return registers()[r.index()]; } - Value reg(Register const& r) const { return registers()[r.index()]; } + Value& reg(Register const& r) + { + return vm().running_execution_context().registers[r.index()]; + } + Value reg(Register const& r) const + { + return vm().running_execution_context().registers[r.index()]; + } [[nodiscard]] Value get(Operand) const; void set(Operand, Value); - auto& saved_lexical_environment_stack() { return call_frame().saved_lexical_environments; } - auto& unwind_contexts() { return call_frame().unwind_contexts; } - void do_return(Value value) { reg(Register::return_value()) = value; @@ -98,30 +76,13 @@ public: BasicBlock const& current_block() const { return *m_current_block; } Optional instruction_stream_iterator() const { return m_pc; } - void visit_edges(Cell::Visitor&); - - Span registers() { return m_current_call_frame; } - ReadonlySpan registers() const { return m_current_call_frame; } + Vector& registers() { return vm().running_execution_context().registers; } + Vector const& registers() const { return vm().running_execution_context().registers; } private: void run_bytecode(); - CallFrame& call_frame() - { - return m_call_frames.last().visit([](auto& x) -> CallFrame& { return *x; }); - } - - CallFrame const& call_frame() const - { - return const_cast(this)->call_frame(); - } - - void push_call_frame(Variant, CallFrame*>); - [[nodiscard]] Variant, CallFrame*> pop_call_frame(); - VM& m_vm; - Vector, CallFrame*>> m_call_frames; - Span m_current_call_frame; BasicBlock const* m_scheduled_jump { nullptr }; GCPtr m_current_executable { nullptr }; BasicBlock const* m_current_block { nullptr }; diff --git a/Userland/Libraries/LibJS/Heap/Heap.cpp b/Userland/Libraries/LibJS/Heap/Heap.cpp index f6d1adf25a6..62d8e91e73e 100644 --- a/Userland/Libraries/LibJS/Heap/Heap.cpp +++ b/Userland/Libraries/LibJS/Heap/Heap.cpp @@ -259,7 +259,6 @@ AK::JsonObject Heap::dump_graph() HashMap roots; gather_roots(roots); GraphConstructorVisitor visitor(*this, roots); - vm().bytecode_interpreter().visit_edges(visitor); visitor.visit_all_cells(); return visitor.dump(); } @@ -457,8 +456,6 @@ void Heap::mark_live_cells(HashMap const& roots) MarkingVisitor visitor(*this, roots); - vm().bytecode_interpreter().visit_edges(visitor); - visitor.mark_all_live_cells(); for (auto& inverse_root : m_uprooted_cells) diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index d1aef644669..42dab329f23 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -693,11 +693,11 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, executable->name = "eval"sv; if (Bytecode::g_dump_bytecode) executable->dump(); - auto result_or_error = vm.bytecode_interpreter().run_and_return_frame(*executable, nullptr); + auto result_or_error = vm.bytecode_interpreter().run_executable(*executable, nullptr); if (result_or_error.value.is_error()) return result_or_error.value.release_error(); - auto& result = result_or_error.frame->registers()[0]; + auto& result = result_or_error.return_register_value; if (!result.is_empty()) eval_result = result; diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp b/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp index c27e67b9cd8..684995525c7 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp +++ b/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp @@ -16,7 +16,7 @@ namespace JS { JS_DEFINE_ALLOCATOR(AsyncGenerator); -ThrowCompletionOr> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr execution_context, NonnullOwnPtr frame) +ThrowCompletionOr> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr execution_context) { auto& vm = realm.vm(); // This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png) @@ -24,7 +24,6 @@ ThrowCompletionOr> AsyncGenerator::create(Realm& re auto generating_function_prototype_object = TRY(generating_function_prototype.to_object(vm)); auto object = realm.heap().allocate(realm, realm, generating_function_prototype_object, move(execution_context)); object->m_generating_function = generating_function; - object->m_frame = move(frame); object->m_previous_value = initial_value; return object; } @@ -45,8 +44,6 @@ void AsyncGenerator::visit_edges(Cell::Visitor& visitor) } visitor.visit(m_generating_function); visitor.visit(m_previous_value); - if (m_frame) - m_frame->visit_edges(visitor); visitor.visit(m_current_promise); m_async_generator_context->visit_edges(visitor); } @@ -192,19 +189,9 @@ void AsyncGenerator::execute(VM& vm, Completion completion) VERIFY(!m_generating_function->bytecode_executable()->basic_blocks.find_if([next_block](auto& block) { return block == next_block; }).is_end()); - Bytecode::CallFrame* frame = nullptr; - if (m_frame) - frame = m_frame.ptr(); + bytecode_interpreter.accumulator() = completion_object; - if (frame) - frame->registers()[0] = completion_object; - else - bytecode_interpreter.accumulator() = completion_object; - - auto next_result = bytecode_interpreter.run_and_return_frame(*m_generating_function->bytecode_executable(), next_block, frame); - - if (!m_frame) - m_frame = move(next_result.frame); + auto next_result = bytecode_interpreter.run_executable(*m_generating_function->bytecode_executable(), next_block); auto result_value = move(next_result.value); if (!result_value.is_throw_completion()) { diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGenerator.h b/Userland/Libraries/LibJS/Runtime/AsyncGenerator.h index 091a241a5b2..db980ce452f 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncGenerator.h +++ b/Userland/Libraries/LibJS/Runtime/AsyncGenerator.h @@ -28,7 +28,7 @@ public: Completed, }; - static ThrowCompletionOr> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr, NonnullOwnPtr); + static ThrowCompletionOr> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr); virtual ~AsyncGenerator() override = default; @@ -60,7 +60,6 @@ private: GCPtr m_generating_function; Value m_previous_value; - OwnPtr m_frame; GCPtr m_current_promise; }; diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 424535a734a..8a369a672d2 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -713,11 +713,23 @@ ThrowCompletionOr 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) { - auto value_and_frame = vm.bytecode_interpreter().run_and_return_frame(*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. - argument_value = value_and_frame.frame->registers()[0]; + auto& running_execution_context = vm.running_execution_context(); + + // NOTE: Registers have to be saved and restored because executable created for default parameter uses + // running execution context. + // FIXME: This is a hack and instead instructions for default parameters should be a part of the function's bytecode. + auto saved_registers = running_execution_context.registers; + for (size_t register_index = 0; register_index < saved_registers.size(); ++register_index) + saved_registers[register_index] = {}; + + auto result_and_return_register = vm.bytecode_interpreter().run_executable(*m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr); + + for (size_t register_index = 0; register_index < saved_registers.size(); ++register_index) + running_execution_context.registers[register_index] = saved_registers[register_index]; + + if (result_and_return_register.value.is_error()) + return result_and_return_register.value.release_error(); + argument_value = result_and_return_register.return_register_value; } else { argument_value = js_undefined(); } @@ -1103,7 +1115,7 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro if (maybe_executable.is_error()) result = maybe_executable.release_error(); else - result = vm.bytecode_interpreter().run_and_return_frame(*maybe_executable.value(), nullptr).value; + result = vm.bytecode_interpreter().run_executable(*maybe_executable.value(), nullptr).value; } // b. Else, else { @@ -1230,9 +1242,8 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() } } - auto result_and_frame = vm.bytecode_interpreter().run_and_return_frame(*m_bytecode_executable, nullptr); + auto result_and_frame = vm.bytecode_interpreter().run_executable(*m_bytecode_executable, nullptr); - VERIFY(result_and_frame.frame != nullptr); if (result_and_frame.value.is_error()) return result_and_frame.value.release_error(); @@ -1244,11 +1255,11 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() return { Completion::Type::Return, result.value_or(js_undefined()), {} }; if (m_kind == FunctionKind::AsyncGenerator) { - auto async_generator_object = TRY(AsyncGenerator::create(realm, result, this, vm.running_execution_context().copy(), result_and_frame.frame.release_nonnull())); + auto async_generator_object = TRY(AsyncGenerator::create(realm, result, this, vm.running_execution_context().copy())); return { Completion::Type::Return, async_generator_object, {} }; } - auto generator_object = TRY(GeneratorObject::create(realm, result, this, vm.running_execution_context().copy(), result_and_frame.frame.release_nonnull())); + auto generator_object = TRY(GeneratorObject::create(realm, result, this, vm.running_execution_context().copy())); // NOTE: Async functions are entirely transformed to generator functions, and wrapped in a custom driver that returns a promise // See AwaitExpression::generate_bytecode() for the transformation. diff --git a/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp b/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp index 54fe22a3603..ec87f2ec34b 100644 --- a/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp +++ b/Userland/Libraries/LibJS/Runtime/ExecutionContext.cpp @@ -43,6 +43,10 @@ NonnullOwnPtr ExecutionContext::copy() const copy->executable = executable; copy->arguments = arguments; copy->locals = locals; + copy->registers = registers; + copy->unwind_contexts = unwind_contexts; + copy->saved_lexical_environments = saved_lexical_environments; + copy->previously_scheduled_jumps = previously_scheduled_jumps; return copy; } @@ -61,6 +65,11 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor) visitor.visit(function_name); visitor.visit(arguments); visitor.visit(locals); + visitor.visit(registers); + for (auto& context : unwind_contexts) { + visitor.visit(context.lexical_environment); + } + visitor.visit(saved_lexical_environments); script_or_module.visit( [](Empty) {}, [&](auto& script_or_module) { diff --git a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h index 43df15c60a7..f343df171be 100644 --- a/Userland/Libraries/LibJS/Runtime/ExecutionContext.h +++ b/Userland/Libraries/LibJS/Runtime/ExecutionContext.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -75,6 +76,10 @@ public: Vector arguments; Vector locals; + Vector registers; + Vector unwind_contexts; + Vector previously_scheduled_jumps; + Vector> saved_lexical_environments; }; struct StackTraceElement { diff --git a/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp b/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp index 5a6d378db98..f4b6d93f095 100644 --- a/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp @@ -16,7 +16,7 @@ namespace JS { JS_DEFINE_ALLOCATOR(GeneratorObject); -ThrowCompletionOr> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr execution_context, NonnullOwnPtr frame) +ThrowCompletionOr> GeneratorObject::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr execution_context) { auto& vm = realm.vm(); // This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png) @@ -32,7 +32,6 @@ ThrowCompletionOr> GeneratorObject::create(Realm& auto generating_function_prototype_object = TRY(generating_function_prototype.to_object(vm)); auto object = realm.heap().allocate(realm, realm, generating_function_prototype_object, move(execution_context)); object->m_generating_function = generating_function; - object->m_frame = move(frame); object->m_previous_value = initial_value; return object; } @@ -49,8 +48,6 @@ void GeneratorObject::visit_edges(Cell::Visitor& visitor) Base::visit_edges(visitor); visitor.visit(m_generating_function); visitor.visit(m_previous_value); - if (m_frame) - m_frame->visit_edges(visitor); m_execution_context->visit_edges(visitor); } @@ -113,22 +110,12 @@ ThrowCompletionOr GeneratorObject::execute(VM& vm, Completion const& comp VERIFY(!m_generating_function->bytecode_executable()->basic_blocks.find_if([next_block](auto& block) { return block == next_block; }).is_end()); - Bytecode::CallFrame* frame = nullptr; - if (m_frame) - frame = m_frame; + bytecode_interpreter.registers()[0] = completion_object; - if (frame) - frame->registers()[0] = completion_object; - else - 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_executable(*m_generating_function->bytecode_executable(), next_block); vm.pop_execution_context(); - if (!m_frame) - m_frame = move(next_result.frame); - auto result_value = move(next_result.value); if (result_value.is_throw_completion()) { // Uncaught exceptions disable the generator. diff --git a/Userland/Libraries/LibJS/Runtime/GeneratorObject.h b/Userland/Libraries/LibJS/Runtime/GeneratorObject.h index 596ba1acf25..48eb59e8679 100644 --- a/Userland/Libraries/LibJS/Runtime/GeneratorObject.h +++ b/Userland/Libraries/LibJS/Runtime/GeneratorObject.h @@ -17,7 +17,7 @@ class GeneratorObject : public Object { JS_DECLARE_ALLOCATOR(GeneratorObject); public: - static ThrowCompletionOr> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr, NonnullOwnPtr); + static ThrowCompletionOr> create(Realm&, Value, ECMAScriptFunctionObject*, NonnullOwnPtr); virtual ~GeneratorObject() override = default; void visit_edges(Cell::Visitor&) override; @@ -43,7 +43,6 @@ private: NonnullOwnPtr m_execution_context; GCPtr m_generating_function; Value m_previous_value; - OwnPtr m_frame; GeneratorState m_generator_state { GeneratorState::SuspendedStart }; Optional m_generator_brand; }; diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp index 297d17dac92..62620856824 100644 --- a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp @@ -180,12 +180,12 @@ ThrowCompletionOr perform_shadow_realm_eval(VM& vm, StringView source_tex else { auto executable = maybe_executable.release_value(); - auto value_and_frame = vm.bytecode_interpreter().run_and_return_frame(*executable, nullptr); - if (value_and_frame.value.is_error()) { - result = value_and_frame.value.release_error(); + auto result_and_return_register = vm.bytecode_interpreter().run_executable(*executable, nullptr); + if (result_and_return_register.value.is_error()) { + result = result_and_return_register.value.release_error(); } else { // Resulting value is in the accumulator. - result = value_and_frame.frame->registers()[0].value_or(js_undefined()); + result = result_and_return_register.return_register_value.value_or(js_undefined()); } } } diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index c751d933064..86399b28c64 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -289,11 +289,24 @@ ThrowCompletionOr VM::binding_initialization(NonnullRefPtr VM::execute_ast_node(ASTNode const& node) { + // FIXME: This function should be gone once we will emit bytecode for everything before executing instructions. + auto executable = TRY(Bytecode::compile(*this, node, {}, FunctionKind::Normal, ""sv)); - auto result_or_error = bytecode_interpreter().run_and_return_frame(*executable, nullptr); + auto& running_execution_context = this->running_execution_context(); + + // Registers have to be saved and restored because executable for compiled ASTNode does not have its own execution context + auto saved_registers = running_execution_context.registers; + for (size_t i = 0; i < saved_registers.size(); ++i) + running_execution_context.registers[i] = {}; + + auto result_or_error = bytecode_interpreter().run_executable(*executable, nullptr); + + for (size_t i = 0; i < saved_registers.size(); ++i) + running_execution_context.registers[i] = saved_registers[i]; + if (result_or_error.value.is_error()) return result_or_error.value.release_error(); - return result_or_error.frame->registers()[0]; + return result_or_error.return_register_value; } // 13.15.5.3 Runtime Semantics: PropertyDestructuringAssignmentEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-propertydestructuringassignmentevaluation diff --git a/Userland/Libraries/LibJS/SourceTextModule.cpp b/Userland/Libraries/LibJS/SourceTextModule.cpp index 9c5fd5a41b2..4dd9d0affd3 100644 --- a/Userland/Libraries/LibJS/SourceTextModule.cpp +++ b/Userland/Libraries/LibJS/SourceTextModule.cpp @@ -717,12 +717,12 @@ ThrowCompletionOr SourceTextModule::execute_module(VM& vm, GCPtrregisters()[0].value_or(js_undefined()); + result = result_and_return_register.return_register_value.value_or(js_undefined()); } }