diff --git a/Zend/tests/exception_previous_already_in_chain_destructor.phpt b/Zend/tests/exception_previous_already_in_chain_destructor.phpt new file mode 100644 index 000000000000..d302609aa76c --- /dev/null +++ b/Zend/tests/exception_previous_already_in_chain_destructor.phpt @@ -0,0 +1,29 @@ +--TEST-- +Exception thrown in a destructor during unwinding that already has the pending exception in its previous chain +--FILE-- +pending); + } +} + +function f() { + $pending = new RuntimeException("pending"); + $object = new ThrowsInDestructor($pending); + throw $pending; +} + +try { + f(); +} catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } +} +?> +--EXPECT-- +thrown +pending diff --git a/Zend/tests/fibers/gc-destructor-throw-previous-already-in-chain.phpt b/Zend/tests/fibers/gc-destructor-throw-previous-already-in-chain.phpt new file mode 100644 index 000000000000..0e50ab9fb29a --- /dev/null +++ b/Zend/tests/fibers/gc-destructor-throw-previous-already-in-chain.phpt @@ -0,0 +1,41 @@ +--TEST-- +Exception thrown by a GC destructor that already has the exception of a previous, suspended destructor in its previous chain +--FILE-- +self = $this; + } + + public function __destruct() { + if (self::$pending === null) { + try { + Fiber::suspend(); + } finally { + self::$pending = new RuntimeException("pending"); + throw self::$pending; + } + } + throw new LogicException("thrown", 0, self::$pending); + } +} + +$fiber = new Fiber(function () { + new Cycle; + new Cycle; + try { + gc_collect_cycles(); + } catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } + } +}); +$fiber->start(); +?> +--EXPECT-- +thrown +pending diff --git a/Zend/tests/fibers/unfinished-fiber-with-throw-previous-already-in-chain.phpt b/Zend/tests/fibers/unfinished-fiber-with-throw-previous-already-in-chain.phpt new file mode 100644 index 000000000000..cf27c8eeff02 --- /dev/null +++ b/Zend/tests/fibers/unfinished-fiber-with-throw-previous-already-in-chain.phpt @@ -0,0 +1,28 @@ +--TEST-- +Exception thrown in finally of a fiber destroyed during unwinding that already has the pending exception in its previous chain +--FILE-- +start(); + throw $pending; +} + +try { + f(); +} catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } +} +?> +--EXPECT-- +thrown +pending diff --git a/Zend/tests/generators/finally/yield_throw_previous_already_in_chain.phpt b/Zend/tests/generators/finally/yield_throw_previous_already_in_chain.phpt new file mode 100644 index 000000000000..9205eedbc5ea --- /dev/null +++ b/Zend/tests/generators/finally/yield_throw_previous_already_in_chain.phpt @@ -0,0 +1,30 @@ +--TEST-- +Exception thrown in finally of a generator destroyed during unwinding that already has the pending exception in its previous chain +--FILE-- +current(); + throw $pending; +} + +try { + f(); +} catch (Throwable $t) { + for (; $t !== null; $t = $t->getPrevious()) { + echo $t->getMessage(), "\n"; + } +} +?> +--EXPECT-- +thrown +pending diff --git a/Zend/tests/try/try_finally_previous_already_in_chain.phpt b/Zend/tests/try/try_finally_previous_already_in_chain.phpt new file mode 100644 index 000000000000..fd0bdc20cb79 --- /dev/null +++ b/Zend/tests/try/try_finally_previous_already_in_chain.phpt @@ -0,0 +1,38 @@ +--TEST-- +Exception thrown in finally that already has the pending exception in its previous chain +--FILE-- +getPrevious()) { + echo $t->getMessage(), "\n"; + } + } +} +?> +--EXPECT-- +thrown +pending +thrown +middle +pending diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 2607cae594c5..02fbaf7e1ad1 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -135,6 +135,8 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo } ex = previous; } while (Z_OBJ_P(ex) != add_previous); + + OBJ_RELEASE(add_previous); } /* }}} */