diff --git a/Tests/LibJS/test-bytecode-js.cpp b/Tests/LibJS/test-bytecode-js.cpp index 267d2a7e62..5c21d25fd8 100644 --- a/Tests/LibJS/test-bytecode-js.cpp +++ b/Tests/LibJS/test-bytecode-js.cpp @@ -120,3 +120,20 @@ TEST_CASE(loading_multiple_files) EXPECT(!vm->exception()); } } + +TEST_CASE(catch_exception) +{ + // FIXME: Currently it seems that try/catch with finally is broken so we test both at once. + EXPECT_NO_EXCEPTION_ALL("var hitCatch = false;\n" + "var hitFinally = false;\n" + "try {\n" + " a();\n" + "} catch (e) {\n" + " hitCatch = e instanceof ReferenceError;\n" + " !1\n" // This is here to fix the alignment issue until that is actually resolved. + "} finally {\n" + " hitFinally = true;\n" + "}\n" + "if (hitCatch !== true) throw new Exception('failed');\n" + "if (hitFinally !== true) throw new Exception('failed');"); +} diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index d9da87b4d1..649ee991aa 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -95,7 +95,7 @@ public: Type type() const { return m_type; } size_t length() const; String to_string(Bytecode::Executable const&) const; - void execute(Bytecode::Interpreter&) const; + ThrowCompletionOr execute(Bytecode::Interpreter&) const; void replace_references(BasicBlock const&, BasicBlock const&); static void destroy(Instruction&); diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 3dfb8c509c..5387b05f7f 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -44,6 +44,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable); TemporaryChange restore_executable { m_current_executable, &executable }; + VERIFY(m_saved_exception.is_null()); ExecutionContext execution_context(vm().heap()); if (vm().execution_context_stack().is_empty() || !vm().running_execution_context().lexical_environment) { @@ -76,9 +77,17 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e bool will_return = false; while (!pc.at_end()) { auto& instruction = *pc; - instruction.execute(*this); + auto ran_or_error = instruction.execute(*this); if (vm().exception()) { - m_saved_exception = {}; + if (!ran_or_error.is_error()) { + // FIXME: Until exception is removed use this to make sure we always get the error if there is one. + ran_or_error = throw_completion(vm().exception()->value()); + } + vm().clear_exception(); + } + if (ran_or_error.is_error()) { + auto exception_value = *ran_or_error.throw_completion().value(); + m_saved_exception = make_handle(exception_value); if (m_unwind_contexts.is_empty()) break; auto& unwind_context = m_unwind_contexts.last(); @@ -87,8 +96,8 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e if (unwind_context.handler) { block = unwind_context.handler; unwind_context.handler = nullptr; - accumulator() = vm().exception()->value(); - vm().clear_exception(); + accumulator() = exception_value; + m_saved_exception = {}; will_jump = true; break; } @@ -96,8 +105,6 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e block = unwind_context.finalizer; m_unwind_contexts.take_last(); will_jump = true; - m_saved_exception = Handle::create(vm().exception()); - vm().clear_exception(); break; } } @@ -119,7 +126,7 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e if (pc.at_end() && !will_jump) break; - if (vm().exception()) + if (!m_saved_exception.is_null()) break; } @@ -142,12 +149,6 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e m_manually_entered_frames.take_last(); } - Value exception_value; - if (vm().exception()) { - exception_value = vm().exception()->value(); - vm().clear_exception(); - } - auto return_value = m_return_value.value_or(js_undefined()); m_return_value = {}; @@ -164,8 +165,11 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e vm().finish_execution_generation(); - if (!exception_value.is_empty()) - return { throw_completion(exception_value), move(frame) }; + if (!m_saved_exception.is_null()) { + Value thrown_value = m_saved_exception.value(); + m_saved_exception = {}; + return { throw_completion(thrown_value), move(frame) }; + } return { return_value, move(frame) }; } @@ -180,14 +184,16 @@ void Interpreter::leave_unwind_context() m_unwind_contexts.take_last(); } -void Interpreter::continue_pending_unwind(Label const& resume_label) +ThrowCompletionOr Interpreter::continue_pending_unwind(Label const& resume_label) { if (!m_saved_exception.is_null()) { - vm().set_exception(*m_saved_exception.cell()); + auto result = throw_completion(m_saved_exception.value()); m_saved_exception = {}; - } else { - jump(resume_label); + return result; } + + jump(resume_label); + return {}; } AK::Array, static_cast>(Interpreter::OptimizationLevel::__Count)> Interpreter::s_optimization_pipelines {}; diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index 82d7b6f482..7d5fcfd0c7 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -13,7 +13,6 @@ #include #include #include -#include #include namespace JS::Bytecode { @@ -69,7 +68,7 @@ public: void enter_unwind_context(Optional