mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 02:48:11 +00:00
LibJS: Generate unwind chains for continue in Bytecode
This works similar to `break` The `try-finally-continue` still do not pass with this, likely because of binding issues.
This commit is contained in:
parent
f5376cb282
commit
d65488b80c
4 changed files with 55 additions and 19 deletions
|
@ -1972,17 +1972,11 @@ Bytecode::CodeGenerationErrorOr<void> ContinueStatement::generate_bytecode(Bytec
|
||||||
// We need to execute the finally block, but tell it to resume
|
// We need to execute the finally block, but tell it to resume
|
||||||
// execution at the designated block
|
// execution at the designated block
|
||||||
if (m_target_label.is_null()) {
|
if (m_target_label.is_null()) {
|
||||||
generator.perform_needed_unwinds<Bytecode::Op::Jump>();
|
generator.generate_continue();
|
||||||
generator.emit<Bytecode::Op::Jump>().set_targets(
|
|
||||||
generator.nearest_continuable_scope(),
|
|
||||||
{});
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto target_to_jump_to = generator.perform_needed_unwinds_for_labelled_continue_and_return_target_block(m_target_label);
|
generator.generate_continue(m_target_label);
|
||||||
generator.emit<Bytecode::Op::Jump>().set_targets(
|
|
||||||
target_to_jump_to,
|
|
||||||
{});
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,23 +335,62 @@ void Generator::generate_break(DeprecatedFlyString const& break_label)
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Label Generator::perform_needed_unwinds_for_labelled_continue_and_return_target_block(DeprecatedFlyString const& continue_label)
|
void Generator::generate_continue()
|
||||||
|
{
|
||||||
|
bool last_was_finally = false;
|
||||||
|
// FIXME: Reduce code duplication
|
||||||
|
for (size_t i = m_boundaries.size(); i > 0; --i) {
|
||||||
|
auto boundary = m_boundaries[i - 1];
|
||||||
|
using enum BlockBoundaryType;
|
||||||
|
switch (boundary) {
|
||||||
|
case Continue:
|
||||||
|
emit<Op::Jump>().set_targets(nearest_continuable_scope(), {});
|
||||||
|
return;
|
||||||
|
case Unwind:
|
||||||
|
if (!last_was_finally)
|
||||||
|
emit<Bytecode::Op::LeaveUnwindContext>();
|
||||||
|
last_was_finally = false;
|
||||||
|
break;
|
||||||
|
case LeaveLexicalEnvironment:
|
||||||
|
emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Lexical);
|
||||||
|
break;
|
||||||
|
case LeaveVariableEnvironment:
|
||||||
|
emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Var);
|
||||||
|
break;
|
||||||
|
case Break:
|
||||||
|
break;
|
||||||
|
case ReturnToFinally: {
|
||||||
|
auto& block = make_block(DeprecatedString::formatted("{}.continue", current_block().name()));
|
||||||
|
emit<Op::ScheduleJump>(Label { block });
|
||||||
|
switch_to_basic_block(block);
|
||||||
|
last_was_finally = true;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Generator::generate_continue(DeprecatedFlyString const& continue_label)
|
||||||
{
|
{
|
||||||
size_t current_boundary = m_boundaries.size();
|
size_t current_boundary = m_boundaries.size();
|
||||||
for (auto& continuable_scope : m_continuable_scopes.in_reverse()) {
|
bool last_was_finally = false;
|
||||||
|
for (auto const& continuable_scope : m_continuable_scopes.in_reverse()) {
|
||||||
for (; current_boundary > 0; --current_boundary) {
|
for (; current_boundary > 0; --current_boundary) {
|
||||||
auto boundary = m_boundaries[current_boundary - 1];
|
auto boundary = m_boundaries[current_boundary - 1];
|
||||||
// FIXME: Handle ReturnToFinally in a graceful manner
|
|
||||||
// We need to execute the finally block, but tell it to resume
|
|
||||||
// execution at the designated label
|
|
||||||
if (boundary == BlockBoundaryType::Unwind) {
|
if (boundary == BlockBoundaryType::Unwind) {
|
||||||
emit<Bytecode::Op::LeaveUnwindContext>();
|
if (!last_was_finally)
|
||||||
|
emit<Bytecode::Op::LeaveUnwindContext>();
|
||||||
|
last_was_finally = false;
|
||||||
} else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) {
|
} else if (boundary == BlockBoundaryType::LeaveLexicalEnvironment) {
|
||||||
emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Lexical);
|
emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Lexical);
|
||||||
} else if (boundary == BlockBoundaryType::LeaveVariableEnvironment) {
|
} else if (boundary == BlockBoundaryType::LeaveVariableEnvironment) {
|
||||||
emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Var);
|
emit<Bytecode::Op::LeaveEnvironment>(Bytecode::Op::EnvironmentMode::Var);
|
||||||
} else if (boundary == BlockBoundaryType::ReturnToFinally) {
|
} else if (boundary == BlockBoundaryType::ReturnToFinally) {
|
||||||
// FIXME: We need to enter the `finally`, while still scheduling the continue to happen
|
auto& block = make_block(DeprecatedString::formatted("{}.continue", current_block().name()));
|
||||||
|
emit<Op::ScheduleJump>(Label { block });
|
||||||
|
switch_to_basic_block(block);
|
||||||
|
last_was_finally = true;
|
||||||
} else if (boundary == BlockBoundaryType::Continue) {
|
} 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.
|
// Make sure we don't process this boundary twice if the current continuable scope doesn't contain the target label.
|
||||||
--current_boundary;
|
--current_boundary;
|
||||||
|
@ -359,8 +398,10 @@ Label Generator::perform_needed_unwinds_for_labelled_continue_and_return_target_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (continuable_scope.language_label_set.contains_slow(continue_label))
|
if (continuable_scope.language_label_set.contains_slow(continue_label)) {
|
||||||
return continuable_scope.bytecode_target;
|
emit<Op::Jump>().set_targets(continuable_scope.bytecode_target, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We must have a continuable scope available that contains the label, as this should be enforced by the parser.
|
// We must have a continuable scope available that contains the label, as this should be enforced by the parser.
|
||||||
|
|
|
@ -216,7 +216,8 @@ public:
|
||||||
void generate_break();
|
void generate_break();
|
||||||
void generate_break(DeprecatedFlyString const& break_label);
|
void generate_break(DeprecatedFlyString const& break_label);
|
||||||
|
|
||||||
Label perform_needed_unwinds_for_labelled_continue_and_return_target_block(DeprecatedFlyString const& continue_label);
|
void generate_continue();
|
||||||
|
void generate_continue(DeprecatedFlyString const& continue_label);
|
||||||
|
|
||||||
void start_boundary(BlockBoundaryType type) { m_boundaries.append(type); }
|
void start_boundary(BlockBoundaryType type) { m_boundaries.append(type); }
|
||||||
void end_boundary(BlockBoundaryType type)
|
void end_boundary(BlockBoundaryType type)
|
||||||
|
|
|
@ -45,7 +45,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e
|
||||||
|
|
||||||
TemporaryChange restore_executable { m_current_executable, &executable };
|
TemporaryChange restore_executable { m_current_executable, &executable };
|
||||||
TemporaryChange restore_saved_jump { m_scheduled_jump, static_cast<BasicBlock const*>(nullptr) };
|
TemporaryChange restore_saved_jump { m_scheduled_jump, static_cast<BasicBlock const*>(nullptr) };
|
||||||
VERIFY(m_saved_exception.is_null());
|
TemporaryChange restore_saved_exception { m_saved_exception, {} };
|
||||||
|
|
||||||
bool pushed_execution_context = false;
|
bool pushed_execution_context = false;
|
||||||
ExecutionContext execution_context(vm().heap());
|
ExecutionContext execution_context(vm().heap());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue