summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
};