diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index ca8e7e227bb..49aa9e640f2 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1972,17 +1972,11 @@ Bytecode::CodeGenerationErrorOr ContinueStatement::generate_bytecode(Bytec // We need to execute the finally block, but tell it to resume // execution at the designated block if (m_target_label.is_null()) { - generator.perform_needed_unwinds(); - generator.emit().set_targets( - generator.nearest_continuable_scope(), - {}); + generator.generate_continue(); return {}; } - auto target_to_jump_to = generator.perform_needed_unwinds_for_labelled_continue_and_return_target_block(m_target_label); - generator.emit().set_targets( - target_to_jump_to, - {}); + generator.generate_continue(m_target_label); return {}; } diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 771454a0561..c08f233319c 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -335,23 +335,62 @@ void Generator::generate_break(DeprecatedFlyString const& break_label) VERIFY_NOT_REACHED(); } -Label Generator::perform_needed_unwinds_for_labelled_continue_and_return_target_block(DeprecatedFlyString const& continue_label) +void Generator::generate_continue() +{ + bool last_was_finally = false; + // FIXME: Reduce code duplication + for (size_t i = m_boundaries.size(); i > 0; --i) { + auto boundary = m_boundaries[i - 1]; + using enum BlockBoundaryType; + switch (boundary) { + case Continue: + emit().set_targets(nearest_continuable_scope(), {}); + return; + case Unwind: + if (!last_was_finally) + emit(); + last_was_finally = false; + break; + case LeaveLexicalEnvironment: + emit(Bytecode::Op::EnvironmentMode::Lexical); + break; + case LeaveVariableEnvironment: + emit(Bytecode::Op::EnvironmentMode::Var); + break; + case Break: + break; + case ReturnToFinally: { + auto& block = make_block(DeprecatedString::formatted("{}.continue", current_block().name())); + emit(Label { block }); + switch_to_basic_block(block); + last_was_finally = true; + break; + }; + } + } + VERIFY_NOT_REACHED(); +} + +void Generator::generate_continue(DeprecatedFlyString const& continue_label) { size_t current_boundary = m_boundaries.size(); - for (auto& continuable_scope : m_continuable_scopes.in_reverse()) { + bool last_was_finally = false; + for (auto const& continuable_scope : m_continuable_scopes.in_reverse()) { for (; current_boundary > 0; --current_boundary) { auto boundary = m_boundaries[current_boundary - 1]; - // FIXME: Handle ReturnToFinally in a graceful manner - // We need to execute the finally block, but tell it to resume - // execution at the designated label if (boundary == BlockBoundaryType::Unwind) { - emit(); + if (!last_was_finally) + emit(); + last_was_finally = false; } else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) { emit(Bytecode::Op::EnvironmentMode::Lexical); } else if (boundary == BlockBoundaryType::LeaveVariableEnvironment) { emit(Bytecode::Op::EnvironmentMode::Var); } else if (boundary == BlockBoundaryType::ReturnToFinally) { - // FIXME: We need to enter the `finally`, while still scheduling the continue to happen + auto& block = make_block(DeprecatedString::formatted("{}.continue", current_block().name())); + emit(Label { block }); + switch_to_basic_block(block); + last_was_finally = true; } else if (boundary == BlockBoundaryType::Continue) { // Make sure we don't process this boundary twice if the current continuable scope doesn't contain the target label. --current_boundary; @@ -359,8 +398,10 @@ Label Generator::perform_needed_unwinds_for_labelled_continue_and_return_target_ } } - if (continuable_scope.language_label_set.contains_slow(continue_label)) - return continuable_scope.bytecode_target; + if (continuable_scope.language_label_set.contains_slow(continue_label)) { + emit().set_targets(continuable_scope.bytecode_target, {}); + return; + } } // We must have a continuable scope available that contains the label, as this should be enforced by the parser. diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index 459db7da96e..12bb4c9e6c0 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -216,7 +216,8 @@ public: void generate_break(); void generate_break(DeprecatedFlyString const& break_label); - Label perform_needed_unwinds_for_labelled_continue_and_return_target_block(DeprecatedFlyString const& continue_label); + void generate_continue(); + void generate_continue(DeprecatedFlyString const& continue_label); void start_boundary(BlockBoundaryType type) { m_boundaries.append(type); } void end_boundary(BlockBoundaryType type) diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 33f43ac8caf..d7ec282ec4f 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -45,7 +45,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e TemporaryChange restore_executable { m_current_executable, &executable }; TemporaryChange restore_saved_jump { m_scheduled_jump, static_cast(nullptr) }; - VERIFY(m_saved_exception.is_null()); + TemporaryChange restore_saved_exception { m_saved_exception, {} }; bool pushed_execution_context = false; ExecutionContext execution_context(vm().heap());