diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 24cb119328..af4e1ca842 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -1227,6 +1227,7 @@ public: virtual void dump(int indent) const override; virtual Value execute(Interpreter&, GlobalObject&) const override; + virtual void generate_bytecode(Bytecode::Generator&) const override; private: NonnullRefPtr m_block; diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 5ceaefd069..3910cd8e31 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -798,4 +798,58 @@ void BreakStatement::generate_bytecode(Bytecode::Generator& generator) const {}); } +void TryStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + auto& saved_block = generator.current_block(); + + Optional handler_target; + Optional finalizer_target; + + Bytecode::BasicBlock* next_block { nullptr }; + + if (m_finalizer) { + auto& finalizer_block = generator.make_block(); + generator.switch_to_basic_block(finalizer_block); + m_finalizer->generate_bytecode(generator); + if (!generator.is_current_block_terminated()) { + next_block = &generator.make_block(); + auto next_target = Bytecode::Label { *next_block }; + generator.emit(next_target); + } + finalizer_target = Bytecode::Label { finalizer_block }; + } + + if (m_handler) { + auto& handler_block = generator.make_block(); + generator.switch_to_basic_block(handler_block); + if (!m_finalizer) + generator.emit(); + if (!m_handler->parameter().is_empty()) { + // FIXME: We need a separate LexicalEnvironment here + generator.emit(generator.intern_string(m_handler->parameter())); + } + m_handler->body().generate_bytecode(generator); + handler_target = Bytecode::Label { handler_block }; + if (!generator.is_current_block_terminated()) { + if (m_finalizer) { + generator.emit(); + generator.emit(finalizer_target); + } else { + VERIFY(!next_block); + next_block = &generator.make_block(); + auto next_target = Bytecode::Label { *next_block }; + generator.emit(next_target); + } + } + } + + generator.switch_to_basic_block(saved_block); + generator.emit(handler_target, finalizer_target); + m_block->generate_bytecode(generator); + if (m_finalizer && !generator.is_current_block_terminated()) + generator.emit(finalizer_target); + + generator.switch_to_basic_block(next_block ? *next_block : saved_block); +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/BasicBlock.h b/Userland/Libraries/LibJS/Bytecode/BasicBlock.h index 17f0e8f73b..384e373d0c 100644 --- a/Userland/Libraries/LibJS/Bytecode/BasicBlock.h +++ b/Userland/Libraries/LibJS/Bytecode/BasicBlock.h @@ -38,6 +38,11 @@ private: size_t m_offset { 0 }; }; +struct UnwindInfo { + BasicBlock const* handler; + BasicBlock const* finalizer; +}; + class BasicBlock { public: static NonnullOwnPtr create(String name); diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index ed425e2c22..f3d8fabf6e 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -68,6 +68,8 @@ public: m_current_basic_block = █ } + [[nodiscard]] BasicBlock& current_block() { return *m_current_basic_block; } + BasicBlock& make_block(String name = {}) { if (name.is_empty()) diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index b52a712123..61aa3aac3b 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -57,7 +57,10 @@ O(ConcatString) \ O(Increment) \ O(Decrement) \ - O(Throw) + O(Throw) \ + O(EnterUnwindContext) \ + O(LeaveUnwindContext) \ + O(ContinuePendingUnwind) namespace JS::Bytecode { diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index c553fbcca5..0d36b5ac46 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -67,8 +67,25 @@ Value Interpreter::run(Executable const& executable) while (!pc.at_end()) { auto& instruction = *pc; instruction.execute(*this); - if (vm().exception()) - break; + if (vm().exception()) { + m_saved_exception = {}; + if (m_unwind_contexts.is_empty()) + break; + auto& unwind_context = m_unwind_contexts.last(); + if (unwind_context.handler) { + block = unwind_context.handler; + unwind_context.handler = nullptr; + accumulator() = vm().exception()->value(); + vm().clear_exception(); + will_jump = true; + } else if (unwind_context.finalizer) { + block = unwind_context.finalizer; + m_unwind_contexts.take_last(); + will_jump = true; + m_saved_exception = Handle::create(vm().exception()); + vm().clear_exception(); + } + } if (m_pending_jump.has_value()) { block = m_pending_jump.release_value(); will_jump = true; @@ -121,4 +138,23 @@ Value Interpreter::run(Executable const& executable) return return_value; } +void Interpreter::enter_unwind_context(Optional