mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-27 19:59:03 +00:00
LibJS: Cleanup unwind state when transferring control out of a finalizer
This does two things: * Clear exceptions when transferring control out of a finalizer Otherwise they would resurface at the end of the next finalizer (see test the new test case), or at the end of a function * Pop one scheduled jump when transferring control out of a finalizer This removes one old FIXME
This commit is contained in:
parent
27b238d9af
commit
ada5027163
Notes:
sideshowbarker
2024-07-17 06:38:11 +09:00
Author: https://github.com/Hendiadyoin1
Commit: ada5027163
Pull-request: https://github.com/SerenityOS/serenity/pull/23927
Reviewed-by: https://github.com/ADKaster
8 changed files with 65 additions and 6 deletions
|
@ -2443,7 +2443,11 @@ Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> TryStatement::gener
|
||||||
auto& finalizer_block = generator.make_block();
|
auto& finalizer_block = generator.make_block();
|
||||||
generator.switch_to_basic_block(finalizer_block);
|
generator.switch_to_basic_block(finalizer_block);
|
||||||
generator.emit<Bytecode::Op::LeaveUnwindContext>();
|
generator.emit<Bytecode::Op::LeaveUnwindContext>();
|
||||||
|
|
||||||
|
generator.start_boundary(Bytecode::Generator::BlockBoundaryType::LeaveFinally);
|
||||||
(void)TRY(m_finalizer->generate_bytecode(generator));
|
(void)TRY(m_finalizer->generate_bytecode(generator));
|
||||||
|
generator.end_boundary(Bytecode::Generator::BlockBoundaryType::LeaveFinally);
|
||||||
|
|
||||||
if (!generator.is_current_block_terminated()) {
|
if (!generator.is_current_block_terminated()) {
|
||||||
next_block = &generator.make_block();
|
next_block = &generator.make_block();
|
||||||
auto next_target = Bytecode::Label { *next_block };
|
auto next_target = Bytecode::Label { *next_block };
|
||||||
|
|
|
@ -532,7 +532,10 @@ void Generator::generate_scoped_jump(JumpType type)
|
||||||
switch_to_basic_block(block);
|
switch_to_basic_block(block);
|
||||||
last_was_finally = true;
|
last_was_finally = true;
|
||||||
break;
|
break;
|
||||||
};
|
}
|
||||||
|
case LeaveFinally:
|
||||||
|
emit<Op::LeaveFinally>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
|
|
|
@ -209,6 +209,7 @@ public:
|
||||||
Continue,
|
Continue,
|
||||||
Unwind,
|
Unwind,
|
||||||
ReturnToFinally,
|
ReturnToFinally,
|
||||||
|
LeaveFinally,
|
||||||
LeaveLexicalEnvironment,
|
LeaveLexicalEnvironment,
|
||||||
};
|
};
|
||||||
template<typename OpType>
|
template<typename OpType>
|
||||||
|
@ -232,6 +233,9 @@ public:
|
||||||
break;
|
break;
|
||||||
case ReturnToFinally:
|
case ReturnToFinally:
|
||||||
return;
|
return;
|
||||||
|
case LeaveFinally:
|
||||||
|
emit<Bytecode::Op::LeaveFinally>();
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,8 @@
|
||||||
O(Div) \
|
O(Div) \
|
||||||
O(Dump) \
|
O(Dump) \
|
||||||
O(End) \
|
O(End) \
|
||||||
O(EnterUnwindContext) \
|
|
||||||
O(EnterObjectEnvironment) \
|
O(EnterObjectEnvironment) \
|
||||||
|
O(EnterUnwindContext) \
|
||||||
O(Exp) \
|
O(Exp) \
|
||||||
O(GetById) \
|
O(GetById) \
|
||||||
O(GetByIdWithThis) \
|
O(GetByIdWithThis) \
|
||||||
|
@ -48,10 +48,10 @@
|
||||||
O(GetByValueWithThis) \
|
O(GetByValueWithThis) \
|
||||||
O(GetCalleeAndThisFromEnvironment) \
|
O(GetCalleeAndThisFromEnvironment) \
|
||||||
O(GetIterator) \
|
O(GetIterator) \
|
||||||
O(GetObjectFromIteratorRecord) \
|
|
||||||
O(GetMethod) \
|
O(GetMethod) \
|
||||||
O(GetNewTarget) \
|
O(GetNewTarget) \
|
||||||
O(GetNextMethodFromIteratorRecord) \
|
O(GetNextMethodFromIteratorRecord) \
|
||||||
|
O(GetObjectFromIteratorRecord) \
|
||||||
O(GetImportMeta) \
|
O(GetImportMeta) \
|
||||||
O(GetObjectPropertyIterator) \
|
O(GetObjectPropertyIterator) \
|
||||||
O(GetPrivateById) \
|
O(GetPrivateById) \
|
||||||
|
@ -71,6 +71,7 @@
|
||||||
O(JumpIf) \
|
O(JumpIf) \
|
||||||
O(JumpNullish) \
|
O(JumpNullish) \
|
||||||
O(JumpUndefined) \
|
O(JumpUndefined) \
|
||||||
|
O(LeaveFinally) \
|
||||||
O(LeaveLexicalEnvironment) \
|
O(LeaveLexicalEnvironment) \
|
||||||
O(LeaveUnwindContext) \
|
O(LeaveUnwindContext) \
|
||||||
O(LeftShift) \
|
O(LeftShift) \
|
||||||
|
|
|
@ -368,9 +368,6 @@ void Interpreter::run_bytecode()
|
||||||
auto& running_execution_context = vm().running_execution_context();
|
auto& running_execution_context = vm().running_execution_context();
|
||||||
auto const* old_scheduled_jump = running_execution_context.previously_scheduled_jumps.take_last();
|
auto const* old_scheduled_jump = running_execution_context.previously_scheduled_jumps.take_last();
|
||||||
if (m_scheduled_jump) {
|
if (m_scheduled_jump) {
|
||||||
// FIXME: If we `break` or `continue` in the finally, we need to clear
|
|
||||||
// this field
|
|
||||||
// Same goes for popping an old_scheduled_jump form the stack
|
|
||||||
m_current_block = exchange(m_scheduled_jump, nullptr);
|
m_current_block = exchange(m_scheduled_jump, nullptr);
|
||||||
} else {
|
} else {
|
||||||
m_current_block = &static_cast<Op::ContinuePendingUnwind const&>(instruction).resume_target().block();
|
m_current_block = &static_cast<Op::ContinuePendingUnwind const&>(instruction).resume_target().block();
|
||||||
|
@ -537,6 +534,12 @@ void Interpreter::restore_scheduled_jump()
|
||||||
m_scheduled_jump = call_frame().previously_scheduled_jumps.take_last();
|
m_scheduled_jump = call_frame().previously_scheduled_jumps.take_last();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Interpreter::leave_finally()
|
||||||
|
{
|
||||||
|
reg(Register::exception()) = {};
|
||||||
|
m_scheduled_jump = call_frame().previously_scheduled_jumps.take_last();
|
||||||
|
}
|
||||||
|
|
||||||
void Interpreter::enter_object_environment(Object& object)
|
void Interpreter::enter_object_environment(Object& object)
|
||||||
{
|
{
|
||||||
auto& running_execution_context = vm().running_execution_context();
|
auto& running_execution_context = vm().running_execution_context();
|
||||||
|
@ -1020,6 +1023,12 @@ ThrowCompletionOr<void> Catch::execute_impl(Bytecode::Interpreter& interpreter)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> LeaveFinally::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
{
|
||||||
|
interpreter.leave_finally();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<void> RestoreScheduledJump::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> RestoreScheduledJump::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.restore_scheduled_jump();
|
interpreter.restore_scheduled_jump();
|
||||||
|
@ -2237,6 +2246,11 @@ ByteString Catch::to_byte_string_impl(Bytecode::Executable const& executable) co
|
||||||
format_operand("dst"sv, m_dst, executable));
|
format_operand("dst"sv, m_dst, executable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ByteString LeaveFinally::to_byte_string_impl(Bytecode::Executable const&) const
|
||||||
|
{
|
||||||
|
return ByteString::formatted("LeaveFinally");
|
||||||
|
}
|
||||||
|
|
||||||
ByteString RestoreScheduledJump::to_byte_string_impl(Bytecode::Executable const&) const
|
ByteString RestoreScheduledJump::to_byte_string_impl(Bytecode::Executable const&) const
|
||||||
{
|
{
|
||||||
return ByteString::formatted("RestoreScheduledJump");
|
return ByteString::formatted("RestoreScheduledJump");
|
||||||
|
|
|
@ -69,6 +69,7 @@ public:
|
||||||
void leave_unwind_context();
|
void leave_unwind_context();
|
||||||
void catch_exception(Operand dst);
|
void catch_exception(Operand dst);
|
||||||
void restore_scheduled_jump();
|
void restore_scheduled_jump();
|
||||||
|
void leave_finally();
|
||||||
|
|
||||||
void enter_object_environment(Object&);
|
void enter_object_environment(Object&);
|
||||||
|
|
||||||
|
|
|
@ -452,6 +452,17 @@ private:
|
||||||
Operand m_dst;
|
Operand m_dst;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LeaveFinally final : public Instruction {
|
||||||
|
public:
|
||||||
|
explicit LeaveFinally()
|
||||||
|
: Instruction(Type::LeaveFinally, sizeof(*this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const;
|
||||||
|
ByteString to_byte_string_impl(Bytecode::Executable const&) const;
|
||||||
|
};
|
||||||
|
|
||||||
class RestoreScheduledJump final : public Instruction {
|
class RestoreScheduledJump final : public Instruction {
|
||||||
public:
|
public:
|
||||||
explicit RestoreScheduledJump()
|
explicit RestoreScheduledJump()
|
||||||
|
|
|
@ -118,3 +118,24 @@ test("Nested try/finally/catch with exception in inner context ", () => {
|
||||||
}
|
}
|
||||||
expect(success).toBe(2);
|
expect(success).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Nested try/catch/finally with exception in inner most finally inside loop", () => {
|
||||||
|
success = 0;
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
throw 1;
|
||||||
|
} finally {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expect.fail();
|
||||||
|
} while (expect.fail());
|
||||||
|
} catch (e) {
|
||||||
|
expect.fail();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
success = 1;
|
||||||
|
}
|
||||||
|
expect(success).toBe(1);
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue