summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Wilde <lukew@serenityos.org>2022-06-11 23:09:37 +0100
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2022-06-13 07:13:03 +0430
commitc0fadfb9b73b6cc3e415c43e0f9ab8231aa74433 (patch)
tree034613b552afd38b43c02b679235152a85fb52f2
parent455c0b7794feb579623bbb584666854e099d9fdf (diff)
downloadserenity-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.h9
-rw-r--r--Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp155
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Generator.cpp68
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Generator.h16
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;
};