From e3f65f215d240a0f8259d6cfb4f95595eabfe083 Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Fri, 14 Jul 2023 15:50:36 +0200 Subject: [PATCH] LibJS/Bytecode: Do not rethrow caught exception from `finally` If the exception from the `try` block has already been caught by `catch`, we need to clear the saved exception before entering `finally` so that ContinuePendingUnwind will not re-throw it. 9 new passes on test262 :^) --- Userland/Libraries/LibJS/Bytecode/BasicBlock.h | 2 ++ Userland/Libraries/LibJS/Bytecode/Interpreter.cpp | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/BasicBlock.h b/Userland/Libraries/LibJS/Bytecode/BasicBlock.h index dde4c1bed1..f2d7ea917c 100644 --- a/Userland/Libraries/LibJS/Bytecode/BasicBlock.h +++ b/Userland/Libraries/LibJS/Bytecode/BasicBlock.h @@ -19,6 +19,8 @@ struct UnwindInfo { BasicBlock const* finalizer; JS::GCPtr lexical_environment; + + bool handler_called { false }; }; class BasicBlock { diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 02236be0ba..b9ee21a682 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -242,10 +242,10 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu auto& unwind_context = unwind_contexts().last(); if (unwind_context.executable != m_current_executable) break; - if (unwind_context.handler) { + if (unwind_context.handler && !unwind_context.handler_called) { vm().running_execution_context().lexical_environment = unwind_context.lexical_environment; m_current_block = unwind_context.handler; - unwind_context.handler = nullptr; + unwind_context.handler_called = true; accumulator() = exception_value; m_saved_exception = {}; @@ -254,6 +254,11 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Execu } if (unwind_context.finalizer) { m_current_block = unwind_context.finalizer; + // If an exception was thrown inside the corresponding `catch` block, we need to rethrow it + // from the `finally` block. But if the exception is from the `try` block, and has already been + // handled by `catch`, we swallow it. + if (!unwind_context.handler_called) + m_saved_exception = {}; will_jump = true; break; }