diff options
author | Luke Wilde <lukew@serenityos.org> | 2022-06-11 23:09:37 +0100 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2022-06-13 07:13:03 +0430 |
commit | c0fadfb9b73b6cc3e415c43e0f9ab8231aa74433 (patch) | |
tree | 034613b552afd38b43c02b679235152a85fb52f2 | |
parent | 455c0b7794feb579623bbb584666854e099d9fdf (diff) | |
download | serenity-c0fadfb9b73b6cc3e415c43e0f9ab8231aa74433.zip |
LibJS/Bytecode: Implement break/continue labels
This is done by keeping track of all the labels that apply to a given
break/continue scope alongside their bytecode target. When a
break/continue with a label is generated, we scan from the most inner
scope to the most outer scope looking for the label, performing any
necessary unwinds on the way. Once the label is found, it is then
jumped to.
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 9 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp | 155 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Generator.cpp | 68 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Generator.h | 16 |
4 files changed, 218 insertions, 30 deletions
diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 743cd5bacd..62f3ef1686 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -113,6 +113,8 @@ public: virtual Completion execute(Interpreter&, GlobalObject&) const override; virtual void dump(int indent) const override; + virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr<void> generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const; FlyString const& label() const { return m_label; } FlyString& label() { return m_label; } @@ -142,6 +144,7 @@ public: using Statement::Statement; virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector<FlyString> const&) const = 0; + virtual Bytecode::CodeGenerationErrorOr<void> generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const; private: virtual bool is_iteration_statement() const final { return true; } @@ -803,6 +806,7 @@ public: virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector<FlyString> const&) const override; virtual void dump(int indent) const override; virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr<void> generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const override; private: NonnullRefPtr<Expression> m_test; @@ -825,6 +829,7 @@ public: virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector<FlyString> const&) const override; virtual void dump(int indent) const override; virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr<void> generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const override; private: NonnullRefPtr<Expression> m_test; @@ -872,6 +877,7 @@ public: virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector<FlyString> const&) const override; virtual void dump(int indent) const override; virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr<void> generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const override; private: RefPtr<ASTNode> m_init; @@ -896,6 +902,7 @@ public: virtual Completion execute(Interpreter&, GlobalObject&) const override; virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr<void> generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const override; virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector<FlyString> const&) const override; virtual void dump(int indent) const override; @@ -921,6 +928,7 @@ public: virtual Completion execute(Interpreter&, GlobalObject&) const override; virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr<void> generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const override; virtual Completion loop_evaluation(Interpreter&, GlobalObject&, Vector<FlyString> const&) const override; virtual void dump(int indent) const override; @@ -2002,6 +2010,7 @@ public: virtual void dump(int indent) const override; virtual Completion execute(Interpreter&, GlobalObject&) const override; virtual Bytecode::CodeGenerationErrorOr<void> generate_bytecode(Bytecode::Generator&) const override; + virtual Bytecode::CodeGenerationErrorOr<void> generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const; Completion execute_impl(Interpreter&, GlobalObject&) const; void add_case(NonnullRefPtr<SwitchCase> switch_case) { m_cases.append(move(switch_case)); } diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index f35c25bfa8..8e4e322b60 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -618,8 +618,81 @@ Bytecode::CodeGenerationErrorOr<void> AssignmentExpression::generate_bytecode(By return {}; } +// 14.13.3 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-labelled-statements-runtime-semantics-evaluation +// LabelledStatement : LabelIdentifier : LabelledItem +Bytecode::CodeGenerationErrorOr<void> LabelledStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + // Return ? LabelledEvaluation of this LabelledStatement with argument « ». + return generate_labelled_evaluation(generator, {}); +} + +// 14.13.4 Runtime Semantics: LabelledEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-labelledevaluation +// LabelledStatement : LabelIdentifier : LabelledItem +Bytecode::CodeGenerationErrorOr<void> LabelledStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<FlyString> const& label_set) const +{ + // Convert the m_labelled_item NNRP to a reference early so we don't have to do it every single time we want to use it. + auto const& labelled_item = *m_labelled_item; + + // 1. Let label be the StringValue of LabelIdentifier. + // NOTE: Not necessary, this is m_label. + + // 2. Let newLabelSet be the list-concatenation of labelSet and « label ». + // FIXME: Avoid copy here. + auto new_label_set = label_set; + new_label_set.append(m_label); + + // 3. Let stmtResult be LabelledEvaluation of LabelledItem with argument newLabelSet. + // NOTE: stmtResult will be in the accumulator after running the generated bytecode. + if (is<IterationStatement>(labelled_item)) { + auto const& iteration_statement = static_cast<IterationStatement const&>(labelled_item); + TRY(iteration_statement.generate_labelled_evaluation(generator, new_label_set)); + } else if (is<SwitchStatement>(labelled_item)) { + auto const& switch_statement = static_cast<SwitchStatement const&>(labelled_item); + TRY(switch_statement.generate_labelled_evaluation(generator, new_label_set)); + } else if (is<LabelledStatement>(labelled_item)) { + auto const& labelled_statement = static_cast<LabelledStatement const&>(labelled_item); + TRY(labelled_statement.generate_labelled_evaluation(generator, new_label_set)); + } else { + auto& labelled_break_block = generator.make_block(); + + // NOTE: We do not need a continuable scope as `continue;` is not allowed outside of iteration statements, throwing a SyntaxError in the parser. + generator.begin_breakable_scope(Bytecode::Label { labelled_break_block }, new_label_set); + TRY(labelled_item.generate_bytecode(generator)); + generator.end_breakable_scope(); + + if (!generator.is_current_block_terminated()) { + generator.emit<Bytecode::Op::Jump>().set_targets( + Bytecode::Label { labelled_break_block }, + {}); + } + + generator.switch_to_basic_block(labelled_break_block); + } + + // 4. If stmtResult.[[Type]] is break and SameValue(stmtResult.[[Target]], label) is true, then + // a. Set stmtResult to NormalCompletion(stmtResult.[[Value]]). + // NOTE: These steps are performed by making labelled break jump straight to the appropriate break block, which preserves the statement result's value in the accumulator. + + // 5. Return Completion(stmtResult). + // NOTE: This is in the accumulator. + return {}; +} + +Bytecode::CodeGenerationErrorOr<void> IterationStatement::generate_labelled_evaluation(Bytecode::Generator&, Vector<FlyString> const&) const +{ + return Bytecode::CodeGenerationError { + this, + "Missing generate_labelled_evaluation()"sv, + }; +} + Bytecode::CodeGenerationErrorOr<void> WhileStatement::generate_bytecode(Bytecode::Generator& generator) const { + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr<void> WhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<FlyString> const& label_set) const +{ // test // jump if_false (true) end (false) body // body @@ -646,8 +719,8 @@ Bytecode::CodeGenerationErrorOr<void> WhileStatement::generate_bytecode(Bytecode Bytecode::Label { end_block }); generator.switch_to_basic_block(body_block); - generator.begin_continuable_scope(Bytecode::Label { test_block }); - generator.begin_breakable_scope(Bytecode::Label { end_block }); + generator.begin_continuable_scope(Bytecode::Label { test_block }, label_set); + generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); TRY(m_body->generate_bytecode(generator)); generator.end_breakable_scope(); generator.end_continuable_scope(); @@ -665,6 +738,11 @@ Bytecode::CodeGenerationErrorOr<void> WhileStatement::generate_bytecode(Bytecode Bytecode::CodeGenerationErrorOr<void> DoWhileStatement::generate_bytecode(Bytecode::Generator& generator) const { + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr<void> DoWhileStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<FlyString> const& label_set) const +{ // jump always (true) body // test // jump if_false (true) end (false) body @@ -692,8 +770,8 @@ Bytecode::CodeGenerationErrorOr<void> DoWhileStatement::generate_bytecode(Byteco Bytecode::Label { end_block }); generator.switch_to_basic_block(body_block); - generator.begin_continuable_scope(Bytecode::Label { test_block }); - generator.begin_breakable_scope(Bytecode::Label { end_block }); + generator.begin_continuable_scope(Bytecode::Label { test_block }, label_set); + generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); TRY(m_body->generate_bytecode(generator)); generator.end_breakable_scope(); generator.end_continuable_scope(); @@ -711,6 +789,11 @@ Bytecode::CodeGenerationErrorOr<void> DoWhileStatement::generate_bytecode(Byteco Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_bytecode(Bytecode::Generator& generator) const { + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<FlyString> const& label_set) const +{ // init // jump always (true) test // test @@ -732,6 +815,9 @@ Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_bytecode(Bytecode:: bool has_lexical_environment = false; + // The breakable scope needs to start here to unwind the potentially created lexical environment for the init bytecode. + generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); + if (m_init) { if (m_init->is_variable_declaration()) { auto& variable_declaration = verify_cast<VariableDeclaration>(*m_init); @@ -783,10 +869,8 @@ Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_bytecode(Bytecode:: } generator.switch_to_basic_block(*body_block_ptr); - generator.begin_continuable_scope(Bytecode::Label { *update_block_ptr }); - generator.begin_breakable_scope(Bytecode::Label { end_block }); + generator.begin_continuable_scope(Bytecode::Label { *update_block_ptr }, label_set); TRY(m_body->generate_bytecode(generator)); - generator.end_breakable_scope(); generator.end_continuable_scope(); if (!generator.is_current_block_terminated()) { @@ -810,6 +894,7 @@ Bytecode::CodeGenerationErrorOr<void> ForStatement::generate_bytecode(Bytecode:: if (has_lexical_environment) generator.end_variable_scope(); + generator.end_breakable_scope(); return {}; } @@ -1345,9 +1430,17 @@ Bytecode::CodeGenerationErrorOr<void> IfStatement::generate_bytecode(Bytecode::G Bytecode::CodeGenerationErrorOr<void> ContinueStatement::generate_bytecode(Bytecode::Generator& generator) const { - generator.perform_needed_unwinds<Bytecode::Op::Jump>(); + if (m_target_label.is_null()) { + generator.perform_needed_unwinds<Bytecode::Op::Jump>(); + generator.emit<Bytecode::Op::Jump>().set_targets( + generator.nearest_continuable_scope(), + {}); + return {}; + } + + auto target_to_jump_to = generator.perform_needed_unwinds_for_labelled_continue_and_return_target_block(m_target_label); generator.emit<Bytecode::Op::Jump>().set_targets( - generator.nearest_continuable_scope(), + target_to_jump_to, {}); return {}; } @@ -1524,9 +1617,17 @@ Bytecode::CodeGenerationErrorOr<void> ThrowStatement::generate_bytecode(Bytecode Bytecode::CodeGenerationErrorOr<void> BreakStatement::generate_bytecode(Bytecode::Generator& generator) const { - generator.perform_needed_unwinds<Bytecode::Op::Jump>(true); + if (m_target_label.is_null()) { + generator.perform_needed_unwinds<Bytecode::Op::Jump>(true); + generator.emit<Bytecode::Op::Jump>().set_targets( + generator.nearest_breakable_scope(), + {}); + return {}; + } + + auto target_to_jump_to = generator.perform_needed_unwinds_for_labelled_break_and_return_target_block(m_target_label); generator.emit<Bytecode::Op::Jump>().set_targets( - generator.nearest_breakable_scope(), + target_to_jump_to, {}); return {}; } @@ -1615,6 +1716,11 @@ Bytecode::CodeGenerationErrorOr<void> TryStatement::generate_bytecode(Bytecode:: Bytecode::CodeGenerationErrorOr<void> SwitchStatement::generate_bytecode(Bytecode::Generator& generator) const { + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr<void> SwitchStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<FlyString> const& label_set) const +{ auto discriminant_reg = generator.allocate_register(); TRY(m_discriminant->generate_bytecode(generator)); generator.emit<Bytecode::Op::Store>(discriminant_reg); @@ -1651,7 +1757,7 @@ Bytecode::CodeGenerationErrorOr<void> SwitchStatement::generate_bytecode(Bytecod generator.emit<Bytecode::Op::Jump>().set_targets(Bytecode::Label { end_block }, {}); } auto current_block = case_blocks.begin(); - generator.begin_breakable_scope(Bytecode::Label { end_block }); + generator.begin_breakable_scope(Bytecode::Label { end_block }, label_set); for (auto& switch_case : m_cases) { generator.switch_to_basic_block(*current_block); @@ -1858,7 +1964,7 @@ static Bytecode::CodeGenerationErrorOr<ForInOfHeadEvaluationResult> for_in_of_he } // 14.7.5.7 ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ), https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset -static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode::Generator& generator, ASTNode const& node, Variant<NonnullRefPtr<ASTNode>, NonnullRefPtr<BindingPattern>> const& lhs, ASTNode const& body, ForInOfHeadEvaluationResult const& head_result, Bytecode::BasicBlock& loop_end, Bytecode::BasicBlock& loop_update) +static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode::Generator& generator, ASTNode const& node, Variant<NonnullRefPtr<ASTNode>, NonnullRefPtr<BindingPattern>> const& lhs, ASTNode const& body, ForInOfHeadEvaluationResult const& head_result, Vector<FlyString> const& label_set, Bytecode::BasicBlock& loop_end, Bytecode::BasicBlock& loop_update) { auto iterator_register = generator.allocate_register(); generator.emit<Bytecode::Op::Store>(iterator_register); @@ -1888,6 +1994,7 @@ static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode: // 6. Repeat, generator.emit<Bytecode::Op::Jump>(Bytecode::Label { loop_update }); generator.switch_to_basic_block(loop_update); + generator.begin_continuable_scope(Bytecode::Label { loop_update }, label_set); // a. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]). generator.emit<Bytecode::Op::Load>(iterator_register); @@ -2042,31 +2149,39 @@ static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode: return {}; } -// 14.7.5.5 Runtime Semantics: ForInOfLoopEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-forinofloopevaluation Bytecode::CodeGenerationErrorOr<void> ForInStatement::generate_bytecode(Bytecode::Generator& generator) const { + return generate_labelled_evaluation(generator, {}); +} + +// 14.7.5.5 Runtime Semantics: ForInOfLoopEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-forinofloopevaluation +Bytecode::CodeGenerationErrorOr<void> ForInStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<FlyString> const& label_set) const +{ auto& loop_end = generator.make_block(); auto& loop_update = generator.make_block(); - generator.begin_breakable_scope(Bytecode::Label { loop_end }); - generator.begin_continuable_scope(Bytecode::Label { loop_update }); + generator.begin_breakable_scope(Bytecode::Label { loop_end }, label_set); auto head_result = TRY(for_in_of_head_evaluation(generator, IterationKind::Enumerate, m_lhs, m_rhs)); // Now perform the rest of ForInOfLoopEvaluation, given that the accumulator holds the iterator we're supposed to iterate over. - return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, loop_end, loop_update); + return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, label_set, loop_end, loop_update); } Bytecode::CodeGenerationErrorOr<void> ForOfStatement::generate_bytecode(Bytecode::Generator& generator) const { + return generate_labelled_evaluation(generator, {}); +} + +Bytecode::CodeGenerationErrorOr<void> ForOfStatement::generate_labelled_evaluation(Bytecode::Generator& generator, Vector<FlyString> const& label_set) const +{ auto& loop_end = generator.make_block(); auto& loop_update = generator.make_block(); - generator.begin_breakable_scope(Bytecode::Label { loop_end }); - generator.begin_continuable_scope(Bytecode::Label { loop_update }); + generator.begin_breakable_scope(Bytecode::Label { loop_end }, label_set); auto head_result = TRY(for_in_of_head_evaluation(generator, IterationKind::Iterate, m_lhs, m_rhs)); // Now perform the rest of ForInOfLoopEvaluation, given that the accumulator holds the iterator we're supposed to iterate over. - return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, loop_end, loop_update); + return for_in_of_body_evaluation(generator, *this, m_lhs, body(), head_result, label_set, loop_end, loop_update); } // 13.3.12.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-meta-properties-runtime-semantics-evaluation diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 74dff1df70..3dc1799104 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -69,7 +69,7 @@ Register Generator::allocate_register() Label Generator::nearest_continuable_scope() const { - return m_continuable_scopes.last(); + return m_continuable_scopes.last().bytecode_target; } void Generator::begin_variable_scope(BindingMode mode, SurroundingScopeKind kind) @@ -99,9 +99,9 @@ void Generator::end_variable_scope() } } -void Generator::begin_continuable_scope(Label continue_target) +void Generator::begin_continuable_scope(Label continue_target, Vector<FlyString> const& language_label_set) { - m_continuable_scopes.append(continue_target); + m_continuable_scopes.append({ continue_target, language_label_set }); start_boundary(BlockBoundaryType::Continue); } @@ -110,13 +110,15 @@ void Generator::end_continuable_scope() m_continuable_scopes.take_last(); end_boundary(BlockBoundaryType::Continue); } + Label Generator::nearest_breakable_scope() const { - return m_breakable_scopes.last(); + return m_breakable_scopes.last().bytecode_target; } -void Generator::begin_breakable_scope(Label breakable_target) + +void Generator::begin_breakable_scope(Label breakable_target, Vector<FlyString> const& language_label_set) { - m_breakable_scopes.append(breakable_target); + m_breakable_scopes.append({ breakable_target, language_label_set }); start_boundary(BlockBoundaryType::Break); } @@ -246,6 +248,60 @@ CodeGenerationErrorOr<void> Generator::emit_delete_reference(JS::ASTNode const& return {}; } +Label Generator::perform_needed_unwinds_for_labelled_break_and_return_target_block(FlyString const& break_label) +{ + size_t current_boundary = m_boundaries.size(); + for (auto& breakable_scope : m_breakable_scopes.in_reverse()) { + for (; current_boundary > 0; --current_boundary) { + auto boundary = m_boundaries[current_boundary - 1]; + if (boundary == BlockBoundaryType::Unwind) { + emit<Bytecode::Op::LeaveUnwindContext>(); + } else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) { + emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Lexical); + } else if (boundary == BlockBoundaryType::LeaveVariableEnvironment) { + emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Var); + } else if (boundary == BlockBoundaryType::Break) { + // Make sure we don't process this boundary twice if the current breakable scope doesn't contain the target label. + --current_boundary; + break; + } + } + + if (breakable_scope.language_label_set.contains_slow(break_label)) + return breakable_scope.bytecode_target; + } + + // We must have a breakable scope available that contains the label, as this should be enforced by the parser. + VERIFY_NOT_REACHED(); +} + +Label Generator::perform_needed_unwinds_for_labelled_continue_and_return_target_block(FlyString const& continue_label) +{ + size_t current_boundary = m_boundaries.size(); + for (auto& continuable_scope : m_continuable_scopes.in_reverse()) { + for (; current_boundary > 0; --current_boundary) { + auto boundary = m_boundaries[current_boundary - 1]; + if (boundary == BlockBoundaryType::Unwind) { + emit<Bytecode::Op::LeaveUnwindContext>(); + } else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) { + emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Lexical); + } else if (boundary == BlockBoundaryType::LeaveVariableEnvironment) { + emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Var); + } 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; + break; + } + } + + if (continuable_scope.language_label_set.contains_slow(continue_label)) + return continuable_scope.bytecode_target; + } + + // We must have a continuable scope available that contains the label, as this should be enforced by the parser. + VERIFY_NOT_REACHED(); +} + String CodeGenerationError::to_string() { return String::formatted("CodeGenerationError in {}: {}", failing_node ? failing_node->class_name() : "<unknown node>", reason_literal); diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index f63f9e7b0f..6af3c9bfdb 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -81,9 +81,9 @@ public: CodeGenerationErrorOr<void> emit_store_to_reference(JS::ASTNode const&); CodeGenerationErrorOr<void> emit_delete_reference(JS::ASTNode const&); - void begin_continuable_scope(Label continue_target); + void begin_continuable_scope(Label continue_target, Vector<FlyString> const& language_label_set); void end_continuable_scope(); - void begin_breakable_scope(Label breakable_target); + void begin_breakable_scope(Label breakable_target, Vector<FlyString> const& language_label_set); void end_breakable_scope(); [[nodiscard]] Label nearest_continuable_scope() const; @@ -186,6 +186,9 @@ public: } } + Label perform_needed_unwinds_for_labelled_break_and_return_target_block(FlyString const& break_label); + Label perform_needed_unwinds_for_labelled_continue_and_return_target_block(FlyString const& continue_label); + void start_boundary(BlockBoundaryType type) { m_boundaries.append(type); } void end_boundary(BlockBoundaryType type) { @@ -200,6 +203,11 @@ private: void grow(size_t); void* next_slot(); + struct LabelableScope { + Label bytecode_target; + Vector<FlyString> language_label_set; + }; + BasicBlock* m_current_basic_block { nullptr }; NonnullOwnPtrVector<BasicBlock> m_root_basic_blocks; NonnullOwnPtr<StringTable> m_string_table; @@ -208,8 +216,8 @@ private: u32 m_next_register { 2 }; u32 m_next_block { 1 }; FunctionKind m_enclosing_function_kind { FunctionKind::Normal }; - Vector<Label> m_continuable_scopes; - Vector<Label> m_breakable_scopes; + Vector<LabelableScope> m_continuable_scopes; + Vector<LabelableScope> m_breakable_scopes; Vector<LexicalScope> m_variable_scopes; Vector<BlockBoundaryType> m_boundaries; }; |