|
@@ -618,7 +618,80 @@ 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
|
|
@@ -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();
|
|
@@ -664,6 +737,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
|
|
@@ -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();
|
|
@@ -710,6 +788,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
|
|
@@ -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 {};
|
|
|
}
|
|
@@ -1614,6 +1715,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));
|
|
@@ -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
|