Browse Source

LibJS: Propagate finalizers into nested try-catch blocks without them

Hendiadyoin1 1 year ago
parent
commit
301a1fc763

+ 7 - 2
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -2510,8 +2510,13 @@ Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> TryStatement::gener
     if (m_finalizer)
         generator.end_boundary(Bytecode::Generator::BlockBoundaryType::ReturnToFinally);
     if (m_handler) {
-        if (!m_finalizer)
-            unwind_context.emplace(generator, OptionalNone());
+        if (!m_finalizer) {
+            auto const* parent_unwind_context = generator.current_unwind_context();
+            if (parent_unwind_context)
+                unwind_context.emplace(generator, parent_unwind_context->finalizer());
+            else
+                unwind_context.emplace(generator, OptionalNone());
+        }
         unwind_context->set_handler(handler_target.value());
     }
 

+ 2 - 0
Userland/Libraries/LibJS/Bytecode/Generator.h

@@ -276,6 +276,8 @@ public:
         return Operand(Operand::Type::Constant, m_constants.size() - 1);
     }
 
+    UnwindContext const* current_unwind_context() const { return m_current_unwind_context; }
+
 private:
     VM& m_vm;
 

+ 50 - 0
Userland/Libraries/LibJS/Tests/try-catch-finally-nested.js

@@ -53,3 +53,53 @@ test("Nested try/catch/finally with exceptions", () => {
     expect(level3CatchHasBeenExecuted).toBeTrue();
     expect(level3FinallyHasBeenExecuted).toBeTrue();
 });
+
+test("Nested try/catch/finally with return in inner context", () => {
+    success = false;
+    (() => {
+        try {
+            try {
+                return;
+            } catch (e) {
+                expect().fail();
+            }
+        } finally {
+            success = true;
+        }
+        expect().fail();
+    })();
+    expect(success).toBeTrue();
+});
+
+test("Deeply nested try/catch/finally with return in inner context", () => {
+    success = 0;
+    (() => {
+        try {
+            try {
+                try {
+                    try {
+                        try {
+                            return;
+                        } catch (e) {
+                            expect().fail();
+                        } finally {
+                            success += 4;
+                        }
+                    } catch (e) {
+                        expect().fail();
+                    }
+                } catch (e) {
+                    expect().fail();
+                } finally {
+                    success += 2;
+                }
+            } catch (e) {
+                expect().fail();
+            }
+        } finally {
+            success += 1;
+        }
+        expect().fail();
+    })();
+    expect(success).toBe(7);
+});