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.
This commit is contained in:
parent
455c0b7794
commit
c0fadfb9b7
Notes:
sideshowbarker
2024-07-17 10:18:17 +09:00
Author: https://github.com/Lubrsi Commit: https://github.com/SerenityOS/serenity/commit/c0fadfb9b7 Pull-request: https://github.com/SerenityOS/serenity/pull/14262 Reviewed-by: https://github.com/alimpfard
4 changed files with 218 additions and 30 deletions
|
@ -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)); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue