From 58e2fe895c0641a01b8c4febaf20c8a98be29e8c Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Fri, 3 Nov 2023 20:47:33 +0200 Subject: [PATCH] LibJS: Stash thrown exception in a register before executing finalizer This kills 2 birds with one stone: 1. It makes sure generated check_exception() calls in the finalizer don't mis-read the pending exception as caused by their matching operation. 2. It implicitly ensures that terminated finally blocks (by a return statement) overwrite any pending exceptions, since they will never execute the ContinuePendingUnwind operation that restores the stashed exception. This additional logic is required in the JIT (as opposed to the interpreter), since the JIT uses the exception register to store and check the possibly-exceptional results from each individual operation, while the interpreter only modifies it when an operation has thrown an exception. --- Userland/Libraries/LibJS/Bytecode/Register.h | 8 +++++++- Userland/Libraries/LibJS/JIT/Compiler.cpp | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/Register.h b/Userland/Libraries/LibJS/Bytecode/Register.h index 0662f552e7..4bd45a98e9 100644 --- a/Userland/Libraries/LibJS/Bytecode/Register.h +++ b/Userland/Libraries/LibJS/Bytecode/Register.h @@ -45,7 +45,13 @@ public: return Register(return_value_index); } - static constexpr u32 reserved_register_count = 5; + static constexpr Register saved_exception() + { + constexpr u32 saved_exception_index = 5; + return Register(saved_exception_index); + } + + static constexpr u32 reserved_register_count = 6; constexpr explicit Register(u32 index) : m_index(index) diff --git a/Userland/Libraries/LibJS/JIT/Compiler.cpp b/Userland/Libraries/LibJS/JIT/Compiler.cpp index a824eeb34f..eb611d251a 100644 --- a/Userland/Libraries/LibJS/JIT/Compiler.cpp +++ b/Userland/Libraries/LibJS/JIT/Compiler.cpp @@ -426,6 +426,8 @@ void Compiler::check_exception() m_assembler.jump(label_for(*handler)); no_exception.link(m_assembler); } else if (auto const* finalizer = current_block().finalizer(); finalizer) { + store_vm_register(Bytecode::Register::saved_exception(), GPR0); + store_vm_register(Bytecode::Register::exception(), GPR1); m_assembler.jump_if(Assembler::Operand::Register(GPR0), Assembler::Condition::NotEqualTo, Assembler::Operand::Register(GPR1), @@ -1247,11 +1249,14 @@ void Compiler::compile_set_variable(Bytecode::Op::SetVariable const& op) void Compiler::compile_continue_pending_unwind(Bytecode::Op::ContinuePendingUnwind const& op) { // re-throw the exception if we reached the end of the finally block and there was no catch block to handle it + load_vm_register(GPR0, Bytecode::Register::saved_exception()); + store_vm_register(Bytecode::Register::exception(), GPR0); + m_assembler.mov(Assembler::Operand::Register(GPR1), Assembler::Operand::Imm(Value().encoded())); + store_vm_register(Bytecode::Register::saved_exception(), GPR1); check_exception(); // if (saved_return_value.is_empty()) goto resume_block; load_vm_register(GPR0, Bytecode::Register::saved_return_value()); - m_assembler.mov(Assembler::Operand::Register(GPR1), Assembler::Operand::Imm(Value().encoded())); m_assembler.jump_if( Assembler::Operand::Register(GPR0), Assembler::Condition::EqualTo,