mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-19 08:41:53 +00:00
LibJS: Avoid emptying the return value register in try/finally
This works because at the end of the finally chunk, a ContinuePendingUnwind is generated which copies the saved return value register into the return value register. In cases where ContinuePendingUnwind is not generated such as when there is a break statement in the finally block, the fonction will return undefined which is consistent with V8 and SpiderMonkey.
This commit is contained in:
parent
fbdabace8e
commit
6b6e13e28c
Notes:
github-actions[bot]
2025-03-27 12:19:55 +00:00
Author: https://github.com/ananas-dev
Commit: 6b6e13e28c
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4115
3 changed files with 14 additions and 6 deletions
|
@ -274,8 +274,6 @@ public:
|
||||||
void emit_return(ScopedOperand value)
|
void emit_return(ScopedOperand value)
|
||||||
requires(IsOneOf<OpType, Op::Return, Op::Yield>)
|
requires(IsOneOf<OpType, Op::Return, Op::Yield>)
|
||||||
{
|
{
|
||||||
// FIXME: Tell the call sites about the `saved_return_value` destination
|
|
||||||
// And take that into account in the movs below.
|
|
||||||
perform_needed_unwinds<OpType>();
|
perform_needed_unwinds<OpType>();
|
||||||
if (must_enter_finalizer()) {
|
if (must_enter_finalizer()) {
|
||||||
VERIFY(m_current_basic_block->finalizer() != nullptr);
|
VERIFY(m_current_basic_block->finalizer() != nullptr);
|
||||||
|
@ -289,8 +287,6 @@ public:
|
||||||
else
|
else
|
||||||
emit<Bytecode::Op::Mov>(Operand(Register::saved_return_value()), value);
|
emit<Bytecode::Op::Mov>(Operand(Register::saved_return_value()), value);
|
||||||
emit<Bytecode::Op::Mov>(Operand(Register::exception()), add_constant(Value {}));
|
emit<Bytecode::Op::Mov>(Operand(Register::exception()), add_constant(Value {}));
|
||||||
// FIXME: Do we really need to clear the return value register here?
|
|
||||||
emit<Bytecode::Op::Mov>(Operand(Register::return_value()), add_constant(Value {}));
|
|
||||||
emit<Bytecode::Op::Jump>(Label { *m_current_basic_block->finalizer() });
|
emit<Bytecode::Op::Jump>(Label { *m_current_basic_block->finalizer() });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -778,8 +778,6 @@ Interpreter::ResultAndReturnRegister Interpreter::run_executable(Executable& exe
|
||||||
auto return_value = js_undefined();
|
auto return_value = js_undefined();
|
||||||
if (!reg(Register::return_value()).is_empty())
|
if (!reg(Register::return_value()).is_empty())
|
||||||
return_value = reg(Register::return_value());
|
return_value = reg(Register::return_value());
|
||||||
else if (!reg(Register::saved_return_value()).is_empty())
|
|
||||||
return_value = reg(Register::saved_return_value());
|
|
||||||
auto exception = reg(Register::exception());
|
auto exception = reg(Register::exception());
|
||||||
|
|
||||||
vm().run_queued_promise_jobs();
|
vm().run_queued_promise_jobs();
|
||||||
|
|
|
@ -490,3 +490,17 @@ test("Break with nested mixed try-catch/finally", () => {
|
||||||
|
|
||||||
expect(executionOrder).toEqual([1, 2]);
|
expect(executionOrder).toEqual([1, 2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Break in finally return in try", () => {
|
||||||
|
function foo() {
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
return "bar";
|
||||||
|
} finally {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (expect.fail("Continued after do-while loop"));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(foo()).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue