From c7265d4ce494cff77c01dd61e8bfc739816c1401 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 29 Jun 2026 16:51:48 -0400 Subject: [PATCH 1/3] Defer user error handlers raised from user-code opcodes A user error handler raised mid-opcode can reenter the VM and free or mutate state the opcode still holds a raw pointer into, a long-standing source of use-after-free and memory-corruption bugs. Defer the handler to the next vm_interrupt safepoint, after the opcode completes. The interpreter and both JITs reach that safepoint through the existing vm_interrupt mechanism. A diagnostic silenced by @ keeps its error_reporting across the deferral; diagnostics raised from internal functions still run synchronously, since the function may gate on EG(exception) or throw right after emitting. Function JIT flushes at the interpreter's safepoints: post-call with the callee frame still on top, each jump-target block, and function entry, spilling the live SSA values before it resumes. Exception handling flushes any buffered diagnostic with the pending exception saved and restored around the handler, so nothing is dropped when a later opcode throws. Under the CALL VM the flush is deferred past a call being assembled, so a throwing handler cannot unwind into a half-built frame and fault in cleanup_unfinished_calls. Fixes GH-20018 Fixes GH-16792 Fixes GH-17416 Fixes GH-18274 Fixes GH-20042 Fixes GH-21245 Fixes GH-22419 --- Zend/tests/ArrayAccess/bug41209.phpt | 3 +- .../bitwise_not_precision_exception.phpt | 1 + Zend/tests/bug63206.phpt | 8 +- Zend/tests/bug70662.phpt | 2 +- Zend/tests/bug70785.phpt | 25 +- Zend/tests/bug72101.phpt | 11 +- Zend/tests/bug76534.phpt | 8 +- Zend/tests/bug78598.phpt | 32 ++- Zend/tests/bug79784.phpt | 18 +- Zend/tests/bug79793.phpt | 4 +- Zend/tests/closures/closure_031.phpt | 2 +- Zend/tests/compound_assign_failure.phpt | 11 +- Zend/tests/concat/bug81705.phpt | 4 +- Zend/tests/dynamic_prop_deprecation_002.phpt | 1 - Zend/tests/exception_in_nested_rope.phpt | 7 +- Zend/tests/exception_in_rope_end.phpt | 8 +- .../exceptions/exception_before_fatal.phpt | 2 +- Zend/tests/falsetoarray_002.phpt | 10 +- Zend/tests/falsetoarray_003.phpt | 2 +- .../error_static_call_trait_method_002.phpt | 9 + Zend/tests/get_class_basic.phpt | 1 + Zend/tests/gh16792.phpt | 16 ++ Zend/tests/gh17416.phpt | 30 ++ Zend/tests/gh21245.phpt | 21 ++ Zend/tests/gh22419.phpt | 19 ++ .../in-de-crement/incdec_bool_exception.phpt | 11 +- .../incdec_strings_exception.phpt | 31 ++- Zend/tests/in-de-crement/incdec_undef.phpt | 4 +- .../increment_diagnostic_change_type.phpt | 2 +- ...nt_diagnostic_change_type_do_operator.phpt | 2 +- ...709_globals_unset_after_undef_warning.phpt | 8 +- ...94_globals_unset_after_string_warning.phpt | 8 +- .../unset_globals_in_error_handler.phpt | 32 +-- ...nset_object_property_in_error_handler.phpt | 34 +-- ...rty_converted_to_obj_in_error_handler.phpt | 1 - ...tion_during_inheritance_can_be_caught.phpt | 4 +- Zend/tests/offsets/array_offset_002.phpt | 6 +- .../offsets/null_offset_dep_promoted.phpt | 8 +- Zend/tests/offsets/null_offset_no_uaf.phpt | 2 +- .../null_offset_unset_via_error_handler.phpt | 3 +- Zend/tests/operator_unsupported_types.phpt | 4 +- Zend/tests/oss_fuzz_54325.phpt | 3 +- Zend/tests/oss_fuzz_61712.phpt | 2 +- Zend/tests/oss_fuzz_61712b.phpt | 2 +- Zend/tests/str_offset_006.phpt | 9 +- Zend/tests/str_offset_007.phpt | 5 +- Zend/tests/str_offset_008.phpt | 2 +- .../strlen_deprecation_to_exception.phpt | 8 +- .../temporary_cleaning_015.phpt | 10 +- .../temporary_cleaning_016.phpt | 7 +- .../non-rep-float-as-int-extra1.phpt | 2 +- .../non-rep-float-as-int-extra3.phpt | 2 +- .../non-rep-float-as-int-extra4.phpt | 2 +- Zend/tests/type_coercion/gh22112.phpt | 2 + Zend/tests/undef_index_to_exception.phpt | 15 +- Zend/tests/undef_var_in_verify_return.phpt | 9 +- Zend/zend.c | 263 +++++++++++++----- Zend/zend.h | 3 + Zend/zend_execute.c | 8 +- Zend/zend_execute_API.c | 3 + Zend/zend_globals.h | 5 + Zend/zend_vm_def.h | 66 ++++- Zend/zend_vm_execute.h | 136 +++++++-- ext/opcache/jit/zend_jit.c | 19 +- ext/opcache/jit/zend_jit_helpers.c | 9 + ext/opcache/jit/zend_jit_ir.c | 144 ++++++++-- ext/opcache/jit/zend_jit_trace.c | 8 +- ext/opcache/jit/zend_jit_vm_helpers.c | 13 + ext/opcache/tests/jit/assign_043.phpt | 4 +- ext/opcache/tests/jit/assign_dim_005.phpt | 7 +- ext/opcache/tests/jit/assign_dim_007.phpt | 4 +- ext/opcache/tests/jit/assign_dim_011.phpt | 10 +- ext/opcache/tests/jit/assign_dim_014.phpt | 3 +- ext/opcache/tests/jit/assign_dim_015.phpt | 2 +- ext/opcache/tests/jit/assign_dim_op_006.phpt | 5 +- ext/opcache/tests/jit/fetch_dim_rw_004.phpt | 12 +- ext/opcache/tests/jit/gh18262-002.phpt | 6 +- ext/opcache/tests/jit/gh18262-003.phpt | 1 + .../tests/jit/qm_assign_undef_exception.phpt | 4 +- ext/opcache/tests/jit/type_check_001.phpt | 4 +- ext/spl/tests/gh18274.phpt | 20 ++ ext/standard/array.c | 98 ++++--- ext/standard/tests/array/gh20042.phpt | 18 ++ .../sort/array_multisort_variation2.phpt | 2 +- .../get_class_methods_variation_001.phpt | 12 +- .../get_parent_class_variation_002.phpt | 12 +- .../is_subclass_of_variation_001.phpt | 20 +- .../is_subclass_of_variation_004.phpt | 20 +- .../method_exists_variation_001.phpt | 16 +- .../tests/image/getimagesize_variation2.phpt | 10 +- ext/zend_test/tests/observer_error_05.phpt | 14 +- tests/lang/bug22592.phpt | 4 +- tests/lang/bug25547.phpt | 2 +- tests/lang/bug25922.phpt | 2 +- 94 files changed, 1085 insertions(+), 399 deletions(-) create mode 100644 Zend/tests/gh16792.phpt create mode 100644 Zend/tests/gh17416.phpt create mode 100644 Zend/tests/gh21245.phpt create mode 100644 Zend/tests/gh22419.phpt create mode 100644 ext/spl/tests/gh18274.phpt create mode 100644 ext/standard/tests/array/gh20042.phpt diff --git a/Zend/tests/ArrayAccess/bug41209.phpt b/Zend/tests/ArrayAccess/bug41209.phpt index 1a7ee8aa515c..f1bc4267d762 100644 --- a/Zend/tests/ArrayAccess/bug41209.phpt +++ b/Zend/tests/ArrayAccess/bug41209.phpt @@ -42,5 +42,6 @@ echo "Done\n"; Fatal error: Uncaught ErrorException: Undefined variable $id in %s:%d Stack trace: #0 %s(%d): env::errorHandler(2, 'Undefined varia...', '%s', %d) -#1 {main} +#1 %s(%d): cache->offsetExists(NULL) +#2 {main} thrown in %s on line %d diff --git a/Zend/tests/bitwise_not_precision_exception.phpt b/Zend/tests/bitwise_not_precision_exception.phpt index e28bf8f4e17b..87631788d561 100644 --- a/Zend/tests/bitwise_not_precision_exception.phpt +++ b/Zend/tests/bitwise_not_precision_exception.phpt @@ -12,4 +12,5 @@ try { } ?> --EXPECT-- +int(-1) The float INF is not representable as an int, cast occurred diff --git a/Zend/tests/bug63206.phpt b/Zend/tests/bug63206.phpt index 6aba55eca1ba..9cb056d48b5c 100644 --- a/Zend/tests/bug63206.phpt +++ b/Zend/tests/bug63206.phpt @@ -22,8 +22,10 @@ set_error_handler(function() { $triggerNotice1++; $triggerNotice2++; ?> ---EXPECT-- +--EXPECTF-- Second handler -Internal handler + +Warning: Undefined variable $triggerInternalNotice in %s on line %d Second handler -Internal handler + +Warning: Undefined variable $triggerInternalNotice in %s on line %d diff --git a/Zend/tests/bug70662.phpt b/Zend/tests/bug70662.phpt index ab540c9d16e5..2bda8141bab4 100644 --- a/Zend/tests/bug70662.phpt +++ b/Zend/tests/bug70662.phpt @@ -14,5 +14,5 @@ var_dump($a); --EXPECT-- array(1) { ["b"]=> - int(2) + int(1) } diff --git a/Zend/tests/bug70785.phpt b/Zend/tests/bug70785.phpt index 05b1b6afa0a0..16789a436ab0 100644 --- a/Zend/tests/bug70785.phpt +++ b/Zend/tests/bug70785.phpt @@ -7,19 +7,32 @@ set_error_handler(function($no, $msg) { throw new Exception($msg); }); -try { +function smart_branch() { if ($a === null) { // ZEND_VM_SMART_BRANCH - undefined_function('Null'); + return 'branch'; } -} catch (Exception $e) { + return 'no branch'; } -try { +function next_opcode() { $c === 3; // ZEND_VM_NEXT_OPCODE - undefined_function(); + return 'done'; +} + +try { + smart_branch(); } catch (Exception $e) { + echo $e->getMessage(), PHP_EOL; } + +try { + next_opcode(); +} catch (Exception $e) { + echo $e->getMessage(), PHP_EOL; +} +echo "okey\n"; ?> -okey --EXPECT-- +Undefined variable $a +Undefined variable $c okey diff --git a/Zend/tests/bug72101.phpt b/Zend/tests/bug72101.phpt index 6362a7ccb303..64393f07ba98 100644 --- a/Zend/tests/bug72101.phpt +++ b/Zend/tests/bug72101.phpt @@ -79,9 +79,10 @@ $foo->bar($a, $b, $c); Fatal error: Uncaught Error: Class "DoesNotExists" not found in %s:%d Stack trace: #0 %s(%d): {closure:%s:%d}(2, 'MethodCallbackB...', '%s', 8) -#1 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#4 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0) -#5 {main} +#1 %sbug72101.php(%d): MethodCallbackByReference->callback(0, 0, 0) +#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#4 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#5 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0) +#6 {main} thrown in %sbug72101.php on line %d diff --git a/Zend/tests/bug76534.phpt b/Zend/tests/bug76534.phpt index c9c897110d6c..9cec1962773b 100644 --- a/Zend/tests/bug76534.phpt +++ b/Zend/tests/bug76534.phpt @@ -10,8 +10,12 @@ $x = "foo"; $y = &$x["2bar"]; ?> --EXPECTF-- -Fatal error: Uncaught Exception: Illegal string offset "2bar" in %s:%d +Fatal error: Uncaught Error: Cannot create references to/from string offsets in %s:%d +Stack trace: +#0 {main} + +Next Exception: Illegal string offset "2bar" in %s:%d Stack trace: #0 %s(%d): {closure:%s:%d}(2, 'Illegal string ...', '%s', 7) #1 {main} - thrown in %sbug76534.php on line %d + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/tests/bug78598.phpt b/Zend/tests/bug78598.phpt index 7e3559f27c26..976040204865 100644 --- a/Zend/tests/bug78598.phpt +++ b/Zend/tests/bug78598.phpt @@ -25,7 +25,31 @@ var_dump($my_var); ?> --EXPECT-- -int(0) -int(0) -int(0) -int(0) +array(1) { + [0]=> + string(3) "xyz" +} +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(3) "xyz" + } + } +} +array(1) { + ["foo"]=> + string(3) "xyz" +} +array(1) { + ["foo"]=> + array(1) { + ["bar"]=> + array(1) { + ["baz"]=> + string(3) "xyz" + } + } +} diff --git a/Zend/tests/bug79784.phpt b/Zend/tests/bug79784.phpt index be1cd729e969..83e64626bba7 100644 --- a/Zend/tests/bug79784.phpt +++ b/Zend/tests/bug79784.phpt @@ -15,6 +15,18 @@ var_dump($a); ?> --EXPECT-- -NULL -NULL -NULL +array(1) { + [""]=> + string(1) "x" +} +array(1) { + [""]=> + string(1) "x" +} +array(1) { + [""]=> + array(1) { + [""]=> + string(1) "x" + } +} diff --git a/Zend/tests/bug79793.phpt b/Zend/tests/bug79793.phpt index 5491a2669ed9..4af55ffd07cb 100644 --- a/Zend/tests/bug79793.phpt +++ b/Zend/tests/bug79793.phpt @@ -18,15 +18,15 @@ var_dump($ary); ?> --EXPECT-- -Undefined array key "foobar" array(1) { ["foobar"]=> int(1) } -Undefined array key "foobarbaz" +Undefined array key "foobar" array(2) { ["foobar"]=> int(1) ["foobarbaz"]=> int(1) } +Undefined array key "foobarbaz" diff --git a/Zend/tests/closures/closure_031.phpt b/Zend/tests/closures/closure_031.phpt index 19f3dc6e3212..7f9d3780e4ff 100644 --- a/Zend/tests/closures/closure_031.phpt +++ b/Zend/tests/closures/closure_031.phpt @@ -15,5 +15,5 @@ try { } ?> --EXPECT-- -Warning: Undefined property: Closure::$a NULL +Warning: Undefined property: Closure::$a diff --git a/Zend/tests/compound_assign_failure.phpt b/Zend/tests/compound_assign_failure.phpt index 0dc9af85f253..4ff85da4561c 100644 --- a/Zend/tests/compound_assign_failure.phpt +++ b/Zend/tests/compound_assign_failure.phpt @@ -22,14 +22,16 @@ try { set_error_handler(function($type, $msg) { throw new Exception($msg); }); +function concat(&$a, $b) { $a .= $b; } + try { $a = []; - $a .= "foo"; + concat($a, "foo"); } catch (Throwable $e) { var_dump($a); } try { $a = "foo"; - $a .= []; + concat($a, []); } catch (Throwable $e) { var_dump($a); } $x = new stdClass; @@ -202,9 +204,8 @@ var_dump($x); int(1) int(1) int(1) -array(0) { -} -string(3) "foo" +string(8) "Arrayfoo" +string(8) "fooArray" object(stdClass)#%d (0) { } int(1) diff --git a/Zend/tests/concat/bug81705.phpt b/Zend/tests/concat/bug81705.phpt index 1c00b1c77d4b..10eea6c0a35c 100644 --- a/Zend/tests/concat/bug81705.phpt +++ b/Zend/tests/concat/bug81705.phpt @@ -15,5 +15,5 @@ $my_var .= $GLOBALS["arr"]; var_dump($my_var); ?> --EXPECT-- -error -string(6) "aArray" \ No newline at end of file +string(6) "aArray" +error \ No newline at end of file diff --git a/Zend/tests/dynamic_prop_deprecation_002.phpt b/Zend/tests/dynamic_prop_deprecation_002.phpt index bd0d3aa5a7df..20182d2a8923 100644 --- a/Zend/tests/dynamic_prop_deprecation_002.phpt +++ b/Zend/tests/dynamic_prop_deprecation_002.phpt @@ -15,4 +15,3 @@ try { ?> --EXPECT-- Err: Creation of dynamic property class@anonymous::$y is deprecated -Exception: Cannot create dynamic property class@anonymous::$y diff --git a/Zend/tests/exception_in_nested_rope.phpt b/Zend/tests/exception_in_nested_rope.phpt index 7afd1412354a..27cfe6d77168 100644 --- a/Zend/tests/exception_in_nested_rope.phpt +++ b/Zend/tests/exception_in_nested_rope.phpt @@ -15,4 +15,9 @@ try { ?> --EXPECTF-- Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in %s on line %d -Exception + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/exception_in_rope_end.phpt b/Zend/tests/exception_in_rope_end.phpt index 3e6402c60f42..da6fa6a61903 100644 --- a/Zend/tests/exception_in_rope_end.phpt +++ b/Zend/tests/exception_in_rope_end.phpt @@ -13,5 +13,9 @@ try { } ?> ---EXPECT-- -Exception +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/exceptions/exception_before_fatal.phpt b/Zend/tests/exceptions/exception_before_fatal.phpt index edc63b2394cd..847cc2cd8f96 100644 --- a/Zend/tests/exceptions/exception_before_fatal.phpt +++ b/Zend/tests/exceptions/exception_before_fatal.phpt @@ -61,4 +61,4 @@ string(23) "Undefined variable $foo" string(23) "Undefined variable $foo" string(23) "Undefined variable $foo" string(23) "Undefined variable $foo" -string(23) "Undefined variable $foo" +string(23) "Undefined variable $foo" \ No newline at end of file diff --git a/Zend/tests/falsetoarray_002.phpt b/Zend/tests/falsetoarray_002.phpt index c01b79954596..639cd51b3661 100644 --- a/Zend/tests/falsetoarray_002.phpt +++ b/Zend/tests/falsetoarray_002.phpt @@ -11,5 +11,13 @@ $a[0][$d]='b'; var_dump($a); ?> --EXPECT-- +array(1) { + [0]=> + array(1) { + [""]=> + string(1) "b" + } +} Err: Automatic conversion of false to array is deprecated -string(0) "" +Err: Undefined variable $d +Err: Using null as an array offset is deprecated, use an empty string instead diff --git a/Zend/tests/falsetoarray_003.phpt b/Zend/tests/falsetoarray_003.phpt index 117e443ef958..d62dff41941c 100644 --- a/Zend/tests/falsetoarray_003.phpt +++ b/Zend/tests/falsetoarray_003.phpt @@ -11,6 +11,6 @@ $a=[]; ?> DONE --EXPECTF-- +DONE Err: The float %f is not representable as an int, cast occurred Err: Undefined array key %i -DONE diff --git a/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt index 97831a8d65f0..51c22102bddd 100644 --- a/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt +++ b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt @@ -28,4 +28,13 @@ try { ?> --EXPECT-- +object(Closure)#2 (2) { + ["function"]=> + string(13) "Foo::myMethod" + ["parameter"]=> + array(1) { + ["$foo"]=> + string(10) "" + } +} Caught: Calling static trait method Foo::myMethod is deprecated, it should only be called on a class using the trait diff --git a/Zend/tests/get_class_basic.phpt b/Zend/tests/get_class_basic.phpt index f6dcbccb6916..d8809ba9d9cf 100644 --- a/Zend/tests/get_class_basic.phpt +++ b/Zend/tests/get_class_basic.phpt @@ -54,6 +54,7 @@ $f1->testNull(); echo "Done\n"; ?> --EXPECTF-- +string(3) "foo" Calling get_class() without arguments is deprecated Deprecated: Calling get_class() without arguments is deprecated in %s on line %d diff --git a/Zend/tests/gh16792.phpt b/Zend/tests/gh16792.phpt new file mode 100644 index 000000000000..ed2eae538d3d --- /dev/null +++ b/Zend/tests/gh16792.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-16792 (Assertion failure when an error handler mutates $GLOBALS during a by-ref sort) +--FILE-- + +--EXPECT-- +ok diff --git a/Zend/tests/gh17416.phpt b/Zend/tests/gh17416.phpt new file mode 100644 index 000000000000..1fd0e7d5ce04 --- /dev/null +++ b/Zend/tests/gh17416.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-17416 (Assertion failure when a throwing error handler runs while an array is unset during foreach) +--FILE-- + $val) { + if ($val === 2) { + unset($a[$fusion]); + } + } +} +function foo($a, bool $b): bool { + do { + $res = bar($a); + } while ($res === null && $n !== $n2); +} +foo([2, 'a' => 5], false); +?> +--EXPECTF-- +Fatal error: Uncaught Exception: Undefined variable $fusion in %s:%d +Stack trace: +#0 %s(%d): Error2Exception(2, 'Undefined varia...', '%s', %d) +#1 %s(%d): bar(Array) +#2 %s(%d): foo(Array, false) +#3 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh21245.phpt b/Zend/tests/gh21245.phpt new file mode 100644 index 000000000000..c922b3865c3c --- /dev/null +++ b/Zend/tests/gh21245.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-21245 (Use-after-free in ASSIGN_DIM when the error handler grows the array holding a typed-property reference) +--FILE-- +x = ""; +$a[0] =& $test->x; +var_dump($a[0] = $v); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Cannot assign null to reference held by property Test::$x of type string in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh22419.phpt b/Zend/tests/gh22419.phpt new file mode 100644 index 000000000000..f6e3241801d3 --- /dev/null +++ b/Zend/tests/gh22419.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-22419 (UAF/refcount assertion in array-offset concat when the error handler replaces the LHS) +--FILE-- + +--EXPECT-- +ok diff --git a/Zend/tests/in-de-crement/incdec_bool_exception.phpt b/Zend/tests/in-de-crement/incdec_bool_exception.phpt index f819af1e2395..d7ccf55e406f 100644 --- a/Zend/tests/in-de-crement/incdec_bool_exception.phpt +++ b/Zend/tests/in-de-crement/incdec_bool_exception.phpt @@ -7,15 +7,22 @@ set_error_handler(function($severity, $m) { throw new Exception($m, $severity); }); +function inc(&$v) { + $v++; +} +function dec(&$v) { + $v--; +} + $values = [false, true]; foreach ($values as $value) { try { - $value++; + inc($value); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } try { - $value--; + dec($value); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } diff --git a/Zend/tests/in-de-crement/incdec_strings_exception.phpt b/Zend/tests/in-de-crement/incdec_strings_exception.phpt index 5acbb1041222..b5b00e6c00d6 100644 --- a/Zend/tests/in-de-crement/incdec_strings_exception.phpt +++ b/Zend/tests/in-de-crement/incdec_strings_exception.phpt @@ -13,6 +13,13 @@ set_error_handler(function($severity, $m) { throw new Exception($m, $severity); }); +function inc(&$v) { + $v++; +} +function dec(&$v) { + $v--; +} + $values = [ '', ' ', @@ -26,47 +33,49 @@ $values = [ '🐘' ]; foreach ($values as $value) { + $v = $value; try { - $value++; + inc($v); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } - var_dump($value); + var_dump($v); + $v = $value; try { - $value--; + dec($v); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } - var_dump($value); + var_dump($v); } ?> --EXPECT-- Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(0) "" +string(1) "1" Deprecated: Decrement on empty string is deprecated as non-numeric -string(0) "" +int(-1) Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead string(1) " " Deprecated: Decrement on non-numeric string has no effect and is deprecated string(1) " " Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "199A" +string(4) "199B" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "199A" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "A199" +string(4) "A200" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "A199" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "199Z" +string(4) "200A" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "199Z" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "Z199" +string(4) "Z200" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "Z199" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(11) "Hello world" +string(11) "Hello worle" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(11) "Hello world" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead diff --git a/Zend/tests/in-de-crement/incdec_undef.phpt b/Zend/tests/in-de-crement/incdec_undef.phpt index db560e31c99f..549c64fea87e 100644 --- a/Zend/tests/in-de-crement/incdec_undef.phpt +++ b/Zend/tests/in-de-crement/incdec_undef.phpt @@ -14,6 +14,7 @@ unset($x); var_dump(++$x); ?> --EXPECT-- +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP NULL @@ -21,6 +22,5 @@ Undefined variable $x NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL -Undefined variable $x int(1) +Undefined variable $x diff --git a/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt b/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt index 11a0edac1917..b55c8c6b811e 100644 --- a/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt +++ b/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt @@ -31,8 +31,8 @@ var_dump($x); DONE --EXPECT-- string(1) "1" -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) "foo!" string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "!" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" DONE diff --git a/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt b/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt index 8e63ad6aeed4..9308a1a7bd3d 100644 --- a/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt +++ b/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt @@ -23,8 +23,8 @@ var_dump($x); ?> DONE --EXPECT-- -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) "foo!" string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "!" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" DONE diff --git a/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt b/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt index e981800eeac1..1f1fc7228134 100644 --- a/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt +++ b/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt @@ -21,16 +21,16 @@ var_dump(++$x); ?> --EXPECT-- POST DEC +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL POST INC -Undefined variable $x NULL +Undefined variable $x PRE DEC +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL PRE INC -Undefined variable $x int(1) +Undefined variable $x diff --git a/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt b/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt index f52dff52e631..e39184e68154 100644 --- a/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt +++ b/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt @@ -25,14 +25,14 @@ var_dump(++$x); ?> --EXPECT-- POST DEC -Decrement on non-numeric string has no effect and is deprecated string(1) " " -PRE DEC Decrement on non-numeric string has no effect and is deprecated +PRE DEC string(1) " " +Decrement on non-numeric string has no effect and is deprecated POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(1) " " -PRE INC Increment on non-numeric string is deprecated, use str_increment() instead +PRE INC string(1) " " +Increment on non-numeric string is deprecated, use str_increment() instead diff --git a/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt index 31ffea22467b..88302517ce05 100644 --- a/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt @@ -78,53 +78,53 @@ unset($x); --EXPECT-- NULL (only --) POST DEC -Decrement on type null has no effect, this will change in the next major version of PHP NULL -PRE DEC Decrement on type null has no effect, this will change in the next major version of PHP +PRE DEC NULL +Decrement on type null has no effect, this will change in the next major version of PHP Empty string POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(0) "" +Increment on non-numeric string is deprecated, use str_increment() instead POST DEC -Decrement on empty string is deprecated as non-numeric string(0) "" +Decrement on empty string is deprecated as non-numeric PRE INC -Increment on non-numeric string is deprecated, use str_increment() instead string(1) "1" +Increment on non-numeric string is deprecated, use str_increment() instead PRE DEC -Decrement on empty string is deprecated as non-numeric int(-1) +Decrement on empty string is deprecated as non-numeric Non fill ASCII (only ++) POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(4) " ad " -PRE INC Increment on non-numeric string is deprecated, use str_increment() instead +PRE INC string(4) " ad " +Increment on non-numeric string is deprecated, use str_increment() instead Bool POST INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(false) +Increment on type bool has no effect, this will change in the next major version of PHP POST DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(false) +Decrement on type bool has no effect, this will change in the next major version of PHP PRE INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(false) +Increment on type bool has no effect, this will change in the next major version of PHP PRE DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(false) +Decrement on type bool has no effect, this will change in the next major version of PHP POST INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(true) +Increment on type bool has no effect, this will change in the next major version of PHP POST DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(true) +Decrement on type bool has no effect, this will change in the next major version of PHP PRE INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(true) +Increment on type bool has no effect, this will change in the next major version of PHP PRE DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(true) +Decrement on type bool has no effect, this will change in the next major version of PHP diff --git a/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt index 4d2a4705588a..b00ba707cecf 100644 --- a/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt @@ -87,57 +87,57 @@ var_dump(--$c->a); unset($c->a); ?> --EXPECT-- -string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL +string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL (only --) POST DEC -string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL -PRE DEC string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" +PRE DEC NULL +string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" Empty string POST INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(0) "" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" POST DEC -string(54) "Decrement on empty string is deprecated as non-numeric" string(0) "" +string(54) "Decrement on empty string is deprecated as non-numeric" PRE INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "1" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" PRE DEC -string(54) "Decrement on empty string is deprecated as non-numeric" int(-1) +string(54) "Decrement on empty string is deprecated as non-numeric" Non fill ASCII (only ++) POST INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) " ad " -PRE INC string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" +PRE INC string(4) " ad " +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" Bool POST INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" POST DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" PRE INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" PRE DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" POST INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" POST DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" PRE INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" PRE DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" diff --git a/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt index d435c9fb4029..55d3283ead91 100644 --- a/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt @@ -20,6 +20,5 @@ try { var_dump($c->a); ?> --EXPECT-- -Cannot increment stdClass object(stdClass)#2 (0) { } diff --git a/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt index 7a59cca70bd6..77020277f3b8 100644 --- a/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt +++ b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt @@ -24,7 +24,7 @@ var_dump(new C()); ?> --EXPECTF-- -Exception: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice +Exception: Return type of C::getTimestamp() should either be compatible with DateTime::getTimestamp(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice object(C)#%d (3) { ["date"]=> string(%d) "%s" @@ -32,4 +32,4 @@ object(C)#%d (3) { int(3) ["timezone"]=> string(3) "UTC" -} +} \ No newline at end of file diff --git a/Zend/tests/offsets/array_offset_002.phpt b/Zend/tests/offsets/array_offset_002.phpt index 4d56258ef8b8..ece8a53c7215 100644 --- a/Zend/tests/offsets/array_offset_002.phpt +++ b/Zend/tests/offsets/array_offset_002.phpt @@ -12,7 +12,9 @@ function x(&$s){ x($y); var_dump($y); ?> ---EXPECT-- +--EXPECTF-- Err: The float 1.0E+20 is not representable as an int, cast occurred -array(0) { +array(1) { + [%d]=> + int(1) } diff --git a/Zend/tests/offsets/null_offset_dep_promoted.phpt b/Zend/tests/offsets/null_offset_dep_promoted.phpt index c26095d4f745..55145c88837a 100644 --- a/Zend/tests/offsets/null_offset_dep_promoted.phpt +++ b/Zend/tests/offsets/null_offset_dep_promoted.phpt @@ -13,5 +13,9 @@ try { echo $e::class, ': ', $e->getMessage(), PHP_EOL; } ?> ---EXPECT-- -Exception: Using null as an array offset is deprecated, use an empty string instead +--EXPECTF-- +Fatal error: Uncaught Exception: Using null as an array offset is deprecated, use an empty string instead in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(%d, 'Using null as a...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/offsets/null_offset_no_uaf.phpt b/Zend/tests/offsets/null_offset_no_uaf.phpt index 38a1b9868345..3352438688fc 100644 --- a/Zend/tests/offsets/null_offset_no_uaf.phpt +++ b/Zend/tests/offsets/null_offset_no_uaf.phpt @@ -13,5 +13,5 @@ $ary[null] = 1; echo "\nSuccess\n"; ?> --EXPECTF-- -Using null as an array offset is deprecated, use an empty string instead Success +Using null as an array offset is deprecated, use an empty string instead diff --git a/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt b/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt index cfaa2b3b20d9..1dd363bab30b 100644 --- a/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt +++ b/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt @@ -14,6 +14,5 @@ $b = [0, null => $a]; echo "\nSuccess\n"; ?> --EXPECTF-- -string(72) "Using null as an array offset is deprecated, use an empty string instead" - Success +string(72) "Using null as an array offset is deprecated, use an empty string instead" diff --git a/Zend/tests/operator_unsupported_types.phpt b/Zend/tests/operator_unsupported_types.phpt index 904ac402b372..1f85bfee871e 100644 --- a/Zend/tests/operator_unsupported_types.phpt +++ b/Zend/tests/operator_unsupported_types.phpt @@ -2104,7 +2104,7 @@ Cannot increment stdClass Cannot decrement stdClass Cannot increment resource Cannot decrement resource -Warning: Increment on non-numeric string is deprecated, use str_increment() instead No error for fop++ -Warning: Decrement on non-numeric string has no effect and is deprecated +Warning: Increment on non-numeric string is deprecated, use str_increment() instead No error for foo-- +Warning: Decrement on non-numeric string has no effect and is deprecated \ No newline at end of file diff --git a/Zend/tests/oss_fuzz_54325.phpt b/Zend/tests/oss_fuzz_54325.phpt index d998acf1ffed..18b108681c2d 100644 --- a/Zend/tests/oss_fuzz_54325.phpt +++ b/Zend/tests/oss_fuzz_54325.phpt @@ -14,6 +14,5 @@ $$x++; var_dump($x); ?> --EXPECT-- +string(3) "oof" string(23) "Undefined variable $oof" -object(stdClass)#2 (0) { -} diff --git a/Zend/tests/oss_fuzz_61712.phpt b/Zend/tests/oss_fuzz_61712.phpt index 5e3aa9060fde..655fb56c1421 100644 --- a/Zend/tests/oss_fuzz_61712.phpt +++ b/Zend/tests/oss_fuzz_61712.phpt @@ -16,5 +16,5 @@ $c->a %= 10; var_dump($c->a); ?> --EXPECT-- -Undefined property: C::$a int(0) +Undefined property: C::$a diff --git a/Zend/tests/oss_fuzz_61712b.phpt b/Zend/tests/oss_fuzz_61712b.phpt index 8ee93b97b432..bfb3d2757366 100644 --- a/Zend/tests/oss_fuzz_61712b.phpt +++ b/Zend/tests/oss_fuzz_61712b.phpt @@ -16,5 +16,5 @@ $c->a %= 10; var_dump($c->a); ?> --EXPECT-- +int(0) Undefined property: C::$a -int(5) diff --git a/Zend/tests/str_offset_006.phpt b/Zend/tests/str_offset_006.phpt index 3cdf91656bd2..01325defe954 100644 --- a/Zend/tests/str_offset_006.phpt +++ b/Zend/tests/str_offset_006.phpt @@ -9,8 +9,13 @@ set_error_handler(function($code, $msg) { $a[$y]=$a.=($y); var_dump($a); ?> ---EXPECT-- +--EXPECTF-- Err: Undefined variable $y +Err: Undefined variable $a Err: Undefined variable $y Err: String offset cast occurred -NULL + +Fatal error: Uncaught Error: Cannot assign an empty string to a string offset in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/tests/str_offset_007.phpt b/Zend/tests/str_offset_007.phpt index 3998ac401f63..09d254cac3bf 100644 --- a/Zend/tests/str_offset_007.phpt +++ b/Zend/tests/str_offset_007.phpt @@ -11,6 +11,9 @@ $a[0][$d]='b'; var_dump($a); ?> --EXPECT-- +array(1) { + [0]=> + string(1) "b" +} Err: Undefined variable $d Err: String offset cast occurred -string(0) "" diff --git a/Zend/tests/str_offset_008.phpt b/Zend/tests/str_offset_008.phpt index e99e46e59e74..7a88136d5298 100644 --- a/Zend/tests/str_offset_008.phpt +++ b/Zend/tests/str_offset_008.phpt @@ -12,7 +12,7 @@ var_dump($a[0][$b]); var_dump($a); ?> --EXPECT-- +string(1) "x" Err: Undefined variable $b Err: String offset cast occurred -string(1) "x" int(8) diff --git a/Zend/tests/strlen_deprecation_to_exception.phpt b/Zend/tests/strlen_deprecation_to_exception.phpt index 7b616f3b6432..48cba977a06a 100644 --- a/Zend/tests/strlen_deprecation_to_exception.phpt +++ b/Zend/tests/strlen_deprecation_to_exception.phpt @@ -13,5 +13,9 @@ try { } ?> ---EXPECT-- -strlen(): Passing null to parameter #1 ($string) of type string is deprecated +--EXPECTF-- +Fatal error: Uncaught Exception: strlen(): Passing null to parameter #1 ($string) of type string is deprecated in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(%s) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt index f5e115faa970..8f0246dee1d6 100644 --- a/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt +++ b/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt @@ -13,5 +13,11 @@ try { } ?> DONE ---EXPECT-- -DONE +--EXPECTF-- +Array + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Array to string...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt index 8dc4c8ea3148..1a920b50d22d 100644 --- a/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt +++ b/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt @@ -13,4 +13,9 @@ try { DONE --EXPECTF-- Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in %s on line %d -DONE + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Array to string...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt index 42b91f4a656d..47bd2c6b05bc 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt @@ -14,5 +14,5 @@ var_dump($b | 1); ?> --EXPECTF-- -Implicit conversion from float-string "1.0E+4%d" to int loses precision int(%d) +Implicit conversion from float-string "1.0E+4%d" to int loses precision diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt index 0bfbeb01a2d0..5d44d8021910 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt @@ -14,5 +14,5 @@ var_dump(isset($ary[1.0E+42])); ?> --EXPECT-- +bool(true) The float 1.0E+42 is not representable as an int, cast occurred -bool(false) diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt index 8134ce08d2ee..3f49fcea85ba 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt @@ -14,5 +14,5 @@ var_dump(\array_key_exists(1.0E+42, $ary)); ?> --EXPECT-- +bool(true) The float 1.0E+42 is not representable as an int, cast occurred -bool(false) diff --git a/Zend/tests/type_coercion/gh22112.phpt b/Zend/tests/type_coercion/gh22112.phpt index 84fdc393a828..dbbaf66757b9 100644 --- a/Zend/tests/type_coercion/gh22112.phpt +++ b/Zend/tests/type_coercion/gh22112.phpt @@ -31,5 +31,7 @@ try { ?> --EXPECT-- +take_bool entered bool: unexpected NAN value was coerced to bool +take_string entered string: unexpected NAN value was coerced to string diff --git a/Zend/tests/undef_index_to_exception.phpt b/Zend/tests/undef_index_to_exception.phpt index bbe13c0e71d0..5ed70b1f1494 100644 --- a/Zend/tests/undef_index_to_exception.phpt +++ b/Zend/tests/undef_index_to_exception.phpt @@ -35,12 +35,9 @@ try { } ?> ---EXPECT-- -Undefined array key 0 -array(0) { -} -Undefined array key "key" -array(0) { -} -Undefined global variable $test -Undefined variable $test +--EXPECTF-- +Fatal error: Uncaught Exception: Undefined array key 0 in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined array...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/undef_var_in_verify_return.phpt b/Zend/tests/undef_var_in_verify_return.phpt index f3966c6992d6..0a9dff20343f 100644 --- a/Zend/tests/undef_var_in_verify_return.phpt +++ b/Zend/tests/undef_var_in_verify_return.phpt @@ -15,9 +15,14 @@ test(); ?> --EXPECTF-- -Fatal error: Uncaught ErrorException: Undefined variable $test in %s:%d +Fatal error: Uncaught TypeError: test(): Return value must be of type string, null returned in %s:%d +Stack trace: +#0 %s(%d): test() +#1 {main} + +Next ErrorException: Undefined variable $test in %s:%d Stack trace: #0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 8) #1 %s(%d): test() #2 {main} - thrown in %s on line %d + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/zend.c b/Zend/zend.c index 07692db85196..f0df91c11e3d 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1438,9 +1438,10 @@ ZEND_API zval *zend_get_configuration_directive(zend_string *name) /* {{{ */ } \ } while (0) -ZEND_API ZEND_COLD void zend_error_zstr_at( +static void zend_call_user_error_handler( int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) { + int type = orig_type & E_ALL; zval params[4]; zval retval; zval orig_user_error_handler; @@ -1448,11 +1449,199 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_class_entry *saved_class_entry = NULL; zend_stack loop_var_stack; zend_stack delayed_oplines_stack; - int type = orig_type & E_ALL; bool orig_record_errors; zend_err_buf orig_errors_buf; zend_result res; + if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF + || !(EG(user_error_handler_error_reporting) & type) + || EG(error_handling) != EH_NORMAL) { + zend_error_cb(orig_type, error_filename, error_lineno, message); + return; + } + + zend_flush_deferred_errors(); + + ZVAL_STR_COPY(¶ms[1], message); + ZVAL_LONG(¶ms[0], type); + + if (error_filename) { + ZVAL_STR_COPY(¶ms[2], error_filename); + } else { + ZVAL_NULL(¶ms[2]); + } + + ZVAL_LONG(¶ms[3], error_lineno); + + ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler)); + ZVAL_UNDEF(&EG(user_error_handler)); + + /* User error handler may include() additional PHP files. + * If an error was generated during compilation PHP will compile + * such scripts recursively, but some CG() variables may be + * inconsistent. */ + + in_compilation = CG(in_compilation); + if (in_compilation) { + saved_class_entry = CG(active_class_entry); + CG(active_class_entry) = NULL; + SAVE_STACK(loop_var_stack); + SAVE_STACK(delayed_oplines_stack); + CG(in_compilation) = 0; + } + + orig_record_errors = EG(record_errors); + EG(record_errors) = false; + + orig_errors_buf = EG(errors); + memset(&EG(errors), 0, sizeof(EG(errors))); + + res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params); + + EG(record_errors) = orig_record_errors; + EG(errors) = orig_errors_buf; + + if (res == SUCCESS) { + if (Z_TYPE(retval) != IS_UNDEF) { + if (Z_TYPE(retval) == IS_FALSE) { + zend_error_cb(orig_type, error_filename, error_lineno, message); + } + zval_ptr_dtor(&retval); + } + } else if (!EG(exception)) { + /* The user error handler failed, use built-in error handler */ + zend_error_cb(orig_type, error_filename, error_lineno, message); + } + + if (in_compilation) { + CG(active_class_entry) = saved_class_entry; + RESTORE_STACK(loop_var_stack); + RESTORE_STACK(delayed_oplines_stack); + CG(in_compilation) = 1; + } + + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[1]); + + if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF) { + ZVAL_COPY_VALUE(&EG(user_error_handler), &orig_user_error_handler); + } else { + zval_ptr_dtor(&orig_user_error_handler); + } +} + +static zend_always_inline bool zend_can_defer_error(int type) +{ + if (type == E_USER_ERROR || type == E_RECOVERABLE_ERROR) { + return false; + } + if (CG(in_compilation) || !EG(current_execute_data) || !EG(current_execute_data)->func) { + return false; + } + zend_execute_data *ex = EG(current_execute_data); + if (!ZEND_USER_CODE(ex->func->type) || !ex->opline) { + return false; + } + switch (ex->opline->opcode) { + case ZEND_DECLARE_CLASS: + case ZEND_DECLARE_CLASS_DELAYED: + case ZEND_DECLARE_ANON_CLASS: + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: + case ZEND_DO_FCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_DO_UCALL: + case ZEND_FETCH_CONSTANT: + case ZEND_FETCH_CLASS_CONSTANT: + case ZEND_INCLUDE_OR_EVAL: + return false; + } + return true; +} + +static void zend_defer_error( + int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) +{ + zend_error_info *info = emalloc(sizeof(zend_error_info)); + info->type = orig_type; + info->lineno = error_lineno; + info->error_reporting = EG(error_reporting); + info->filename = error_filename ? zend_string_copy(error_filename) : NULL; + info->message = zend_string_copy(message); + + EG(deferred_errors).size++; + if (EG(deferred_errors).size > EG(deferred_errors).capacity) { + uint32_t capacity = EG(deferred_errors).capacity + ? EG(deferred_errors).capacity + (EG(deferred_errors).capacity >> 1) : 2; + EG(deferred_errors).errors = erealloc(EG(deferred_errors).errors, sizeof(zend_error_info *) * capacity); + EG(deferred_errors).capacity = capacity; + } + EG(deferred_errors).errors[EG(deferred_errors).size - 1] = info; + + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); +} + +ZEND_API void zend_flush_deferred_errors(void) +{ + if (!EG(deferred_errors).size) { + return; + } + + /* A user error handler cannot run while an exception is pending. Keep the + * errors buffered so ZEND_HANDLE_EXCEPTION can flush them with the + * exception cleared, instead of silently dropping them. */ + if (EG(exception)) { + return; + } + + zend_err_buf buf = EG(deferred_errors); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); + + int orig_error_reporting = EG(error_reporting); + for (uint32_t i = 0; i < buf.size; i++) { + zend_error_info *info = buf.errors[i]; + if (!EG(exception)) { + EG(error_reporting) = info->error_reporting; + zend_call_user_error_handler(info->type, info->filename, info->lineno, info->message); + } + if (info->filename) { + zend_string_release(info->filename); + } + zend_string_release(info->message); + efree_size(info, sizeof(zend_error_info)); + } + EG(error_reporting) = orig_error_reporting; + efree(buf.errors); +} + +ZEND_API void zend_free_deferred_errors(void) +{ + if (!EG(deferred_errors).size) { + return; + } + for (uint32_t i = 0; i < EG(deferred_errors).size; i++) { + zend_error_info *info = EG(deferred_errors).errors[i]; + if (info->filename) { + zend_string_release(info->filename); + } + zend_string_release(info->message); + efree_size(info, sizeof(zend_error_info)); + } + efree(EG(deferred_errors).errors); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); +} + +ZEND_API ZEND_COLD void zend_error_zstr_at( + int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) +{ + int type = orig_type & E_ALL; + /* If we're executing a function during SCCP, count any warnings that may be emitted, * but don't perform any other error handling. */ if (EG(capture_warnings_during_sccp)) { @@ -1485,6 +1674,7 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_error_info *info = emalloc(sizeof(zend_error_info)); info->type = type; info->lineno = error_lineno; + info->error_reporting = EG(error_reporting); info->filename = zend_string_copy(error_filename); info->message = zend_string_copy(message); EG(errors).size++; @@ -1546,72 +1736,10 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_error_cb(orig_type, error_filename, error_lineno, message); break; default: - /* Handle the error in user space */ - ZVAL_STR_COPY(¶ms[1], message); - ZVAL_LONG(¶ms[0], type); - - if (error_filename) { - ZVAL_STR_COPY(¶ms[2], error_filename); - } else { - ZVAL_NULL(¶ms[2]); - } - - ZVAL_LONG(¶ms[3], error_lineno); - - ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler)); - ZVAL_UNDEF(&EG(user_error_handler)); - - /* User error handler may include() additional PHP files. - * If an error was generated during compilation PHP will compile - * such scripts recursively, but some CG() variables may be - * inconsistent. */ - - in_compilation = CG(in_compilation); - if (in_compilation) { - saved_class_entry = CG(active_class_entry); - CG(active_class_entry) = NULL; - SAVE_STACK(loop_var_stack); - SAVE_STACK(delayed_oplines_stack); - CG(in_compilation) = 0; - } - - orig_record_errors = EG(record_errors); - EG(record_errors) = false; - - orig_errors_buf = EG(errors); - memset(&EG(errors), 0, sizeof(EG(errors))); - - res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params); - - EG(record_errors) = orig_record_errors; - EG(errors) = orig_errors_buf; - - if (res == SUCCESS) { - if (Z_TYPE(retval) != IS_UNDEF) { - if (Z_TYPE(retval) == IS_FALSE) { - zend_error_cb(orig_type, error_filename, error_lineno, message); - } - zval_ptr_dtor(&retval); - } - } else if (!EG(exception)) { - /* The user error handler failed, use built-in error handler */ - zend_error_cb(orig_type, error_filename, error_lineno, message); - } - - if (in_compilation) { - CG(active_class_entry) = saved_class_entry; - RESTORE_STACK(loop_var_stack); - RESTORE_STACK(delayed_oplines_stack); - CG(in_compilation) = 1; - } - - zval_ptr_dtor(¶ms[2]); - zval_ptr_dtor(¶ms[1]); - - if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF) { - ZVAL_COPY_VALUE(&EG(user_error_handler), &orig_user_error_handler); + if (zend_can_defer_error(type)) { + zend_defer_error(orig_type, error_filename, error_lineno, message); } else { - zval_ptr_dtor(&orig_user_error_handler); + zend_call_user_error_handler(orig_type, error_filename, error_lineno, message); } break; } @@ -1975,6 +2103,7 @@ ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handl zend_result ret = SUCCESS; if (op_array) { zend_execute(op_array, retval); + zend_flush_deferred_errors(); if (UNEXPECTED(EG(exception))) { if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { zend_user_exception_handler(); diff --git a/Zend/zend.h b/Zend/zend.h index 0d5303192b57..302f76039295 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -128,6 +128,7 @@ typedef struct _zend_inheritance_cache_entry zend_inheritance_cache_entry; typedef struct _zend_error_info { int type; uint32_t lineno; + int error_reporting; zend_string *filename; zend_string *message; } zend_error_info; @@ -453,6 +454,8 @@ ZEND_API void zend_begin_record_errors(void); ZEND_API void zend_emit_recorded_errors(void); ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors); ZEND_API void zend_free_recorded_errors(void); +ZEND_API void zend_flush_deferred_errors(void); +ZEND_API void zend_free_deferred_errors(void); END_EXTERN_C() #define DEBUG_BACKTRACE_PROVIDE_OBJECT (1<<0) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 1b28ce25fe37..928bc3395e90 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4316,13 +4316,17 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt(zend_execute_data *ca zend_atomic_bool_store_ex(&EG(vm_interrupt), false); if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(call); + } else { + zend_flush_deferred_errors(); + if (zend_interrupt_function) { + zend_interrupt_function(call); + } } } #define ZEND_VM_INTERRUPT_CHECK() do { \ if (UNEXPECTED(zend_atomic_bool_load_ex(&EG(vm_interrupt)))) { \ + SAVE_OPLINE(); \ ZEND_VM_INTERRUPT(); \ } \ } while (0) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index cb48e6234371..0fd95ed7a5e3 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -192,6 +192,7 @@ void init_executor(void) /* {{{ */ EG(record_errors) = false; memset(&EG(errors), 0, sizeof(EG(errors))); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); EG(filename_override) = NULL; EG(lineno_override) = -1; @@ -445,6 +446,8 @@ void shutdown_executor(void) /* {{{ */ bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup); #endif + zend_free_deferred_errors(); + zend_try { zend_stream_shutdown(); } zend_end_try(); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index f78567cfaa74..8a267ff7149e 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -306,6 +306,11 @@ struct _zend_executor_globals { bool record_errors; zend_err_buf errors; + /* Diagnostics whose user error handler is deferred to the next VM + * safepoint, so the handler cannot run mid-opcode and corrupt a + * structure the opcode still holds a pointer into. */ + zend_err_buf deferred_errors; + /* Override filename or line number of thrown errors and exceptions */ zend_string *filename_override; zend_long lineno_override; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index ec9a97eeb254..95f53eb8053d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2992,6 +2992,10 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -8223,6 +8227,21 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, -1, op_num, 0); @@ -10635,23 +10654,44 @@ ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; + } + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 34b2455ce439..3bad09e34261 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1157,6 +1157,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -3399,6 +3403,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_HANDLE_EXCEPT { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX -1, 0)); @@ -4040,23 +4059,44 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; + } + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } @@ -53962,6 +54002,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -56088,6 +56132,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HANDLE_EXCEPTION_S { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX -1, 0)); @@ -56729,23 +56788,44 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; + } + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } @@ -110304,6 +110384,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index fbbfab6b243c..2c47d2690f62 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1425,6 +1425,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op int call_level = 0; void *checkpoint = NULL; bool recv_emitted = false; /* emitted at least one RECV opcode */ + bool entry_flush_emitted = false; uint8_t smart_branch_opcode; uint32_t target_label, target_label2; uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_mem_info; @@ -1574,7 +1575,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op zend_jit_set_last_valid_opline(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start); } if (ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) { - zend_jit_check_timeout(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start, NULL); + zend_jit_check_loop_timeout(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start); } if (!ssa->cfg.blocks[b].len) { zend_jit_bb_end(&ctx, b); @@ -1604,6 +1605,13 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op phi = phi->next; } } + if ((ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) + && ssa->cfg.blocks[b].start != 0 + && !(ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER)) { + if (!zend_jit_deferred_error_deopt(&ctx, op_array, ssa, b, op_array->opcodes + ssa->cfg.blocks[b].start, ssa->cfg.blocks[b].start)) { + goto jit_failure; + } + } end = ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1; for (i = ssa->cfg.blocks[b].start; i <= end; i++) { zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[i] : NULL; @@ -1612,6 +1620,13 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op call_level++; } + if (!entry_flush_emitted) { + entry_flush_emitted = true; + if (!zend_jit_deferred_error_deopt(&ctx, op_array, ssa, b, opline, i)) { + goto jit_failure; + } + } + if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) { switch (opline->opcode) { case ZEND_PRE_INC: @@ -2934,7 +2949,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } if (!zend_jit_leave_func(&ctx, op_array, NULL, MAY_BE_ANY, left_frame, - NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, 1)) { + NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0)) { goto jit_failure; } } diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 64a48068f378..2b7d03ad11ea 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3437,6 +3437,15 @@ static void ZEND_FASTCALL zend_jit_exception_in_interrupt_handler_helper(void) } } +static void ZEND_FASTCALL zend_jit_check_deferred_errors(zend_execute_data *execute_data) +{ + if (EG(deferred_errors).size && execute_data->call) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } else { + zend_flush_deferred_errors(); + } +} + static zend_string* ZEND_FASTCALL zend_jit_rope_end(zend_string **rope, uint32_t count) { zend_string *ret; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index b0b04456706b..e54182b4369b 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -1944,6 +1944,32 @@ static void zend_jit_check_timeout(zend_jit_ctx *jit, const zend_op *opline, con } } +static void zend_jit_check_loop_timeout(zend_jit_ctx *jit, const zend_op *opline) +{ + ir_ref if_interrupt, if_timeout, if_deferred, ride_path, cont_path; + + if_interrupt = ir_IF(ir_LOAD_U8(jit_EG(vm_interrupt))); + ir_IF_TRUE_cold(if_interrupt); + + if_timeout = ir_IF(ir_LOAD_U8(jit_EG(timed_out))); + ir_IF_TRUE(if_timeout); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_FALSE(if_timeout); + + if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + ir_IF_FALSE(if_deferred); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_TRUE(if_deferred); + ride_path = ir_END(); + + ir_IF_FALSE(if_interrupt); + cont_path = ir_END(); + + ir_MERGE_2(ride_path, cont_path); +} + static void zend_jit_vm_enter(zend_jit_ctx *jit, ir_ref to_opline) { // ZEND_VM_ENTER() @@ -2073,16 +2099,17 @@ static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit) ir_CALL(IR_VOID, ir_CONST_FUNC(zend_timeout)); ir_MERGE_WITH_EMPTY_TRUE(if_timeout); + ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_jit_check_deferred_errors), jit_FP(jit)); if (zend_interrupt_function) { ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_interrupt_function), jit_FP(jit)); - if_exception = ir_IF(ir_LOAD_A(jit_EG(exception))); - ir_IF_TRUE(if_exception); - ir_CALL(IR_VOID, ir_CONST_FUNC(zend_jit_exception_in_interrupt_handler_helper)); - ir_MERGE_WITH_EMPTY_FALSE(if_exception); - - jit_STORE_FP(jit, ir_LOAD_A(jit_EG(current_execute_data))); - jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline))); } + if_exception = ir_IF(ir_LOAD_A(jit_EG(exception))); + ir_IF_TRUE(if_exception); + ir_CALL(IR_VOID, ir_CONST_FUNC(zend_jit_exception_in_interrupt_handler_helper)); + ir_MERGE_WITH_EMPTY_FALSE(if_exception); + + jit_STORE_FP(jit, ir_LOAD_A(jit_EG(current_execute_data))); + jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline))); if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) { zend_jit_tailcall_handler(jit, ir_LOAD_A(jit_IP(jit))); @@ -2528,9 +2555,6 @@ static int zend_jit_trace_exit_stub(zend_jit_ctx *jit) ref = ir_LOAD_A(jit_EX(opline)); jit_STORE_IP(jit, ref); - // check for interrupt (try to avoid this ???) - zend_jit_check_timeout(jit, NULL, NULL); - addr = zend_jit_orig_opline_handler(jit); if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) { zend_jit_tailcall_handler(jit, addr); @@ -3132,6 +3156,7 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_free_extra_named_params); REGISTER_HELPER(zend_jit_free_call_frame); REGISTER_HELPER(zend_jit_exception_in_interrupt_handler_helper); + REGISTER_HELPER(zend_jit_check_deferred_errors); REGISTER_HELPER(zend_jit_verify_arg_slow); REGISTER_HELPER(zend_missing_arg_error); REGISTER_HELPER(zend_jit_only_vars_by_reference); @@ -4339,6 +4364,68 @@ static int zend_jit_store_var(zend_jit_ctx *jit, uint32_t info, int var, int ssa return zend_jit_spill_store(jit, src, dst, info, set_type); } +static bool zend_jit_ssa_var_live_at(const zend_ssa *ssa, int v, uint32_t i) +{ + int use = ssa->vars[v].use_chain; + + while (use >= 0) { + if ((uint32_t)use >= i) { + return 1; + } + use = zend_ssa_next_use(ssa->ops, v, use); + } + return 0; +} + +static int zend_jit_materialize_live_vars(zend_jit_ctx *jit, const zend_op_array *op_array, zend_ssa *ssa, int b, uint32_t i) +{ + int v; + zend_ssa_phi *phi; + + if (!jit->ra) { + return 1; + } + phi = ssa->blocks[b].phis; + while (phi) { + v = phi->ssa_var; + if (phi->pi < 0 + && jit->ra[v].ref + && !(jit->ra[v].flags & ZREG_LOAD)) { + if (!zend_jit_store_var(jit, ssa->var_info[v].type, ssa->vars[v].var, v, 1)) { + return 0; + } + } + phi = phi->next; + } + for (v = 0; v < ssa->vars_count; v++) { + if (jit->ra[v].ref + && !(jit->ra[v].flags & ZREG_LOAD) + && ssa->vars[v].definition >= 0 + && (uint32_t)ssa->vars[v].definition < i + && zend_jit_ssa_var_live_at(ssa, v, i)) { + if (!zend_jit_store_var(jit, ssa->var_info[v].type, ssa->vars[v].var, v, 1)) { + return 0; + } + } + } + return 1; +} + +static int zend_jit_deferred_error_deopt(zend_jit_ctx *jit, const zend_op_array *op_array, zend_ssa *ssa, int b, const zend_op *opline, uint32_t i) +{ + ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + + ir_IF_TRUE_cold(if_deferred); + if (!zend_jit_materialize_live_vars(jit, op_array, ssa, b, i)) { + return 0; + } + ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_FALSE(if_deferred); + return 1; +} + static int zend_jit_store_ref(zend_jit_ctx *jit, uint32_t info, int var, int32_t src, bool set_type) { zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var)); @@ -10597,7 +10684,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen * doing it later once it's popped off. There is code further * down that handles when there isn't an interrupt function. */ - if (zend_interrupt_function) { + if (zend_interrupt_function || !trace) { // JIT: if (EG(vm_interrupt)) zend_fcall_interrupt(execute_data); ir_ref if_interrupt = ir_IF(ir_LOAD_U8(jit_EG(vm_interrupt))); ir_IF_TRUE_cold(if_interrupt); @@ -10718,9 +10805,9 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen /* If there isn't a zend_interrupt_function, the timeout is * handled here because it's more efficient. */ - if (!zend_interrupt_function) { + if (!zend_interrupt_function && trace) { // TODO: Can we avoid checking for interrupts after each call ??? - if (trace && jit->last_valid_opline != opline) { + if (trace) { int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM); exit_addr = zend_jit_trace_get_exit_addr(exit_point); @@ -11061,8 +11148,7 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, bool left_frame, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info, - int indirect_var_access, - int may_throw) + int indirect_var_access) { bool may_be_top_frame = JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || @@ -11086,6 +11172,17 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, !TRACE_FRAME_NO_NEED_RELEASE_THIS(JIT_G(current_frame))); ir_ref call_info = IR_UNUSED, ref, cold_path = IR_UNUSED; + { + ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + ir_IF_TRUE_cold(if_deferred); + ir_ref saved_ced = ir_LOAD_A(jit_EG(current_execute_data)); + ir_STORE(jit_EX(opline), jit_IP(jit)); + ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); + ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); + ir_STORE(jit_EG(current_execute_data), saved_ced); + ir_MERGE_WITH_EMPTY_FALSE(if_deferred); + } + if (may_need_call_helper) { if (!left_frame) { left_frame = true; @@ -11167,8 +11264,6 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, if (fast_path) { ir_MERGE_WITH(fast_path); } - // TODO: avoid EG(excption) check for $this->foo() calls - may_throw = 1; } // JIT: EG(vm_stack_top) = (zval*)execute_data @@ -11211,13 +11306,8 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, && (!JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) { const zend_op *next_opline = trace->opline; - if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) - && (op1_info & MAY_BE_RC1) - && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { - /* exception might be thrown during destruction of unused return value */ - // JIT: if (EG(exception)) - ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); - } + // JIT: if (EG(exception)) + ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); do { trace++; } while (trace->op == ZEND_JIT_TRACE_INIT_CALL); @@ -11249,11 +11339,7 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, zend_jit_set_last_valid_opline(jit, trace->opline); return 1; - } else if (may_throw || - (((opline->op1_type & (IS_VAR|IS_TMP_VAR)) - && (op1_info & MAY_BE_RC1) - && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) - && (!JIT_G(current_frame) || TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))))) { + } else { // JIT: if (EG(exception)) ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 77bfaa03473a..993dbd088d75 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5596,7 +5596,6 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, } } else { int j; - int may_throw = 0; bool left_frame = 0; if (!zend_jit_return(&ctx, opline, op_array, @@ -5638,17 +5637,12 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, if (!zend_jit_free_cv(&ctx, info, j)) { goto jit_failure; } - if (info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_RESOURCE)) { - if (info & MAY_BE_RC1) { - may_throw = 1; - } - } } } } if (!zend_jit_leave_func(&ctx, op_array, opline, op1_info, left_frame, p + 1, &zend_jit_traces[ZEND_JIT_TRACE_NUM], - (op_array_ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, may_throw)) { + (op_array_ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0)) { goto jit_failure; } } diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 85a81c1573bc..8422a928eb8c 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -102,6 +102,11 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPC { zend_execute_data *old_execute_data; + if (UNEXPECTED(EG(deferred_errors).size)) { + EX(opline) = opline; + zend_flush_deferred_errors(); + } + if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_clean_and_cache_symbol_table(EX(symbol_table)); } @@ -1114,6 +1119,14 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (execute_data->prev_execute_data == prev_execute_data) { /* Enter into function */ + if (UNEXPECTED(EG(deferred_errors).size)) { + EX(opline) = opline; + zend_flush_deferred_errors(); + if (UNEXPECTED(EG(exception))) { + stop = ZEND_JIT_TRACE_STOP_EXCEPTION; + break; + } + } prev_call = NULL; if (level > ZEND_JIT_TRACE_MAX_CALL_DEPTH) { stop = ZEND_JIT_TRACE_STOP_TOO_DEEP; diff --git a/ext/opcache/tests/jit/assign_043.phpt b/ext/opcache/tests/jit/assign_043.phpt index edb0cb212ab0..6c86b94e545f 100644 --- a/ext/opcache/tests/jit/assign_043.phpt +++ b/ext/opcache/tests/jit/assign_043.phpt @@ -17,5 +17,5 @@ try { echo $e->getMessage(), "\n"; } ?> ---EXPECT-- -Undefined variable $b +--EXPECTF-- +Undefined variable $b \ No newline at end of file diff --git a/ext/opcache/tests/jit/assign_dim_005.phpt b/ext/opcache/tests/jit/assign_dim_005.phpt index e7bfdabacc4f..b839739574bb 100644 --- a/ext/opcache/tests/jit/assign_dim_005.phpt +++ b/ext/opcache/tests/jit/assign_dim_005.phpt @@ -16,6 +16,11 @@ $a[$c] = 'x' ; var_dump($a); ?> --EXPECT-- +array(1) { + [""]=> + string(1) "x" +} Error: Undefined variable $c +Error: Using null as an array offset is deprecated, use an empty string instead Error: Undefined variable $c -NULL +Error: Using null as an array offset is deprecated, use an empty string instead diff --git a/ext/opcache/tests/jit/assign_dim_007.phpt b/ext/opcache/tests/jit/assign_dim_007.phpt index 0df4e291ae93..590ab14b6670 100644 --- a/ext/opcache/tests/jit/assign_dim_007.phpt +++ b/ext/opcache/tests/jit/assign_dim_007.phpt @@ -18,7 +18,9 @@ x($y); var_dump($x,$y); ?> --EXPECT-- -array(0) { +array(1) { + [0]=> + int(1) } array(1) { [0]=> diff --git a/ext/opcache/tests/jit/assign_dim_011.phpt b/ext/opcache/tests/jit/assign_dim_011.phpt index 5df415c89e6c..daad27634196 100644 --- a/ext/opcache/tests/jit/assign_dim_011.phpt +++ b/ext/opcache/tests/jit/assign_dim_011.phpt @@ -18,7 +18,11 @@ try { } ?> DONE ---EXPECT-- +--EXPECTF-- Err: Automatic conversion of false to array is deprecated -Exception: Cannot use a scalar value as an array -DONE + +Fatal error: Uncaught Error: Cannot use a scalar value as an array in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(8192, 'Automatic conve...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/ext/opcache/tests/jit/assign_dim_014.phpt b/ext/opcache/tests/jit/assign_dim_014.phpt index 574b05258421..45958d82edab 100644 --- a/ext/opcache/tests/jit/assign_dim_014.phpt +++ b/ext/opcache/tests/jit/assign_dim_014.phpt @@ -14,5 +14,6 @@ $a[$y] = function(){}; ?> DONE --EXPECT-- -Error: Undefined variable $y DONE +Error: Undefined variable $y +Error: Using null as an array offset is deprecated, use an empty string instead diff --git a/ext/opcache/tests/jit/assign_dim_015.phpt b/ext/opcache/tests/jit/assign_dim_015.phpt index 2a5b6d46d37a..3cfb75a31b74 100644 --- a/ext/opcache/tests/jit/assign_dim_015.phpt +++ b/ext/opcache/tests/jit/assign_dim_015.phpt @@ -14,5 +14,5 @@ $my_var[] = $y; ?> DONE --EXPECT-- -Error: Undefined variable $y DONE +Error: Undefined variable $y diff --git a/ext/opcache/tests/jit/assign_dim_op_006.phpt b/ext/opcache/tests/jit/assign_dim_op_006.phpt index 8bfb9197177a..b2b606245be8 100644 --- a/ext/opcache/tests/jit/assign_dim_op_006.phpt +++ b/ext/opcache/tests/jit/assign_dim_op_006.phpt @@ -15,4 +15,7 @@ $my_var[000000000000000000000100000000000000000000000000000000000000000000000000 var_dump($my_var); ?> --EXPECT-- -int(0) +array(1) { + [0]=> + string(3) "xyz" +} diff --git a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt index ae639c8f56ba..b0f18f9746c5 100644 --- a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt +++ b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt @@ -12,12 +12,12 @@ $k=[]; $y[$k]++; ?> --EXPECTF-- -Fatal error: Uncaught TypeError: {closure:%s:%d}(): Argument #1 ($y) must be of type y, int given, called in %s on line %d and defined in %s:%d +Fatal error: Uncaught TypeError: Cannot access offset of type array on array in %s:%d Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 5) -#1 {main} +#0 {main} -Next TypeError: Cannot access offset of type array on array in %sfetch_dim_rw_004.php:5 +Next TypeError: {closure:%s:%d}(): Argument #1 ($y) must be of type y, int given, called in %s on line %d and defined in %s:%d Stack trace: -#0 {main} - thrown in %sfetch_dim_rw_004.php on line 5 +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 5) +#1 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/ext/opcache/tests/jit/gh18262-002.phpt b/ext/opcache/tests/jit/gh18262-002.phpt index b82723abaa29..c4de9ff0375a 100644 --- a/ext/opcache/tests/jit/gh18262-002.phpt +++ b/ext/opcache/tests/jit/gh18262-002.phpt @@ -26,9 +26,11 @@ foreach ($tests as $obj) { ?> --EXPECTF-- int(1) +NULL Fatal error: Uncaught Exception: Undefined property: B::$b in %s:%d Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Undefined prope...', '%s', %d) -#1 {main} +#0 [internal function]: {closure:%s:%d}(2, 'Undefined prope...', '%s', %d) +#1 %s(%d): var_dump(NULL) +#2 {main} thrown in %s on line %d diff --git a/ext/opcache/tests/jit/gh18262-003.phpt b/ext/opcache/tests/jit/gh18262-003.phpt index a3c3e037632f..9f3f8c57ad55 100644 --- a/ext/opcache/tests/jit/gh18262-003.phpt +++ b/ext/opcache/tests/jit/gh18262-003.phpt @@ -32,4 +32,5 @@ foreach ($tests as $obj) { --EXPECT-- int(1) string(3) "str" +NULL Exception: Undefined property: B::$b diff --git a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt index 4e93e2ed7e56..45de4de6763b 100644 --- a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt +++ b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt @@ -17,5 +17,5 @@ try { echo $e->getMessage(), "\n"; } ?> ---EXPECT-- -Undefined variable $b +--EXPECTF-- +Undefined variable $b \ No newline at end of file diff --git a/ext/opcache/tests/jit/type_check_001.phpt b/ext/opcache/tests/jit/type_check_001.phpt index 028d2e8637e2..78936f197d5a 100644 --- a/ext/opcache/tests/jit/type_check_001.phpt +++ b/ext/opcache/tests/jit/type_check_001.phpt @@ -20,5 +20,5 @@ try { echo "Exception: " . $e->getMessage() . "\n"; } ?> ---EXPECT-- -Exception: Undefined variable $a +--EXPECTF-- +Exception: Undefined variable $a \ No newline at end of file diff --git a/ext/spl/tests/gh18274.phpt b/ext/spl/tests/gh18274.phpt new file mode 100644 index 000000000000..bc4f017d2c3b --- /dev/null +++ b/ext/spl/tests/gh18274.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-18274 (Assertion failure in SplFixedArray when an error handler runs during an undefined-variable write) +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: func_get_args() expects exactly 0 arguments, 4 given in %s:%d +Stack trace: +#%d %A: func_get_args(%d, %s, %s, %d)%A +#%d {main} + thrown in %s on line %d diff --git a/ext/standard/array.c b/ext/standard/array.c index 2d1dd584906a..e7ca18e0a307 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -680,19 +680,20 @@ PHP_FUNCTION(natcasesort) typedef bucket_compare_func_t(*get_compare_function)(zend_long); static zend_always_inline void php_sort(INTERNAL_FUNCTION_PARAMETERS, get_compare_function get_cmp, bool renumber) { - HashTable *array; + zval *array; zend_long sort_type = PHP_SORT_REGULAR; bucket_compare_func_t cmp; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_HT_EX(array, 0, 1) + Z_PARAM_ARRAY_EX(array, 0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(sort_type) ZEND_PARSE_PARAMETERS_END(); cmp = get_cmp(sort_type); - zend_array_sort(array, cmp, renumber); + SEPARATE_ARRAY(array); + zend_array_sort(Z_ARRVAL_P(array), cmp, renumber); RETURN_TRUE; } @@ -911,22 +912,25 @@ PHP_FUNCTION(uksort) } /* }}} */ -static inline HashTable *get_ht_for_iap(zval *zv, bool separate) { +static inline HashTable *get_ht_for_iap(zval *zv, bool separate, zend_object **objp) { + *objp = NULL; if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) { return Z_ARRVAL_P(zv); } ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); + zend_object *zobj = Z_OBJ_P(zv); + GC_ADDREF(zobj); php_error_docref(NULL, E_DEPRECATED, "Calling %s() on an object is deprecated", get_active_function_name()); - zend_object *zobj = Z_OBJ_P(zv); if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) { GC_DELREF(zobj->properties); } zobj->properties = zend_array_dup(zobj->properties); } + *objp = zobj; return zobj->handlers->get_properties(zobj); } @@ -979,15 +983,19 @@ PHP_FUNCTION(end) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_internal_pointer_end(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, false); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_internal_pointer_end(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, false); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1001,15 +1009,19 @@ PHP_FUNCTION(prev) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_move_backwards(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, false); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_move_backwards(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, false); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1023,15 +1035,19 @@ PHP_FUNCTION(next) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_move_forward(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, true); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_move_forward(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1045,15 +1061,19 @@ PHP_FUNCTION(reset) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_internal_pointer_reset(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, true); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_internal_pointer_reset(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1067,8 +1087,12 @@ PHP_FUNCTION(current) Z_PARAM_ARRAY_OR_OBJECT(array_zv) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ false, &iap_obj); php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); + } } /* }}} */ @@ -1081,11 +1105,15 @@ PHP_FUNCTION(key) Z_PARAM_ARRAY_OR_OBJECT(array_zv) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ false, &iap_obj); zval *entry = php_array_iter_seek_current(array, true); if (EXPECTED(entry)) { zend_hash_get_current_key_zval(array, return_value); } + if (iap_obj) { + OBJ_RELEASE(iap_obj); + } } /* }}} */ diff --git a/ext/standard/tests/array/gh20042.phpt b/ext/standard/tests/array/gh20042.phpt new file mode 100644 index 000000000000..34ca982b2ae5 --- /dev/null +++ b/ext/standard/tests/array/gh20042.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-20042 (SEGV in array.c when the error handler reassigns the array during prev()) +--FILE-- + +--EXPECT-- +OK survived diff --git a/ext/standard/tests/array/sort/array_multisort_variation2.phpt b/ext/standard/tests/array/sort/array_multisort_variation2.phpt index b491b87bfda8..b3bdba7a63df 100644 --- a/ext/standard/tests/array/sort/array_multisort_variation2.phpt +++ b/ext/standard/tests/array/sort/array_multisort_variation2.phpt @@ -107,7 +107,7 @@ foreach($inputs as $key =>$value) { }; ?> ---EXPECT-- +--EXPECTF-- *** Testing array_multisort() : usage variation *** --int 0-- diff --git a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt index f7b86a2977ac..71392b9f4dc1 100644 --- a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt +++ b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt @@ -109,25 +109,25 @@ get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid Arg value 0.5 get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, float given -Error: 2 - Array to string conversion Arg value Array -get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value @@ -169,4 +169,4 @@ get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid Arg value get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, null given -Done +Done \ No newline at end of file diff --git a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt index 8faeff9dd8dc..eb951cfbfdce 100644 --- a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt +++ b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt @@ -112,25 +112,25 @@ get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid Arg value 0.5 get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, float given -Error: 2 - Array to string conversion Arg value Array -get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array -get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Error: 2 - Array to string conversion +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value @@ -173,4 +173,4 @@ get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid Arg value get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, null given -Done +Done \ No newline at end of file diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt index f03841dbac7d..7e74d8022d61 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt @@ -79,12 +79,12 @@ foreach($values as $value) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing is_subclass_of() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - -Arg value 0 +%A +%A +%A +%A bool(false) Arg value 1 @@ -110,25 +110,25 @@ bool(false) Arg value 0.5 bool(false) -Error: 2 - Array to string conversion Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array +Error: 2 - Array to string conversion bool(false) Arg value diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt index d045a4c1a915..fed9ef8c864d 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt @@ -79,12 +79,12 @@ foreach($values as $value) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing is_subclass_of() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - -Arg value 0 +%A +%A +%A +%A bool(false) Arg value 1 @@ -110,25 +110,25 @@ bool(false) Arg value 0.5 bool(false) -Error: 2 - Array to string conversion Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array +Error: 2 - Array to string conversion bool(false) Arg value diff --git a/ext/standard/tests/class_object/method_exists_variation_001.phpt b/ext/standard/tests/class_object/method_exists_variation_001.phpt index d45d2652d11c..1761475ace66 100644 --- a/ext/standard/tests/class_object/method_exists_variation_001.phpt +++ b/ext/standard/tests/class_object/method_exists_variation_001.phpt @@ -83,10 +83,10 @@ echo "Done"; ?> --EXPECT-- *** Testing method_exists() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var Arg value 0 +Error: 2 - Undefined variable $undefined_var +Error: 2 - Undefined variable $unset_var method_exists(): Argument #1 ($object_or_class) must be of type object|string, int given Arg value 1 @@ -112,25 +112,25 @@ method_exists(): Argument #1 ($object_or_class) must be of type object|string, f Arg value 0.5 method_exists(): Argument #1 ($object_or_class) must be of type object|string, float given -Error: 2 - Array to string conversion Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array +Error: 2 - Array to string conversion method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value @@ -170,4 +170,4 @@ method_exists(): Argument #1 ($object_or_class) must be of type object|string, n Arg value method_exists(): Argument #1 ($object_or_class) must be of type object|string, null given -Done +Done \ No newline at end of file diff --git a/ext/standard/tests/image/getimagesize_variation2.phpt b/ext/standard/tests/image/getimagesize_variation2.phpt index 045d9141a6bd..1728c668fab0 100644 --- a/ext/standard/tests/image/getimagesize_variation2.phpt +++ b/ext/standard/tests/image/getimagesize_variation2.phpt @@ -72,12 +72,12 @@ foreach($values as $key => $value) { }; ?> ---EXPECT-- +--EXPECTF-- *** Testing getimagesize() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - --- Arg value 0 -- +%A +%A +%A +%A string(28) "4a46494600010201006000600000" -- Arg value 1 -- diff --git a/ext/zend_test/tests/observer_error_05.phpt b/ext/zend_test/tests/observer_error_05.phpt index d672e6405ec8..378bb00589ab 100644 --- a/ext/zend_test/tests/observer_error_05.phpt +++ b/ext/zend_test/tests/observer_error_05.phpt @@ -30,15 +30,15 @@ echo 'You should not see this.'; - - <{closure:%s:%d}> - - + + + <{closure:%s:%d}> + + Deprecated: Passing E_USER_ERROR to trigger_error() is deprecated since 8.4, throw an exception or call exit with a string message instead in %s on line %d Fatal error: Foo error in %s on line %d - - - + + diff --git a/tests/lang/bug22592.phpt b/tests/lang/bug22592.phpt index 24b5f50fdf5a..2505b2ff7212 100644 --- a/tests/lang/bug22592.phpt +++ b/tests/lang/bug22592.phpt @@ -39,12 +39,12 @@ var_dump($result); string(5) "* *-*" string(7) "* *-* *" string(7) "*4*-* *" -[Only the first byte will be assigned to the string offset] string(7) "*4*s* *" +[Only the first byte will be assigned to the string offset] string(8) "*4*s* *0" string(8) "*-*-* *0" -[Only the first byte will be assigned to the string offset] string(8) "*-*s*s*0" +[Only the first byte will be assigned to the string offset] string(8) "4-4s4s*0" string(9) "4-4s4s505" string(9) "454s4s505" diff --git a/tests/lang/bug25547.phpt b/tests/lang/bug25547.phpt index 53b951fd4543..49bdb5e75115 100644 --- a/tests/lang/bug25547.phpt +++ b/tests/lang/bug25547.phpt @@ -22,9 +22,9 @@ print_r($output); echo "Done"; ?> --EXPECT-- -handler(Undefined array key "foo") Array ( [foo] => 1 ) +handler(Undefined array key "foo") Done diff --git a/tests/lang/bug25922.phpt b/tests/lang/bug25922.phpt index 4927bfea776c..41aa72ae35d6 100644 --- a/tests/lang/bug25922.phpt +++ b/tests/lang/bug25922.phpt @@ -19,6 +19,6 @@ function test() test(); ?> --EXPECT-- +Undefined index here: '' Undefined variable $data Trying to access array offset on null -Undefined index here: '' From 86497b1274d079476ff4cf44618141662f9555e3 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 12 Jul 2026 19:03:33 -0400 Subject: [PATCH 2/3] Defer object destructors to the next VM safepoint Run an object's __destruct at the next VM safepoint instead of inline at the point its refcount reaches zero, so a destructor can no longer reenter the VM in the middle of an opcode's operand cleanup. Logical death stays synchronous: a WeakReference reads null and a WeakMap entry is gone at the drop, and only the physical destructor call and the free are deferred. Safepoints are entry into a user function, method, closure, magic method or hook, a statement-level internal call, every try-region boundary (entry, normal exit, return/break/continue/goto/throw out of a try, and the end of a finally body), a loop back-edge, and script or fiber end. Because a flushed destructor exception is thrown from an opline still inside the try region that owns it, the existing exception table delivers it to the same catch that would have caught an inline throw; region scoping is positional and needs no per-region runtime state. Several destructor exceptions at one safepoint compose into a single getPrevious chain, an exit or unwind exception wins over a destructor exception, and every object is destroyed exactly once. Behavior is identical across the interpreter, tracing JIT, and function JIT. Generators and fibers carry their own deferred-destructor queue, so a destructor deferred inside one runs there rather than in the consumer. --- Zend/tests/bug38220.phpt | 2 +- .../tests/dtor_defer/catch_on_early_exit.phpt | 57 + .../exit_wins_over_throwing_dtor.phpt | 36 + .../finally_body_dtor_composition.phpt | 32 + .../finally_throwing_dtor_once.phpt | 30 + .../tests/dtor_defer/gc_condemned_inline.phpt | 35 + .../tests/dtor_defer/loop_backedge_flush.phpt | 28 + .../dtor_defer/nested_region_scoping.phpt | 59 + .../dtor_defer/throw_after_deferred_dtor.phpt | 43 + .../dtor_defer/throw_preempt_uncatchable.phpt | 18 + .../dtor_defer/weakmap_dead_key_absent.phpt | 18 + .../dtor_defer/weakref_no_resurrection.phpt | 21 + .../ex_func_null_during_property_write.phpt | 4 +- Zend/tests/exceptions/bug53511.phpt | 12 +- Zend/tests/exceptions/exception_008.phpt | 6 +- Zend/tests/exceptions/exception_026.phpt | 4 +- .../fibers/unfinished-fiber-with-finally.phpt | 2 +- ...nfinished-fiber-with-nested-try-catch.phpt | 2 +- ...inished-fiber-with-suspend-in-finally.phpt | 2 +- ...nfinished-fiber-with-throw-in-finally.phpt | 2 +- Zend/tests/frameless_throwing_destructor.phpt | 2 +- ...ve_parameter_value_keeps_object_alive.phpt | 2 +- Zend/tests/gc_053.phpt | 8 +- Zend/tests/generators/bug71601.phpt | 2 +- .../yield_in_force_closed_finally_error.phpt | 2 +- .../generator_closure_collection.phpt | 2 + ...tor_deferred_dtor_caught_in_generator.phpt | 58 + Zend/tests/gh10168/assign_prop_ref.phpt | 5 +- .../assign_prop_ref_with_prop_ref.phpt | 3 +- Zend/tests/gh10168/assign_ref.phpt | 3 +- .../gh10168/assign_ref_with_prop_ref.phpt | 3 +- .../tests/gh10168/assign_static_prop_ref.phpt | 3 +- .../assign_static_untyped_prop_ref.phpt | 3 +- .../gh10168/assign_untyped_prop_ref.phpt | 5 +- ...assign_untyped_prop_ref_with_prop_ref.phpt | 3 +- .../reset_as_lazy_calls_destructor.phpt | 2 +- .../reset_as_lazy_may_skip_destructor.phpt | 2 +- Zend/tests/magic_methods/bug32596.phpt | 2 +- Zend/tests/oss_fuzz_456317305.phpt | 5 + Zend/tests/stack_limit/gh16041_001.phpt | 11 +- Zend/tests/try/catch_novar_2.phpt | 8 +- .../type_casts/cast_to_void_destructor.phpt | 2 +- Zend/tests/unset/unset_prop_recursion.phpt | 8 +- Zend/tests/weakrefs/gh13612.phpt | 2 +- Zend/tests/weakrefs/weakmap_iteration.phpt | 16 +- Zend/tests/weakrefs/weakmap_weakness.phpt | 10 +- Zend/zend.c | 9 + Zend/zend_compile.c | 19 + Zend/zend_execute.c | 79 +- Zend/zend_execute_API.c | 40 +- Zend/zend_fibers.c | 22 + Zend/zend_gc.c | 22 + Zend/zend_gc.h | 1 + Zend/zend_generators.c | 79 ++ Zend/zend_generators.h | 5 + Zend/zend_globals.h | 14 + Zend/zend_objects.c | 36 +- Zend/zend_objects_API.c | 403 +++++- Zend/zend_objects_API.h | 6 + Zend/zend_types.h | 2 + Zend/zend_vm_def.h | 177 ++- Zend/zend_vm_execute.h | 1203 ++++++++++++++--- Zend/zend_vm_gen.php | 24 +- Zend/zend_vm_handlers.h | 935 ++++++------- Zend/zend_vm_opcodes.c | 6 +- Zend/zend_vm_opcodes.h | 3 +- Zend/zend_weakrefs.c | 53 + Zend/zend_weakrefs.h | 1 + .../curl_multi_add_handle_lifecycle.phpt | 2 +- ext/dom/tests/gh12616_3.phpt | 10 +- ext/opcache/jit/zend_jit.c | 8 + ext/opcache/jit/zend_jit_helpers.c | 41 +- ext/opcache/jit/zend_jit_internal.h | 1 + ext/opcache/jit/zend_jit_ir.c | 181 ++- ext/opcache/jit/zend_jit_trace.c | 23 +- ext/opcache/jit/zend_jit_vm_helpers.c | 24 +- ext/opcache/tests/jit/count_001.phpt | 5 + ext/opcache/tests/jit/gh18262-004.phpt | 5 +- ext/opcache/tests/opt/gh18107_1.phpt | 21 +- ext/opcache/tests/opt/gh18107_2.phpt | 31 +- .../pdo_ATTR_STATEMENT_CLASS_basic.phpt | 2 +- ext/pdo/tests/pdo_stmt_cyclic_references.phpt | 6 +- .../tests/pg_fetch_object_ctor_paths.phpt | 2 +- ext/phar/tests/bug69720.phpt | 2 +- ext/phar/tests/tar/bug70417.phpt | 2 +- ext/phar/tests/test_alias_unset.phpt | 9 +- ext/spl/tests/SplFixedArray_immediate_gc.phpt | 2 +- ext/spl/tests/gh12721.phpt | 2 +- ext/spl/tests/iterator_044.phpt | 2 +- .../zend_object_init_with_constructor.phpt | 4 +- ...nstructor_classes_without_constructor.phpt | 16 +- ext/zip/tests/ZipArchive_destruct.phpt | 9 +- tests/classes/bug27468.phpt | 2 +- tests/classes/ctor_dtor_inheritance.phpt | 4 +- tests/classes/destructor_and_exceptions.phpt | 1 + tests/classes/destructor_inheritance.phpt | 3 +- tests/classes/destructor_visibility_001.phpt | 4 +- tests/classes/factory_and_singleton_005.phpt | 4 +- tests/classes/factory_and_singleton_006.phpt | 4 +- tests/classes/iterators_002.phpt | 2 +- tests/classes/tostring_002.phpt | 2 +- 101 files changed, 3274 insertions(+), 886 deletions(-) create mode 100644 Zend/tests/dtor_defer/catch_on_early_exit.phpt create mode 100644 Zend/tests/dtor_defer/exit_wins_over_throwing_dtor.phpt create mode 100644 Zend/tests/dtor_defer/finally_body_dtor_composition.phpt create mode 100644 Zend/tests/dtor_defer/finally_throwing_dtor_once.phpt create mode 100644 Zend/tests/dtor_defer/gc_condemned_inline.phpt create mode 100644 Zend/tests/dtor_defer/loop_backedge_flush.phpt create mode 100644 Zend/tests/dtor_defer/nested_region_scoping.phpt create mode 100644 Zend/tests/dtor_defer/throw_after_deferred_dtor.phpt create mode 100644 Zend/tests/dtor_defer/throw_preempt_uncatchable.phpt create mode 100644 Zend/tests/dtor_defer/weakmap_dead_key_absent.phpt create mode 100644 Zend/tests/dtor_defer/weakref_no_resurrection.phpt create mode 100644 Zend/tests/generators/generator_deferred_dtor_caught_in_generator.phpt diff --git a/Zend/tests/bug38220.phpt b/Zend/tests/bug38220.phpt index 2700299a1026..3d7ef66195a5 100644 --- a/Zend/tests/bug38220.phpt +++ b/Zend/tests/bug38220.phpt @@ -67,6 +67,7 @@ A Object ) func1(): 1 after call func1 +before call func1 A::__destruct() before call close A Object @@ -75,7 +76,6 @@ A Object ) close(): 1 after call close -before call func1 A Object ( [i] => 2 diff --git a/Zend/tests/dtor_defer/catch_on_early_exit.phpt b/Zend/tests/dtor_defer/catch_on_early_exit.phpt new file mode 100644 index 000000000000..c61950cf5ddc --- /dev/null +++ b/Zend/tests/dtor_defer/catch_on_early_exit.phpt @@ -0,0 +1,57 @@ +--TEST-- +Deferred destructor: a throwing destructor dropped in a try is caught by that try when the try is left via return/break/continue +--FILE-- +id}"); } +} + +function via_return() { + try { + $d = new D("r"); + unset($d); + return "returned"; + } catch (Exception $e) { + echo "return-case caught: {$e->getMessage()}\n"; + return "caught"; + } +} +var_dump(via_return()); + +function via_break() { + foreach ([1] as $v) { + try { + $d = new D("b"); + unset($d); + break; + } catch (Exception $e) { + echo "break-case caught: {$e->getMessage()}\n"; + } + } + return "done"; +} +var_dump(via_break()); + +function via_continue() { + foreach ([1, 2] as $v) { + try { + $d = new D("c$v"); + unset($d); + continue; + } catch (Exception $e) { + echo "continue-case caught: {$e->getMessage()}\n"; + } + } + return "done"; +} +var_dump(via_continue()); +?> +--EXPECT-- +return-case caught: dtor r +string(6) "caught" +break-case caught: dtor b +string(4) "done" +continue-case caught: dtor c1 +continue-case caught: dtor c2 +string(4) "done" diff --git a/Zend/tests/dtor_defer/exit_wins_over_throwing_dtor.phpt b/Zend/tests/dtor_defer/exit_wins_over_throwing_dtor.phpt new file mode 100644 index 000000000000..17a597ad9643 --- /dev/null +++ b/Zend/tests/dtor_defer/exit_wins_over_throwing_dtor.phpt @@ -0,0 +1,36 @@ +--TEST-- +Deferred destructor: exit() in a destructor wins over a throwing destructor in the same flush, either order +--FILE-- +id = $id; } + function __destruct() { + echo "Thrower {$this->id} dtor\n"; + throw new Exception("thrown {$this->id}"); + } +} + +register_shutdown_function(function () { echo "shutdown\n"; }); + +// Thrower deferred before Exiter, then Exiter: exit must win over the pending throw. +$t = new Thrower(1); +$t = null; +$e = new Exiter(); +$e = null; + +// A safepoint flushes the batch. exit(7) wins; no user code runs afterwards. +(function () {})(); + +echo "UNREACHABLE\n"; +?> +--EXPECT-- +Thrower 1 dtor +Exiter dtor +shutdown diff --git a/Zend/tests/dtor_defer/finally_body_dtor_composition.phpt b/Zend/tests/dtor_defer/finally_body_dtor_composition.phpt new file mode 100644 index 000000000000..89d270bb9d58 --- /dev/null +++ b/Zend/tests/dtor_defer/finally_body_dtor_composition.phpt @@ -0,0 +1,32 @@ +--TEST-- +Deferred destructor: a destructor dropped in a finally body composes with a destructor exception pending from the protected block and is caught by the enclosing try +--FILE-- +id}"); } +} +function f() { + try { + try { + $a = new D("try"); + unset($a); + return "unreached"; + } finally { + $b = new D("finally"); + unset($b); + echo "finally body ran\n"; + } + } catch (Exception $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; + echo "previous: ", $e->getPrevious()?->getMessage() ?? "none", "\n"; + return "caught"; + } +} +var_dump(f()); +?> +--EXPECT-- +finally body ran +Exception: dtor finally +previous: dtor try +string(6) "caught" diff --git a/Zend/tests/dtor_defer/finally_throwing_dtor_once.phpt b/Zend/tests/dtor_defer/finally_throwing_dtor_once.phpt new file mode 100644 index 000000000000..9ce2b18899e0 --- /dev/null +++ b/Zend/tests/dtor_defer/finally_throwing_dtor_once.phpt @@ -0,0 +1,30 @@ +--TEST-- +Deferred destructor: a throwing destructor deferred in a finally runs exactly once +--FILE-- + +--EXPECTF-- +dtor + +Fatal error: Uncaught Exception: boom in %s:%d +Stack trace: +#0 %s(%d): D->__destruct() +#1 %s(%d): g() +#2 {main} + thrown in %s on line %d diff --git a/Zend/tests/dtor_defer/gc_condemned_inline.phpt b/Zend/tests/dtor_defer/gc_condemned_inline.phpt new file mode 100644 index 000000000000..9f996bacf662 --- /dev/null +++ b/Zend/tests/dtor_defer/gc_condemned_inline.phpt @@ -0,0 +1,35 @@ +--TEST-- +Deferred destructor: a drop inside a GC destructor of a condemned sibling runs inline +--FILE-- +peer = null; + } +} + +for ($r = 0; $r < 2000; $r++) { + $a = new Node(); + $b = new Node(); + $c = new Node(); + $a->next = $b; + $b->next = $c; + $c->next = $a; + $a->peer = $c; + $b->peer = $a; + $c->peer = $b; + unset($a, $b, $c); + if ($r % 500 === 0) { + gc_collect_cycles(); + } +} +gc_collect_cycles(); +echo "log=$log\n"; +?> +--EXPECT-- +log=6000 diff --git a/Zend/tests/dtor_defer/loop_backedge_flush.phpt b/Zend/tests/dtor_defer/loop_backedge_flush.phpt new file mode 100644 index 000000000000..315d97b9aec3 --- /dev/null +++ b/Zend/tests/dtor_defer/loop_backedge_flush.phpt @@ -0,0 +1,28 @@ +--TEST-- +Deferred destructors flush at loop back-edges +--INI-- +opcache.enable_cli=0 +--FILE-- + +--EXPECT-- +0:0 +1:1 +2:2 +done:2 diff --git a/Zend/tests/dtor_defer/nested_region_scoping.phpt b/Zend/tests/dtor_defer/nested_region_scoping.phpt new file mode 100644 index 000000000000..bafeb9fb3252 --- /dev/null +++ b/Zend/tests/dtor_defer/nested_region_scoping.phpt @@ -0,0 +1,59 @@ +--TEST-- +Deferred destructor: nested try regions scope the deferred queue; each region's destructor surfaces at its own level and a finally-return discards only its own +--FILE-- +id}"); } +} + +function inner_level() { + try { + try { + $d = new D("inner"); + unset($d); + return "inner-try-return"; + } catch (Exception $e) { + echo "inner caught: {$e->getMessage()}\n"; + return "inner-catch"; + } + } catch (Exception $e) { + echo "outer WRONGLY caught: {$e->getMessage()}\n"; + return "outer-catch"; + } +} +var_dump(inner_level()); + +function outer_survives() { + try { + $d = new D("outer"); + unset($d); + try { + return "inner-try-return"; + } finally { + return "inner-finally-return"; + } + } catch (Exception $e) { + echo "outer caught: {$e->getMessage()}\n"; + return "outer-catch"; + } +} +var_dump(outer_survives()); + +function finally_discards() { + try { + $d = new D("discarded"); + unset($d); + return "try-return"; + } finally { + return "finally-return"; + } +} +var_dump(finally_discards()); +?> +--EXPECT-- +inner caught: dtor inner +string(11) "inner-catch" +outer caught: dtor outer +string(11) "outer-catch" +string(14) "finally-return" diff --git a/Zend/tests/dtor_defer/throw_after_deferred_dtor.phpt b/Zend/tests/dtor_defer/throw_after_deferred_dtor.phpt new file mode 100644 index 000000000000..34f7cf9b74e9 --- /dev/null +++ b/Zend/tests/dtor_defer/throw_after_deferred_dtor.phpt @@ -0,0 +1,43 @@ +--TEST-- +Deferred destructor: a throwing destructor pending at an explicit throw preempts it, whether dropped before the statement or during the throw operand's evaluation, so no spurious exception chain is created +--FILE-- +getMessage(), " prev=", $e->getPrevious()?->getMessage() ?? "none", "\n"; + } +} +rethrow_case(); + +// The throw operand is a call that drops the destructor during its evaluation. +function make(Exception $e) { + $x = new D(); + unset($x); + return $e; +} +function operand_call_case() { + $pre = new Exception("explicit"); + try { + throw make($pre); + } catch (Exception $e) { + echo "operand-call: ", $e->getMessage(), " prev=", $e->getPrevious()?->getMessage() ?? "none", "\n"; + } +} +operand_call_case(); +?> +--EXPECT-- +rethrow: dtor prev=none +operand-call: dtor prev=none diff --git a/Zend/tests/dtor_defer/throw_preempt_uncatchable.phpt b/Zend/tests/dtor_defer/throw_preempt_uncatchable.phpt new file mode 100644 index 000000000000..f2e2cf876739 --- /dev/null +++ b/Zend/tests/dtor_defer/throw_preempt_uncatchable.phpt @@ -0,0 +1,18 @@ +--TEST-- +Deferred destructor: an uncatchable throwing destructor pending at an explicit throw or internal call preempts it positionally instead of being held to shutdown +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception: dtor in %s:%d +Stack trace: +#0 %s(%d): D->__destruct() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/dtor_defer/weakmap_dead_key_absent.phpt b/Zend/tests/dtor_defer/weakmap_dead_key_absent.phpt new file mode 100644 index 000000000000..17e9e478df8b --- /dev/null +++ b/Zend/tests/dtor_defer/weakmap_dead_key_absent.phpt @@ -0,0 +1,18 @@ +--TEST-- +Deferred destructor: a dying WeakMap key leaves the map at logical death +--FILE-- + +--EXPECT-- +before: 2 +after unset k1: 1 +end diff --git a/Zend/tests/dtor_defer/weakref_no_resurrection.phpt b/Zend/tests/dtor_defer/weakref_no_resurrection.phpt new file mode 100644 index 000000000000..bb3806161ace --- /dev/null +++ b/Zend/tests/dtor_defer/weakref_no_resurrection.phpt @@ -0,0 +1,21 @@ +--TEST-- +Deferred destructor: a WeakReference to the dying object returns null immediately +--FILE-- +get(); +echo $v === null ? "null\n" : "live\n"; +echo "end\n"; +?> +--EXPECT-- +null +end +dtor diff --git a/Zend/tests/ex_func_null_during_property_write.phpt b/Zend/tests/ex_func_null_during_property_write.phpt index 6a253f760e0b..05c01dad4059 100644 --- a/Zend/tests/ex_func_null_during_property_write.phpt +++ b/Zend/tests/ex_func_null_during_property_write.phpt @@ -15,11 +15,11 @@ new a; --EXPECTF-- Fatal error: Uncaught Error: Object of class a could not be converted to string in %s:%d Stack trace: -#0 %s(%d): a->__destruct() +#0 [internal function]: a->__destruct() #1 {main} Next Exception in %s:%d Stack trace: -#0 %s(%d): a->__destruct() +#0 [internal function]: a->__destruct() #1 {main} thrown in %s on line %d diff --git a/Zend/tests/exceptions/bug53511.phpt b/Zend/tests/exceptions/bug53511.phpt index b07504506a94..c3a7d90e0449 100644 --- a/Zend/tests/exceptions/bug53511.phpt +++ b/Zend/tests/exceptions/bug53511.phpt @@ -21,9 +21,11 @@ test(); echo "bug\n"; ?> --EXPECTF-- -Fatal error: Uncaught Exception: ops 1 in %sbug53511.php:4 +ops 2 +bug + +Fatal error: Uncaught Exception: ops 1 in %sbug53511.php:%d Stack trace: -#0 %sbug53511.php(12): Foo->__destruct() -#1 %sbug53511.php(17): test() -#2 {main} - thrown in %sbug53511.php on line 4 +#0 %s: Foo->__destruct() +#1 {main} + thrown in %sbug53511.php on line %d diff --git a/Zend/tests/exceptions/exception_008.phpt b/Zend/tests/exceptions/exception_008.phpt index 4640ec965ad5..3877fda928d3 100644 --- a/Zend/tests/exceptions/exception_008.phpt +++ b/Zend/tests/exceptions/exception_008.phpt @@ -24,13 +24,15 @@ unset($ar); ?> ===DONE=== --EXPECTF-- +===DONE=== + Fatal error: Uncaught Exception: First in %sexception_008.php:%d Stack trace: -#0 %sexception_008.php(%d): TestFirst->__destruct() +#0 [internal function]: TestFirst->__destruct() #1 {main} Next Exception: Second in %sexception_008.php:%d Stack trace: -#0 %sexception_008.php(%d): TestSecond->__destruct() +#0 [internal function]: TestSecond->__destruct() #1 {main} thrown in %sexception_008.php on line %d diff --git a/Zend/tests/exceptions/exception_026.phpt b/Zend/tests/exceptions/exception_026.phpt index 0c47a449a4bf..1994545f571c 100644 --- a/Zend/tests/exceptions/exception_026.phpt +++ b/Zend/tests/exceptions/exception_026.phpt @@ -16,11 +16,11 @@ new A; --EXPECTF-- Fatal error: Uncaught Error: Undefined constant "Y" in %s:8 Stack trace: -#0 %s(11): A->__destruct() +#0 [internal function]: A->__destruct() #1 {main} Next Error: Undefined constant "X" in %s:6 Stack trace: -#0 %s(11): A->__destruct() +#0 [internal function]: A->__destruct() #1 {main} thrown in %s on line 6 diff --git a/Zend/tests/fibers/unfinished-fiber-with-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-finally.phpt index 84ba74e646c6..b0a5649adb81 100644 --- a/Zend/tests/fibers/unfinished-fiber-with-finally.phpt +++ b/Zend/tests/fibers/unfinished-fiber-with-finally.phpt @@ -26,5 +26,5 @@ echo "done\n"; ?> --EXPECT-- fiber -finally done +finally diff --git a/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt b/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt index 664e4a052b8f..e8c881e30c66 100644 --- a/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt +++ b/Zend/tests/fibers/unfinished-fiber-with-nested-try-catch.phpt @@ -42,6 +42,6 @@ echo "done\n"; ?> --EXPECT-- fiber +done inner finally outer finally -done diff --git a/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt index e38774df82bf..93eb00833ae7 100644 --- a/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt +++ b/Zend/tests/fibers/unfinished-fiber-with-suspend-in-finally.phpt @@ -32,5 +32,5 @@ echo "done\n"; --EXPECT-- fiber inner finally -outer finally done +outer finally diff --git a/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt b/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt index f1b17caeb5d7..567034be0a4a 100644 --- a/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt +++ b/Zend/tests/fibers/unfinished-fiber-with-throw-in-finally.phpt @@ -41,8 +41,8 @@ echo "done\n"; ?> --EXPECT-- fiber +done inner finally finally exception outer finally Cannot suspend in a force-closed fiber -done diff --git a/Zend/tests/frameless_throwing_destructor.phpt b/Zend/tests/frameless_throwing_destructor.phpt index ad555d860e66..28bb6caf4ba4 100644 --- a/Zend/tests/frameless_throwing_destructor.phpt +++ b/Zend/tests/frameless_throwing_destructor.phpt @@ -13,6 +13,6 @@ in_array(new Foo(), [new Bar()], true); --EXPECTF-- Fatal error: Uncaught Exception in %s:%d Stack trace: -#0 %s(%d): Bar->__destruct() +#0 [internal function]: Bar->__destruct() #1 {main} thrown in %s on line %d diff --git a/Zend/tests/function_arguments/sensitive_parameter_value_keeps_object_alive.phpt b/Zend/tests/function_arguments/sensitive_parameter_value_keeps_object_alive.phpt index e7b0de51b969..d4deb4e51294 100644 --- a/Zend/tests/function_arguments/sensitive_parameter_value_keeps_object_alive.phpt +++ b/Zend/tests/function_arguments/sensitive_parameter_value_keeps_object_alive.phpt @@ -70,5 +70,5 @@ CustomDestructor::__construct - 2 catch int(2) Before unset -CustomDestructor::__destruct - 2 After unset +CustomDestructor::__destruct - 2 diff --git a/Zend/tests/gc_053.phpt b/Zend/tests/gc_053.phpt index 858be7cbebd5..920609600e3d 100644 --- a/Zend/tests/gc_053.phpt +++ b/Zend/tests/gc_053.phpt @@ -23,15 +23,15 @@ $weakO = \WeakReference::create($o); echo "---\n"; unset($o); echo "---\n"; -var_dump($weakO->get() !== null); // verify if kept allocated +var_dump($weakO->get() !== null); // weakref severed at the drop; the deferred dtor re-referencing $this does not restore it G::$v = null; echo "---\n"; -var_dump($weakO->get() !== null); // verify if released +var_dump($weakO->get() !== null); // still severed ?> --EXPECT-- --- -d --- -bool(true) +d +bool(false) --- bool(false) diff --git a/Zend/tests/generators/bug71601.phpt b/Zend/tests/generators/bug71601.phpt index c8e226329dfe..a4a156e5ae3c 100644 --- a/Zend/tests/generators/bug71601.phpt +++ b/Zend/tests/generators/bug71601.phpt @@ -34,6 +34,6 @@ echo "Done\n"; --EXPECT-- Entered try/catch int(1) +Done Inner finally Finally -Done diff --git a/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt b/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt index af84e50215ce..894a4d7e8e4b 100644 --- a/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt +++ b/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt @@ -28,6 +28,6 @@ before yield in finally Fatal error: Uncaught Error: Cannot yield from finally in a force-closed generator in %s:%d Stack trace: -#0 %s(%d): gen() +#0 [internal function]: gen() #1 {main} thrown in %s on line %d diff --git a/Zend/tests/generators/generator_closure_collection.phpt b/Zend/tests/generators/generator_closure_collection.phpt index 7386adeb80c5..0721c0bf8979 100644 --- a/Zend/tests/generators/generator_closure_collection.phpt +++ b/Zend/tests/generators/generator_closure_collection.phpt @@ -21,6 +21,8 @@ var_dump($r->get()); unset($generator); +(function () {})(); + var_dump($r->get()); ?> diff --git a/Zend/tests/generators/generator_deferred_dtor_caught_in_generator.phpt b/Zend/tests/generators/generator_deferred_dtor_caught_in_generator.phpt new file mode 100644 index 000000000000..803c069f7993 --- /dev/null +++ b/Zend/tests/generators/generator_deferred_dtor_caught_in_generator.phpt @@ -0,0 +1,58 @@ +--TEST-- +A deferred destructor dropped in a generator is caught by that generator, not the consumer +--FILE-- +id}"); } +} + +function gen() { + try { + $d = new D(1); + unset($d); + yield 1; + yield 2; + } catch (Exception $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; + } +} + +try { + foreach (gen() as $v) { + echo "consumer got $v\n"; + } + echo "consumer done\n"; +} catch (Exception $e) { + echo "ESCAPED to consumer: ", $e->getMessage(), "\n"; +} + +function inner() { + try { + $d = new D(2); + unset($d); + yield 1; + } catch (Exception $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; + } + yield 3; +} +function outer() { yield from inner(); } + +try { + foreach (outer() as $v) { + echo "outer got $v\n"; + } + echo "outer done\n"; +} catch (Exception $e) { + echo "ESCAPED to outer consumer: ", $e->getMessage(), "\n"; +} +echo "end\n"; +?> +--EXPECT-- +Exception: dtor 1 +consumer done +Exception: dtor 2 +outer got 3 +outer done +end diff --git a/Zend/tests/gh10168/assign_prop_ref.phpt b/Zend/tests/gh10168/assign_prop_ref.phpt index 606542fbd6c6..6e56a863a1ff 100644 --- a/Zend/tests/gh10168/assign_prop_ref.phpt +++ b/Zend/tests/gh10168/assign_prop_ref.phpt @@ -27,6 +27,7 @@ test($box); ?> --EXPECT-- -NULL -object(Test)#2 (0) { +object(Test)#3 (0) { +} +object(Test)#3 (0) { } diff --git a/Zend/tests/gh10168/assign_prop_ref_with_prop_ref.phpt b/Zend/tests/gh10168/assign_prop_ref_with_prop_ref.phpt index f887acbc1d06..737754b67125 100644 --- a/Zend/tests/gh10168/assign_prop_ref_with_prop_ref.phpt +++ b/Zend/tests/gh10168/assign_prop_ref_with_prop_ref.phpt @@ -32,4 +32,5 @@ test($box); --EXPECT-- object(Test)#3 (0) { } -NULL +object(Test)#4 (0) { +} diff --git a/Zend/tests/gh10168/assign_ref.phpt b/Zend/tests/gh10168/assign_ref.phpt index 9f3be5619d1a..a05c26700bcf 100644 --- a/Zend/tests/gh10168/assign_ref.phpt +++ b/Zend/tests/gh10168/assign_ref.phpt @@ -15,4 +15,5 @@ var_dump($a = &$tmp); ?> --EXPECT-- -NULL +object(Test)#2 (0) { +} diff --git a/Zend/tests/gh10168/assign_ref_with_prop_ref.phpt b/Zend/tests/gh10168/assign_ref_with_prop_ref.phpt index 42ce94b3f83e..8afc098294ba 100644 --- a/Zend/tests/gh10168/assign_ref_with_prop_ref.phpt +++ b/Zend/tests/gh10168/assign_ref_with_prop_ref.phpt @@ -17,4 +17,5 @@ var_dump($a = &$tmp); ?> --EXPECT-- -NULL +object(Test)#2 (0) { +} diff --git a/Zend/tests/gh10168/assign_static_prop_ref.phpt b/Zend/tests/gh10168/assign_static_prop_ref.phpt index 2e90be884ded..582d1f03da46 100644 --- a/Zend/tests/gh10168/assign_static_prop_ref.phpt +++ b/Zend/tests/gh10168/assign_static_prop_ref.phpt @@ -17,4 +17,5 @@ var_dump(Test::$test = &$tmp); ?> --EXPECT-- -NULL +object(Test)#2 (0) { +} diff --git a/Zend/tests/gh10168/assign_static_untyped_prop_ref.phpt b/Zend/tests/gh10168/assign_static_untyped_prop_ref.phpt index dfef8ead26f6..2d3679edc36d 100644 --- a/Zend/tests/gh10168/assign_static_untyped_prop_ref.phpt +++ b/Zend/tests/gh10168/assign_static_untyped_prop_ref.phpt @@ -17,4 +17,5 @@ var_dump(Test::$test = &$tmp); ?> --EXPECT-- -NULL +object(Test)#2 (0) { +} diff --git a/Zend/tests/gh10168/assign_untyped_prop_ref.phpt b/Zend/tests/gh10168/assign_untyped_prop_ref.phpt index a28395d43bbb..1f09f0662bd2 100644 --- a/Zend/tests/gh10168/assign_untyped_prop_ref.phpt +++ b/Zend/tests/gh10168/assign_untyped_prop_ref.phpt @@ -27,6 +27,7 @@ test($box); ?> --EXPECT-- -NULL -object(Test)#2 (0) { +object(Test)#3 (0) { +} +object(Test)#3 (0) { } diff --git a/Zend/tests/gh10168/assign_untyped_prop_ref_with_prop_ref.phpt b/Zend/tests/gh10168/assign_untyped_prop_ref_with_prop_ref.phpt index 79ff5fefc07e..b0889ad832e0 100644 --- a/Zend/tests/gh10168/assign_untyped_prop_ref_with_prop_ref.phpt +++ b/Zend/tests/gh10168/assign_untyped_prop_ref_with_prop_ref.phpt @@ -32,4 +32,5 @@ test($box); --EXPECT-- object(Test)#3 (0) { } -NULL +object(Test)#4 (0) { +} diff --git a/Zend/tests/lazy_objects/reset_as_lazy_calls_destructor.phpt b/Zend/tests/lazy_objects/reset_as_lazy_calls_destructor.phpt index 733da55a5cbc..cf94e4f1a134 100644 --- a/Zend/tests/lazy_objects/reset_as_lazy_calls_destructor.phpt +++ b/Zend/tests/lazy_objects/reset_as_lazy_calls_destructor.phpt @@ -51,8 +51,8 @@ string(13) "C::__destruct" After makeLazy string(11) "initializer" int(1) -string(13) "C::__destruct" # Proxy: +string(13) "C::__destruct" In makeLazy string(13) "C::__destruct" After makeLazy diff --git a/Zend/tests/lazy_objects/reset_as_lazy_may_skip_destructor.phpt b/Zend/tests/lazy_objects/reset_as_lazy_may_skip_destructor.phpt index d0c22a0333ff..e45fbe7f8b29 100644 --- a/Zend/tests/lazy_objects/reset_as_lazy_may_skip_destructor.phpt +++ b/Zend/tests/lazy_objects/reset_as_lazy_may_skip_destructor.phpt @@ -50,8 +50,8 @@ In makeLazy After makeLazy string(11) "initializer" int(1) -string(13) "C::__destruct" # Proxy: +string(13) "C::__destruct" In makeLazy After makeLazy string(11) "initializer" diff --git a/Zend/tests/magic_methods/bug32596.phpt b/Zend/tests/magic_methods/bug32596.phpt index 40bd70382b30..ae22c82f4772 100644 --- a/Zend/tests/magic_methods/bug32596.phpt +++ b/Zend/tests/magic_methods/bug32596.phpt @@ -21,6 +21,6 @@ BUG::instance()->error; echo "this is still executed\n"; ?> --EXPECT-- +this is still executed BUG please fix this thing, it wasted a nice part of my life! -this is still executed diff --git a/Zend/tests/oss_fuzz_456317305.phpt b/Zend/tests/oss_fuzz_456317305.phpt index 37e7c59e4651..c0e3a1d70c95 100644 --- a/Zend/tests/oss_fuzz_456317305.phpt +++ b/Zend/tests/oss_fuzz_456317305.phpt @@ -21,5 +21,10 @@ $c = new C; Fatal error: Uncaught Exception in %s:%d Stack trace: #0 [internal function]: C->__destruct() +#1 {main} + +Next Exception in %s:%d +Stack trace: +#0 [internal function]: C->__destruct() #1 {main} thrown in %s on line %d diff --git a/Zend/tests/stack_limit/gh16041_001.phpt b/Zend/tests/stack_limit/gh16041_001.phpt index 4d3e41a54dcd..4a9a369354f8 100644 --- a/Zend/tests/stack_limit/gh16041_001.phpt +++ b/Zend/tests/stack_limit/gh16041_001.phpt @@ -27,13 +27,6 @@ new Canary(); ?> --EXPECTF-- [Successful compilation of %sgh16041_001.php] -prompt> prompt> [Uncaught Error in %s on line %d: Maximum call stack size of %d bytes%s ->00005: new Canary(); - 00006: } - 00007: } -prompt> [Uncaught Error in %s on line %d] -Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? in %s:%d -Stack trace: -#0 %s(%d): Canary->__destruct() -%a +prompt> prompt> [Script ended normally] +prompt> [Not running] prompt> diff --git a/Zend/tests/try/catch_novar_2.phpt b/Zend/tests/try/catch_novar_2.phpt index fa1ada927534..c36367b26682 100644 --- a/Zend/tests/try/catch_novar_2.phpt +++ b/Zend/tests/try/catch_novar_2.phpt @@ -11,16 +11,18 @@ class ThrowsOnDestruct extends Exception { try { throw new ThrowsOnDestruct(); } catch (Exception) { - echo "Unreachable catch\n"; + echo "Caught\n"; } -echo "Unreachable fallthrough\n"; +echo "After try/catch\n"; ?> --EXPECTF-- +Caught +After try/catch Throwing Fatal error: Uncaught RuntimeException: ThrowsOnDestruct::__destruct in %s:%d Stack trace: -#0 %s(%d): ThrowsOnDestruct->__destruct() +#0 [internal function]: ThrowsOnDestruct->__destruct() #1 {main} thrown in %s on line %d diff --git a/Zend/tests/type_coercion/type_casts/cast_to_void_destructor.phpt b/Zend/tests/type_coercion/type_casts/cast_to_void_destructor.phpt index 027e4b77e5dd..07774bb72f47 100644 --- a/Zend/tests/type_coercion/type_casts/cast_to_void_destructor.phpt +++ b/Zend/tests/type_coercion/type_casts/cast_to_void_destructor.phpt @@ -26,5 +26,5 @@ do_it(); ?> --EXPECT-- Before -WithDestructor::__destruct After +WithDestructor::__destruct diff --git a/Zend/tests/unset/unset_prop_recursion.phpt b/Zend/tests/unset/unset_prop_recursion.phpt index afb1929424f8..3c3a36ed8cb6 100644 --- a/Zend/tests/unset/unset_prop_recursion.phpt +++ b/Zend/tests/unset/unset_prop_recursion.phpt @@ -43,9 +43,9 @@ object(Node)#1 (2) { } } } -object(Node)#2 (2) { +object(Node)#3 (2) { ["parent"]=> - object(Node)#1 (2) { + object(Node)#1 (1) { ["parent"]=> NULL } @@ -53,9 +53,9 @@ object(Node)#2 (2) { array(0) { } } -object(Node)#3 (2) { +object(Node)#2 (2) { ["parent"]=> - object(Node)#1 (2) { + object(Node)#1 (1) { ["parent"]=> NULL } diff --git a/Zend/tests/weakrefs/gh13612.phpt b/Zend/tests/weakrefs/gh13612.phpt index 4ca4c9250876..327155a34246 100644 --- a/Zend/tests/weakrefs/gh13612.phpt +++ b/Zend/tests/weakrefs/gh13612.phpt @@ -35,5 +35,5 @@ echo "Done\n"; ?> --EXPECT-- -NULL Done +NULL diff --git a/Zend/tests/weakrefs/weakmap_iteration.phpt b/Zend/tests/weakrefs/weakmap_iteration.phpt index 1d1f6def8f48..60f34d78663a 100644 --- a/Zend/tests/weakrefs/weakmap_iteration.phpt +++ b/Zend/tests/weakrefs/weakmap_iteration.phpt @@ -38,24 +38,24 @@ var_dump($map); Empty loop: Simple loop: -object(stdClass)#2 (0) { +object(stdClass)#3 (0) { } int(0) -object(stdClass)#3 (0) { +object(stdClass)#4 (0) { } int(1) -object(stdClass)#4 (0) { +object(stdClass)#5 (0) { } int(2) Object removed during loop: -object(stdClass)#2 (0) { +object(stdClass)#3 (0) { } int(0) -object(stdClass)#3 (0) { +object(stdClass)#4 (0) { } int(1) -object(stdClass)#4 (0) { +object(stdClass)#5 (0) { } int(2) @@ -64,7 +64,7 @@ object(WeakMap)#1 (2) { [0]=> array(2) { ["key"]=> - object(stdClass)#2 (0) { + object(stdClass)#3 (0) { } ["value"]=> &int(1) @@ -72,7 +72,7 @@ object(WeakMap)#1 (2) { [1]=> array(2) { ["key"]=> - object(stdClass)#4 (0) { + object(stdClass)#5 (0) { } ["value"]=> &int(3) diff --git a/Zend/tests/weakrefs/weakmap_weakness.phpt b/Zend/tests/weakrefs/weakmap_weakness.phpt index d740d2b219c1..84d9fde53515 100644 --- a/Zend/tests/weakrefs/weakmap_weakness.phpt +++ b/Zend/tests/weakrefs/weakmap_weakness.phpt @@ -78,13 +78,13 @@ object(WeakMap)#1 (0) { Destructor in WeakMap value: Before unset: -Dtor! After unset: +Dtor! object(WeakMap)#1 (0) { } Destroying map with live object: -object(stdClass)#2 (0) { +object(stdClass)#3 (0) { } Object freed by GC: @@ -92,7 +92,7 @@ object(WeakMap)#1 (1) { [0]=> array(2) { ["key"]=> - object(stdClass)#3 (1) { + object(stdClass)#2 (1) { ["obj"]=> *RECURSION* } @@ -104,7 +104,7 @@ object(WeakMap)#1 (0) { } Storing object as own value: -object(WeakMap)#3 (1) { +object(WeakMap)#2 (1) { [0]=> array(2) { ["key"]=> @@ -117,7 +117,7 @@ object(WeakMap)#3 (1) { } Storing map in itself: -object(WeakMap)#3 (1) { +object(WeakMap)#2 (1) { [0]=> array(2) { ["key"]=> diff --git a/Zend/zend.c b/Zend/zend.c index f0df91c11e3d..8ab8466e7bd2 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1460,8 +1460,13 @@ static void zend_call_user_error_handler( return; } + zend_flush_deferred_dtors(); zend_flush_deferred_errors(); + if (UNEXPECTED(EG(exception))) { + return; + } + ZVAL_STR_COPY(¶ms[1], message); ZVAL_LONG(¶ms[0], type); @@ -2102,7 +2107,11 @@ ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handl zend_result ret = SUCCESS; if (op_array) { + zend_execute_data *previous_execute_data = EG(current_execute_data); zend_execute(op_array, retval); + EG(current_execute_data) = previous_execute_data; + zend_flush_deferred_dtors(); + zend_surface_deferred_dtor_exception(); zend_flush_deferred_errors(); if (UNEXPECTED(EG(exception))) { if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ac5a9d71a6ea..0066fe744463 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6009,6 +6009,10 @@ static void zend_compile_return(const zend_ast *ast) /* {{{ */ expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, false); } + if (CG(context).try_catch_offset != (uint32_t)-1) { + zend_emit_op(NULL, ZEND_FLUSH_DEFERRED_DTORS, NULL, NULL); + } + uint32_t opnum_before_finally = get_next_op_number(); zend_handle_loops_and_finally((expr_node.op_type & (IS_TMP_VAR | IS_VAR)) ? &expr_node : NULL); @@ -6076,6 +6080,8 @@ static void zend_compile_throw(znode *result, const zend_ast *ast) /* {{{ */ znode expr_node; zend_compile_expr(&expr_node, expr_ast); + zend_emit_op(NULL, ZEND_FLUSH_DEFERRED_DTORS, NULL, NULL); + zend_op *opline = zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL); if (result) { /* Mark this as an "expression throw" for opcache. */ @@ -6117,6 +6123,9 @@ static void zend_compile_break_continue(const zend_ast *ast) /* {{{ */ zend_error_noreturn(E_COMPILE_ERROR, "'%s' not in the 'loop' or 'switch' context", ast->kind == ZEND_AST_BREAK ? "break" : "continue"); } else { + if (CG(context).try_catch_offset != (uint32_t)-1) { + zend_emit_op(NULL, ZEND_FLUSH_DEFERRED_DTORS, NULL, NULL); + } if (!zend_handle_loops_and_finally_ex(depth, NULL)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' " ZEND_LONG_FMT " level%s", ast->kind == ZEND_AST_BREAK ? "break" : "continue", @@ -6232,6 +6241,10 @@ static void zend_compile_goto(const zend_ast *ast) /* {{{ */ zend_compile_expr(&label_node, label_ast); + if (CG(context).try_catch_offset != (uint32_t)-1) { + zend_emit_op(NULL, ZEND_FLUSH_DEFERRED_DTORS, NULL, NULL); + } + /* Label resolution and unwinding adjustments happen in pass two. */ uint32_t opnum_start = get_next_op_number(); zend_handle_loops_and_finally(NULL); @@ -7041,6 +7054,8 @@ static void zend_compile_try(const zend_ast *ast) /* {{{ */ } ZEND_HASH_FOREACH_END(); } + zend_emit_op(NULL, ZEND_FLUSH_DEFERRED_DTORS, NULL, NULL); + try_catch_offset = zend_add_try_element(get_next_op_number()); if (finally_ast) { @@ -7062,6 +7077,8 @@ static void zend_compile_try(const zend_ast *ast) /* {{{ */ zend_compile_stmt(try_ast); + zend_emit_op(NULL, ZEND_FLUSH_DEFERRED_DTORS, NULL, NULL); + if (catches->children != 0) { jmp_opnums[0] = zend_emit_jump(0); } @@ -7164,6 +7181,8 @@ static void zend_compile_try(const zend_ast *ast) /* {{{ */ zend_compile_stmt(finally_ast); + zend_emit_op(NULL, ZEND_FLUSH_DEFERRED_DTORS, NULL, NULL); + CG(active_op_array)->try_catch_array[try_catch_offset].finally_op = opnum_jmp + 1; CG(active_op_array)->try_catch_array[try_catch_offset].finally_end = get_next_op_number(); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 928bc3395e90..6cf5fdca2863 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4321,6 +4321,62 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt(zend_execute_data *ca if (zend_interrupt_function) { zend_interrupt_function(call); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } + } +} + +static zend_always_inline bool zend_interrupt_process( + zend_execute_data *execute_data, const zend_op *opline, bool flush_dtors) +{ + bool entered = false; + bool pending_dtors = EG(deferred_dtors).count || EG(deferred_dtors).values_count; + + if (EG(deferred_errors).size || pending_dtors) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } else { + if (flush_dtors + || (pending_dtors + && opline >= EX(func)->op_array.opcodes + && opline <= EX(func)->op_array.opcodes + EX(func)->op_array.num_args)) { + zend_flush_deferred_dtors(); + entered = true; + } else if (pending_dtors) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } + if (EG(deferred_errors).size) { + zend_flush_deferred_errors(); + entered = true; + } + } + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + + return entered; +} + +static zend_always_inline void zend_interrupt_handle_exception(void) +{ + if (EG(exception)) { + const zend_op *throw_op = EG(opline_before_exception); + + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } } @@ -4785,6 +4841,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o { if (UNEXPECTED(EX(call))) { zend_execute_data *call = EX(call); + EG(frame_teardown)++; zend_op *opline = EX(func)->op_array.opcodes + op_num; int level; int do_exit; @@ -4913,12 +4970,14 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o zend_vm_stack_free_call_frame(call); call = EX(call); } while (call); + EG(frame_teardown)--; } } /* }}} */ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */ { + EG(frame_teardown)++; for (uint32_t i = 0; i < EX(func)->op_array.last_live_range; i++) { const zend_live_range *range = &EX(func)->op_array.live_range[i]; if (range->start > op_num) { @@ -4979,6 +5038,7 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, } } } + EG(frame_teardown)--; } /* }}} */ @@ -5788,9 +5848,22 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint CHECK_SYMBOL_TABLES() \ OPLINE = new_op -#define ZEND_VM_SET_OPCODE(new_op) \ - ZEND_VM_SET_OPCODE_NO_INTERRUPT(new_op); \ - ZEND_VM_INTERRUPT_CHECK() +#define ZEND_VM_SET_OPCODE(new_op) do { \ + const zend_op *_target = (new_op); \ + if (UNEXPECTED(zend_atomic_bool_load_ex(&EG(vm_interrupt)))) { \ + bool _backward = _target <= OPLINE; \ + ZEND_VM_SET_OPCODE_NO_INTERRUPT(_target); \ + if (_backward) { \ + ZEND_VM_KIND_TAILCALL_SAVE_OPLINE(); \ + ZEND_VM_LOOP_INTERRUPT(); \ + } else { \ + SAVE_OPLINE(); \ + ZEND_VM_INTERRUPT(); \ + } \ + } else { \ + ZEND_VM_SET_OPCODE_NO_INTERRUPT(_target); \ + } \ + } while (0) #define ZEND_VM_SET_RELATIVE_OPCODE(opline, offset) \ ZEND_VM_SET_OPCODE(ZEND_OFFSET_TO_OPLINE(opline, offset)) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 0fd95ed7a5e3..37ecdd7d8e01 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -193,6 +193,10 @@ void init_executor(void) /* {{{ */ EG(record_errors) = false; memset(&EG(errors), 0, sizeof(EG(errors))); memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); + memset(&EG(deferred_dtors), 0, sizeof(EG(deferred_dtors))); + EG(deferred_dtor_exception) = NULL; + EG(dtor_defer_gate) = 1; + EG(frame_teardown) = 0; EG(filename_override) = NULL; EG(lineno_override) = -1; @@ -258,11 +262,21 @@ void shutdown_destructors(void) /* {{{ */ do { symbols = zend_hash_num_elements(&EG(symbol_table)); zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor); - } while (symbols != zend_hash_num_elements(&EG(symbol_table))); + zend_flush_deferred_dtors(); + zend_surface_deferred_dtor_exception(); + } while (symbols != zend_hash_num_elements(&EG(symbol_table)) + || (!EG(exception) && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))); zend_objects_store_call_destructors(&EG(objects_store)); + zend_flush_deferred_dtors(); + zend_surface_deferred_dtor_exception(); + if (UNEXPECTED(EG(exception) != NULL)) { + zend_exception_error(EG(exception), E_ERROR); + zend_bailout(); + } } zend_catch { /* if we couldn't destruct cleanly, mark all objects as destructed anyway */ zend_objects_store_mark_destructed(&EG(objects_store)); + zend_release_deferred_dtors(); } zend_end_try(); } /* }}} */ @@ -447,6 +461,11 @@ void shutdown_executor(void) /* {{{ */ #endif zend_free_deferred_errors(); + if (EG(deferred_dtor_exception)) { + GC_DELREF(EG(deferred_dtor_exception)); + EG(deferred_dtor_exception) = NULL; + } + zend_release_deferred_dtors(); zend_try { zend_stream_shutdown(); @@ -872,6 +891,14 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ } #endif + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !EG(frame_teardown))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + return SUCCESS; + } + } + call = zend_vm_stack_push_call_frame(call_info, func, fci->param_count, object_or_called_scope); uint32_t consumed_args = fci->param_count ? fci->consumed_args : 0; @@ -1058,6 +1085,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ #endif ZEND_OBSERVER_FCALL_END(call, fci->retval); EG(current_execute_data) = call->prev_execute_data; + EG(frame_teardown)++; zend_vm_stack_free_args(call); if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { zend_array_release(call->extra_named_params); @@ -1067,6 +1095,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ zval_ptr_dtor(fci->retval); ZVAL_UNDEF(fci->retval); } + EG(frame_teardown)--; /* This flag is regularly checked while running user functions, but not internal * So see whether interrupt flag was set while the function was running... */ @@ -1077,9 +1106,14 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ zend_interrupt_function(EG(current_execute_data)); } } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) { + EG(frame_teardown)++; OBJ_RELEASE(Z_OBJ(call->This)); + EG(frame_teardown)--; } } EG(fake_scope) = orig_fake_scope; @@ -1088,7 +1122,9 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ if (UNEXPECTED(EG(exception))) { if (UNEXPECTED(!EG(current_execute_data))) { - zend_throw_exception_internal(NULL); + if (!EG(frame_teardown)) { + zend_throw_exception_internal(NULL); + } } else if (EG(current_execute_data)->func && ZEND_USER_CODE(EG(current_execute_data)->func->common.type)) { zend_rethrow_exception(EG(current_execute_data)); diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index c91436050856..37315a8d014b 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -113,6 +113,9 @@ typedef struct _zend_fiber_vm_state { uint32_t jit_trace_num; JMP_BUF *bailout; zend_fiber *active_fiber; + zend_dtor_buf deferred_dtors; + uint32_t dtor_defer_gate; + uint32_t frame_teardown; #ifdef ZEND_CHECK_STACK_LIMIT void *stack_base; void *stack_limit; @@ -130,6 +133,9 @@ static zend_always_inline void zend_fiber_capture_vm_state(zend_fiber_vm_state * state->jit_trace_num = EG(jit_trace_num); state->bailout = EG(bailout); state->active_fiber = EG(active_fiber); + state->deferred_dtors = EG(deferred_dtors); + state->dtor_defer_gate = EG(dtor_defer_gate); + state->frame_teardown = EG(frame_teardown); #ifdef ZEND_CHECK_STACK_LIMIT state->stack_base = EG(stack_base); state->stack_limit = EG(stack_limit); @@ -147,10 +153,17 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state * EG(jit_trace_num) = state->jit_trace_num; EG(bailout) = state->bailout; EG(active_fiber) = state->active_fiber; + EG(deferred_dtors) = state->deferred_dtors; + EG(dtor_defer_gate) = state->dtor_defer_gate; + EG(frame_teardown) = state->frame_teardown; #ifdef ZEND_CHECK_STACK_LIMIT EG(stack_base) = state->stack_base; EG(stack_limit) = state->stack_limit; #endif + + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } } #ifdef ZEND_FIBER_UCONTEXT @@ -598,6 +611,10 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) EG(jit_trace_num) = 0; EG(error_reporting) = error_reporting; + memset(&EG(deferred_dtors), 0, sizeof(EG(deferred_dtors))); + EG(dtor_defer_gate) = 1; + EG(frame_teardown) = 0; + #ifdef ZEND_CHECK_STACK_LIMIT EG(stack_base) = zend_fiber_stack_base(fiber->context.stack); EG(stack_limit) = zend_fiber_stack_limit(fiber->context.stack); @@ -607,6 +624,10 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) zend_call_function(&fiber->fci, &fiber->fci_cache); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + } + /* Cleanup callback and unset field to prevent GC / duplicate dtor issues. */ zval_ptr_dtor(&fiber->fci.function_name); ZVAL_UNDEF(&fiber->fci.function_name); @@ -626,6 +647,7 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) } zend_catch { fiber->flags |= ZEND_FIBER_FLAG_BAILOUT; transfer->flags = ZEND_FIBER_TRANSFER_FLAG_BAILOUT; + zend_release_deferred_dtors(); } zend_end_try(); fiber->context.cleanup = &zend_fiber_cleanup; diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index 5de2b69bf568..cf37cdd9bf83 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -732,6 +732,23 @@ static zend_never_inline void ZEND_FASTCALL gc_possible_root_when_full(zend_refc GC_BENCH_PEAK(root_buf_peak, root_buf_length); } +ZEND_API bool ZEND_FASTCALL zend_gc_is_condemned(zend_refcounted *ref) +{ + uint32_t idx = GC_REF_ADDRESS(ref); + + if (idx == 0) { + return false; + } + + gc_root_buffer *slot; + if (UNEXPECTED(GC_G(first_unused) >= GC_MAX_UNCOMPRESSED)) { + slot = gc_decompress(ref, idx); + } else { + slot = GC_IDX2PTR(idx); + } + return GC_IS_GARBAGE(slot->ref) || GC_IS_DTOR_GARBAGE(slot->ref); +} + /* Add a possible root node to the buffer. * Maybe perform a GC run. */ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref) @@ -743,6 +760,11 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref) return; } + if (UNEXPECTED(GC_TYPE(ref) == IS_OBJECT + && (OBJ_EXTRA_FLAGS((zend_object*)ref) & IS_OBJ_DTOR_PENDING))) { + return; + } + GC_BENCH_INC(zval_possible_root); if (EXPECTED(GC_HAS_UNUSED())) { diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h index b72b90ccd5ef..7a872d1fe19f 100644 --- a/Zend/zend_gc.h +++ b/Zend/zend_gc.h @@ -46,6 +46,7 @@ ZEND_API extern int (*gc_collect_cycles)(void); ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref); ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref); +ZEND_API bool ZEND_FASTCALL zend_gc_is_condemned(zend_refcounted *ref); /* enable/disable automatic start of GC collection */ ZEND_API bool gc_enable(bool enable); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 52d409f2e69b..bbbfb544646e 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -130,14 +130,70 @@ static void zend_generator_cleanup_unfinished_execution( } /* }}} */ +static void zend_generator_drain_deferred_dtors(zend_generator *generator) +{ + if (EXPECTED(!generator->deferred_dtors.count && !generator->deferred_dtors.values_count)) { + return; + } + + zend_dtor_buf original_deferred_dtors = EG(deferred_dtors); + + EG(deferred_dtors) = generator->deferred_dtors; + memset(&generator->deferred_dtors, 0, sizeof(generator->deferred_dtors)); + + if (UNEXPECTED(CG(unclean_shutdown))) { + zend_release_deferred_dtors(); + } else { + zend_flush_deferred_dtors(); + } + + EG(deferred_dtors) = original_deferred_dtors; +} + +static zend_always_inline void zend_generator_enter_dtor_state(zend_generator *generator, zend_dtor_buf *deferred_dtors, uint32_t *dtor_defer_gate, uint32_t *frame_teardown) +{ + *deferred_dtors = EG(deferred_dtors); + *dtor_defer_gate = EG(dtor_defer_gate); + *frame_teardown = EG(frame_teardown); + + EG(deferred_dtors) = generator->deferred_dtors; + EG(dtor_defer_gate) = generator->dtor_defer_gate; + EG(frame_teardown) = generator->frame_teardown; + + memset(&generator->deferred_dtors, 0, sizeof(generator->deferred_dtors)); + generator->frame_teardown = 0; + + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } +} + +static zend_always_inline void zend_generator_leave_dtor_state(zend_generator *generator, const zend_dtor_buf *deferred_dtors, uint32_t dtor_defer_gate, uint32_t frame_teardown) +{ + generator->deferred_dtors = EG(deferred_dtors); + generator->dtor_defer_gate = EG(dtor_defer_gate); + generator->frame_teardown = EG(frame_teardown); + + EG(deferred_dtors) = *deferred_dtors; + EG(dtor_defer_gate) = dtor_defer_gate; + EG(frame_teardown) = frame_teardown; + + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } +} + ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution) /* {{{ */ { + zend_generator_drain_deferred_dtors(generator); + if (EXPECTED(generator->execute_data)) { zend_execute_data *execute_data = generator->execute_data; /* Null out execute_data early, to prevent double frees if GC runs while we're * already cleaning up execute_data. */ generator->execute_data = NULL; + EG(frame_teardown)++; if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) { zend_clean_and_cache_symbol_table(execute_data->symbol_table); } @@ -150,6 +206,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, bool finished_exec if (EX_CALL_INFO() & ZEND_CALL_RELEASE_THIS) { OBJ_RELEASE(Z_OBJ(execute_data->This)); } + EG(frame_teardown)--; /* A fatal error / die occurred during the generator execution. * Trying to clean up the stack may not be safe in this case. */ @@ -158,7 +215,9 @@ ZEND_API void zend_generator_close(zend_generator *generator, bool finished_exec return; } + EG(frame_teardown)++; zend_vm_stack_free_extra_args(execute_data); + EG(frame_teardown)--; /* Some cleanups are only necessary if the generator was closed * before it could finish execution (reach a return statement). */ @@ -470,6 +529,8 @@ static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ * /* The key will be incremented on first use, so it'll start at 0 */ generator->largest_used_integer_key = -1; + generator->dtor_defer_gate = 1; + ZVAL_UNDEF(&generator->retval); ZVAL_UNDEF(&generator->values); @@ -519,12 +580,17 @@ static zend_result zend_generator_throw_exception(zend_generator *generator, zva } zend_execute_data *original_execute_data = EG(current_execute_data); + zend_dtor_buf original_deferred_dtors; + uint32_t original_dtor_defer_gate; + uint32_t original_frame_teardown; /* Throw the exception in the context of the generator. Decrementing the opline * to pretend the exception happened during the YIELD opcode. */ EG(current_execute_data) = generator->execute_data; generator->execute_data->prev_execute_data = original_execute_data; + zend_generator_enter_dtor_state(generator, &original_deferred_dtors, &original_dtor_defer_gate, &original_frame_teardown); + if (exception) { zend_throw_exception_object(exception); } else { @@ -537,6 +603,7 @@ static zend_result zend_generator_throw_exception(zend_generator *generator, zva ZVAL_UNDEF(&generator->values); } + zend_generator_leave_dtor_state(generator, &original_deferred_dtors, original_dtor_defer_gate, original_frame_teardown); EG(current_execute_data) = original_execute_data; return SUCCESS; @@ -791,11 +858,16 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ /* Backup executor globals */ zend_execute_data *original_execute_data = EG(current_execute_data); uint32_t original_jit_trace_num = EG(jit_trace_num); + zend_dtor_buf original_deferred_dtors; + uint32_t original_dtor_defer_gate; + uint32_t original_frame_teardown; /* Set executor globals */ EG(current_execute_data) = generator->execute_data; EG(jit_trace_num) = 0; + zend_generator_enter_dtor_state(generator, &original_deferred_dtors, &original_dtor_defer_gate, &original_frame_teardown); + /* We want the backtrace to look as if the generator function was * called from whatever method we are current running (e.g. next()). * So we have to link generator call frame with caller call frame. */ @@ -814,6 +886,7 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ if (UNEXPECTED(!Z_ISUNDEF(generator->values))) { if (EXPECTED(zend_generator_get_next_delegated_value(generator) == SUCCESS)) { /* Restore executor globals */ + zend_generator_leave_dtor_state(generator, &original_deferred_dtors, original_dtor_defer_gate, original_frame_teardown); EG(current_execute_data) = original_execute_data; EG(jit_trace_num) = original_jit_trace_num; @@ -864,6 +937,12 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ EG(current_execute_data) = original_execute_data; EG(jit_trace_num) = original_jit_trace_num; + if (UNEXPECTED(!generator->execute_data) + && UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + } + zend_generator_leave_dtor_state(generator, &original_deferred_dtors, original_dtor_defer_gate, original_frame_teardown); + /* If an exception was thrown in the generator we have to internally * rethrow it in the parent scope. * In case we did yield from, the Exception must be rethrown into diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 73dd9ddc1f91..18d9b89db76c 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -22,6 +22,7 @@ #include #include "zend_compile.h" +#include "zend_globals.h" BEGIN_EXTERN_C() @@ -91,6 +92,10 @@ struct _zend_generator { * the generator is alive. */ zend_function *func; + zend_dtor_buf deferred_dtors; + uint32_t dtor_defer_gate; + uint32_t frame_teardown; + /* ZEND_GENERATOR_* flags */ uint8_t flags; }; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 8a267ff7149e..189e399774df 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -87,6 +87,15 @@ typedef struct zend_err_buf { zend_error_info **errors; } zend_err_buf; +typedef struct zend_dtor_buf { + uint32_t count; + uint32_t capacity; + zend_object **objects; + uint32_t values_count; + uint32_t values_capacity; + zval *values; +} zend_dtor_buf; + struct _zend_compiler_globals { zend_stack loop_var_stack; @@ -311,6 +320,11 @@ struct _zend_executor_globals { * structure the opcode still holds a pointer into. */ zend_err_buf deferred_errors; + zend_dtor_buf deferred_dtors; + zend_object *deferred_dtor_exception; + uint32_t dtor_defer_gate; + uint32_t frame_teardown; + /* Override filename or line number of thrown errors and exceptions */ zend_string *filename_override; zend_long lineno_override; diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 474157e73d39..f32f382e577a 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -115,6 +115,11 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) zend_function *destructor = object->ce->destructor; if (destructor) { + uint32_t defer_flags = OBJ_EXTRA_FLAGS(object) + & (IS_OBJ_DTOR_PENDING|IS_OBJ_DTOR_VIS_FAIL); + + OBJ_EXTRA_FLAGS(object) &= ~(IS_OBJ_DTOR_PENDING|IS_OBJ_DTOR_VIS_FAIL); + if (UNEXPECTED(zend_object_is_lazy(object))) { return; } @@ -123,23 +128,32 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) const zend_op *old_opline_before_exception = NULL; if (destructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) { - if (EG(current_execute_data)) { - zend_class_entry *scope = zend_get_executed_scope(); - /* Ensure that if we're calling a protected or private function, we're allowed to do so. */ - ZEND_ASSERT(!(destructor->common.fn_flags & ZEND_ACC_PUBLIC)); - if (!zend_check_method_accessible(destructor, scope)) { - zend_throw_error(NULL, + zend_class_entry *scope = NULL; + bool accessible; + + ZEND_ASSERT(!(destructor->common.fn_flags & ZEND_ACC_PUBLIC)); + if (defer_flags & IS_OBJ_DTOR_PENDING) { + accessible = !(defer_flags & IS_OBJ_DTOR_VIS_FAIL); + if (!accessible) { + scope = zend_get_executed_scope(); + } + } else { + scope = zend_get_executed_scope(); + accessible = zend_check_method_accessible(destructor, scope); + } + if (!accessible) { + if (EG(current_execute_data) || !(EG(flags) & EG_FLAGS_IN_SHUTDOWN)) { + zend_throw_exception_ex(zend_ce_error, 0, "Call to %s %s::__destruct() from %s%s", zend_visibility_string(destructor->common.fn_flags), ZSTR_VAL(object->ce->name), scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : "" ); - return; + } else { + zend_error(E_WARNING, + "Call to %s %s::__destruct() from global scope during shutdown ignored", + zend_visibility_string(destructor->common.fn_flags), ZSTR_VAL(object->ce->name)); } - } else { - zend_error(E_WARNING, - "Call to %s %s::__destruct() from global scope during shutdown ignored", - zend_visibility_string(destructor->common.fn_flags), ZSTR_VAL(object->ce->name)); return; } } diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 537cad8a3644..dcc5db519499 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -22,7 +22,10 @@ #include "zend_variables.h" #include "zend_API.h" #include "zend_objects_API.h" +#include "zend_exceptions.h" #include "zend_fibers.h" +#include "zend_weakrefs.h" +#include "zend_gc.h" ZEND_API void ZEND_FASTCALL zend_objects_store_init(zend_objects_store *objects, uint32_t init_size) { @@ -39,6 +42,346 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_destroy(zend_objects_store *objec objects->object_buckets = NULL; } +ZEND_API void ZEND_FASTCALL zend_defer_dtor(zend_object *object) +{ + zend_dtor_buf *buf = &EG(deferred_dtors); + zend_function *destructor = object->ce->destructor; + + ZEND_ASSERT(GC_REFCOUNT(object) == 0); + GC_ADDREF(object); + OBJ_EXTRA_FLAGS(object) |= IS_OBJ_DTOR_PENDING; + if (destructor + && UNEXPECTED(destructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) + && !zend_check_method_accessible(destructor, zend_get_executed_scope())) { + OBJ_EXTRA_FLAGS(object) |= IS_OBJ_DTOR_VIS_FAIL; + } + GC_REMOVE_FROM_BUFFER(object); + + if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) { + zend_weakrefs_notify_detach(object); + } + + if (UNEXPECTED(buf->count == buf->capacity)) { + uint32_t capacity = buf->capacity ? buf->capacity + (buf->capacity >> 1) : 8; + buf->objects = erealloc(buf->objects, sizeof(zend_object*) * capacity); + buf->capacity = capacity; + } + buf->objects[buf->count++] = object; + + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); +} + +static zend_always_inline void zend_dtor_flush_stash_exception(zend_object **pending, const zend_op **first_throw_opline) +{ + if (UNEXPECTED(EG(exception))) { + if (*pending) { + if (zend_is_unwind_exit(*pending) || zend_is_graceful_exit(*pending)) { + OBJ_RELEASE(EG(exception)); + EG(exception) = NULL; + return; + } + if (zend_is_unwind_exit(EG(exception)) || zend_is_graceful_exit(EG(exception))) { + OBJ_RELEASE(*pending); + } else { + zend_exception_set_previous(EG(exception), *pending); + } + } + *pending = EG(exception); + EG(exception) = NULL; + if (!*first_throw_opline) { + *first_throw_opline = EG(opline_before_exception); + } + } +} + +static zend_always_inline bool zend_dtor_flush_get_op_num( + const zend_op_array *op_array, const zend_op *opline, uint32_t *op_num) +{ + if (!opline || (uintptr_t) opline < (uintptr_t) op_array->opcodes) { + return false; + } + + uintptr_t offset = (uintptr_t) opline - (uintptr_t) op_array->opcodes; + if (offset >= (uintptr_t) op_array->last * sizeof(zend_op) + || offset % sizeof(zend_op) != 0) { + return false; + } + + *op_num = offset / sizeof(zend_op); + return true; +} + +static zend_always_inline zend_execute_data *zend_dtor_flush_find_frame( + zend_execute_data *execute_data, const zend_op *opline, uint32_t *op_num) +{ + for (; execute_data; execute_data = execute_data->prev_execute_data) { + if (!execute_data->func + || !ZEND_USER_CODE(execute_data->func->common.type)) { + continue; + } + + if (zend_dtor_flush_get_op_num( + &execute_data->func->op_array, opline, op_num)) { + return execute_data; + } + } + + return NULL; +} + +static zend_always_inline bool zend_dtor_flush_exception_is_catchable( + zend_execute_data *execute_data, const zend_op *first_throw_opline, + const zend_op *flush_opline) +{ + uint32_t throw_op_num; + zend_execute_data *throw_frame = zend_dtor_flush_find_frame( + execute_data, first_throw_opline, &throw_op_num); + + if (!throw_frame) { + throw_frame = zend_dtor_flush_find_frame( + execute_data, flush_opline, &throw_op_num); + } + if (!throw_frame) { + for (zend_execute_data *ex = execute_data; ex; ex = ex->prev_execute_data) { + if (ex->func && ZEND_USER_CODE(ex->func->common.type) && ex->opline + && zend_dtor_flush_get_op_num(&ex->func->op_array, ex->opline, &throw_op_num)) { + throw_frame = ex; + break; + } + } + } + if (!throw_frame) { + return false; + } + + execute_data = throw_frame; + for (; execute_data; execute_data = execute_data->prev_execute_data) { + if (!execute_data->func + || !ZEND_USER_CODE(execute_data->func->common.type) + || !execute_data->opline) { + continue; + } + + const zend_op_array *op_array = &execute_data->func->op_array; + uint32_t op_num; + + if (execute_data == throw_frame) { + op_num = throw_op_num; + } else if (!zend_dtor_flush_get_op_num( + op_array, execute_data->opline, &op_num)) { + continue; + } + + const zend_op *opline = &op_array->opcodes[op_num]; + if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) + && (opline->extended_value & ZEND_FREE_ON_RETURN)) { + op_num += opline->op2.opline_num; + } + + for (uint32_t i = 0; i < op_array->last_try_catch; i++) { + const zend_try_catch_element *try_catch = &op_array->try_catch_array[i]; + + if (try_catch->try_op > op_num) { + break; + } + if (op_num < try_catch->catch_op || op_num < try_catch->finally_end) { + return true; + } + } + } + + return false; +} + +static zend_always_inline bool zend_dtor_flush_release_throw_operand_at( + zend_execute_data *execute_data, const zend_op *flush_opline) +{ + uint32_t op_num; + zend_execute_data *frame = zend_dtor_flush_find_frame( + execute_data, flush_opline, &op_num); + + if (!frame) { + return false; + } + + const zend_op_array *op_array = &frame->func->op_array; + if (op_num + 1 >= op_array->last + || op_array->opcodes[op_num].opcode != ZEND_FLUSH_DEFERRED_DTORS) { + return false; + } + + const zend_op *throw_op = &op_array->opcodes[op_num + 1]; + if (throw_op->opcode != ZEND_THROW) { + return false; + } + + if (throw_op->op1_type & (IS_TMP_VAR | IS_VAR)) { + zval *operand = ZEND_CALL_VAR(frame, throw_op->op1.var); + + EG(frame_teardown)++; + zval_ptr_dtor_nogc(operand); + EG(frame_teardown)--; + ZVAL_UNDEF(operand); + } + + return true; +} + +static zend_always_inline void zend_dtor_flush_release_throw_operand( + zend_execute_data *execute_data, const zend_op *first_throw_opline, + const zend_op *flush_opline) +{ + if (zend_dtor_flush_release_throw_operand_at( + execute_data, first_throw_opline)) { + return; + } + + zend_dtor_flush_release_throw_operand_at( + execute_data, flush_opline); +} + +static zend_always_inline void zend_flush_deferred_dtors_ex(bool surface_uncatchable) +{ + zend_dtor_buf *buf = &EG(deferred_dtors); + zend_execute_data *execute_data = EG(current_execute_data); + const zend_op *flush_opline = execute_data && execute_data->func + && ZEND_USER_CODE(execute_data->func->common.type) ? execute_data->opline : NULL; + const zend_op *saved_opline_before_exception = EG(opline_before_exception); + zend_object *pending = EG(exception); + const zend_op *first_throw_opline = NULL; + bool had_exception = pending != NULL; + bool opline_rewound = false; + + if (UNEXPECTED(pending != NULL)) { + EG(exception) = NULL; + if (flush_opline == EG(exception_op) + && saved_opline_before_exception) { + execute_data->opline = saved_opline_before_exception; + opline_rewound = true; + } + } + + EG(frame_teardown)++; + while (buf->count || buf->values_count) { + zend_dtor_buf batch = *buf; + memset(buf, 0, sizeof(*buf)); + + for (uint32_t i = 0; i < batch.count; i++) { + zend_object *object = batch.objects[i]; + OBJ_RELEASE(object); + zend_dtor_flush_stash_exception(&pending, &first_throw_opline); + } + if (batch.objects) { + efree(batch.objects); + } + + for (uint32_t j = 0; j < batch.values_count; j++) { + zval_ptr_dtor(&batch.values[j]); + zend_dtor_flush_stash_exception(&pending, &first_throw_opline); + } + if (batch.values) { + efree(batch.values); + } + } + EG(frame_teardown)--; + + if (opline_rewound) { + execute_data->opline = EG(exception_op); + } + + if (EXPECTED(pending == NULL)) { + return; + } + + bool catchable = !had_exception + && zend_dtor_flush_exception_is_catchable( + execute_data, first_throw_opline, flush_opline); + + if (had_exception + || catchable + || surface_uncatchable + || zend_is_unwind_exit(pending) + || zend_is_graceful_exit(pending)) { + zend_execute_data *ex = EG(current_execute_data); + + EG(exception) = pending; + if (had_exception) { + EG(opline_before_exception) = saved_opline_before_exception; + } else if (first_throw_opline + && ex && ex->func && ZEND_USER_CODE(ex->func->common.type) + && ex->opline == EG(exception_op)) { + EG(opline_before_exception) = first_throw_opline; + } + + zend_dtor_flush_release_throw_operand( + execute_data, first_throw_opline, flush_opline); + return; + } + + if (EG(deferred_dtor_exception)) { + zend_object *previous = EG(deferred_dtor_exception); + EG(deferred_dtor_exception) = NULL; + zend_exception_set_previous(pending, previous); + } + EG(deferred_dtor_exception) = pending; + + if (flush_opline && execute_data->opline == EG(exception_op)) { + execute_data->opline = flush_opline; + } + EG(opline_before_exception) = saved_opline_before_exception; +} + +ZEND_API void ZEND_FASTCALL zend_flush_deferred_dtors(void) +{ + zend_flush_deferred_dtors_ex(false); +} + +ZEND_API void ZEND_FASTCALL zend_flush_deferred_dtors_surface(void) +{ + zend_flush_deferred_dtors_ex(true); +} + +ZEND_API void ZEND_FASTCALL zend_surface_deferred_dtor_exception(void) +{ + zend_object *exception = EG(deferred_dtor_exception); + + if (!exception) { + return; + } + if (EG(exception) + && (zend_is_unwind_exit(EG(exception)) || zend_is_graceful_exit(EG(exception)))) { + return; + } + + EG(deferred_dtor_exception) = NULL; + if (EG(exception)) { + zend_exception_set_previous(EG(exception), exception); + } else { + EG(exception) = exception; + } +} + +ZEND_API void ZEND_FASTCALL zend_release_deferred_dtors(void) +{ + zend_dtor_buf *buf = &EG(deferred_dtors); + + for (uint32_t i = 0; i < buf->count; i++) { + zend_object *object = buf->objects[i]; + OBJ_EXTRA_FLAGS(object) &= ~(IS_OBJ_DTOR_PENDING|IS_OBJ_DTOR_VIS_FAIL); + GC_DELREF(object); + } + for (uint32_t i = 0; i < buf->values_count; i++) { + zval_ptr_dtor(&buf->values[i]); + } + if (buf->objects) { + efree(buf->objects); + } + if (buf->values) { + efree(buf->values); + } + memset(buf, 0, sizeof(*buf)); +} + ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_store *objects) { EG(flags) |= EG_FLAGS_OBJECT_STORE_NO_REUSE; @@ -156,6 +499,45 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_put(zend_object *object) EG(objects_store).object_buckets[handle] = object; } +static zend_always_inline bool zend_can_defer_dtor(zend_object *object) +{ + if (!EG(dtor_defer_gate) || EG(frame_teardown)) { + return false; + } + if (OBJ_EXTRA_FLAGS(object) & IS_OBJ_DTOR_PENDING) { + return false; + } + if (CG(in_compilation) || !EG(current_execute_data) || !EG(current_execute_data)->func) { + return false; + } + zend_execute_data *ex = EG(current_execute_data); + if (!ZEND_USER_CODE(ex->func->type) || !ex->opline) { + return false; + } + switch (ex->opline->opcode) { + case ZEND_DECLARE_CLASS: + case ZEND_DECLARE_CLASS_DELAYED: + case ZEND_DECLARE_ANON_CLASS: + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: + case ZEND_DO_FCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_DO_UCALL: + case ZEND_DO_ICALL: + case ZEND_FETCH_CONSTANT: + case ZEND_FETCH_CLASS_CONSTANT: + case ZEND_INCLUDE_OR_EVAL: + return false; + } + return true; +} + ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object) /* {{{ */ { ZEND_ASSERT(GC_REFCOUNT(object) == 0); @@ -165,15 +547,24 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object) /* {{{ * return; } - /* Make sure we hold a reference count during the destructor call - otherwise, when the destructor ends the storage might be freed - when the refcount reaches 0 a second time - */ if (!(OBJ_FLAGS(object) & IS_OBJ_DESTRUCTOR_CALLED)) { + bool has_dtor = object->handlers->dtor_obj != zend_objects_destroy_object + || object->ce->destructor; + + if (has_dtor + && zend_can_defer_dtor(object) + && !zend_gc_is_condemned((zend_refcounted*)object)) { + zend_defer_dtor(object); + return; + } + + /* Make sure we hold a reference count during the destructor call + otherwise, when the destructor ends the storage might be freed + when the refcount reaches 0 a second time + */ GC_ADD_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); - if (object->handlers->dtor_obj != zend_objects_destroy_object - || object->ce->destructor) { + if (has_dtor) { GC_SET_REFCOUNT(object, 1); object->handlers->dtor_obj(object); GC_DELREF(object); diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h index 434ac4499e7f..2a5bc8dad426 100644 --- a/Zend/zend_objects_API.h +++ b/Zend/zend_objects_API.h @@ -62,6 +62,12 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_destroy(zend_objects_store *objec ZEND_API void ZEND_FASTCALL zend_objects_store_put(zend_object *object); ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object); +ZEND_API void ZEND_FASTCALL zend_defer_dtor(zend_object *object); +ZEND_API void ZEND_FASTCALL zend_flush_deferred_dtors(void); +ZEND_API void ZEND_FASTCALL zend_flush_deferred_dtors_surface(void); +ZEND_API void ZEND_FASTCALL zend_surface_deferred_dtor_exception(void); +ZEND_API void ZEND_FASTCALL zend_release_deferred_dtors(void); + /* Called when the ctor was terminated by an exception */ static zend_always_inline void zend_object_store_ctor_failed(zend_object *obj) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 56774e4e9d8f..5e630faa28a8 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -943,6 +943,8 @@ static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint3 #define IS_OBJ_LAZY_UNINITIALIZED (1U<<31) /* Virtual proxy or uninitialized Ghost */ #define IS_OBJ_LAZY_PROXY (1U<<30) /* Virtual proxy (may be initialized) */ +#define IS_OBJ_DTOR_PENDING (1U<<29) /* Destructor deferred to the next VM safepoint */ +#define IS_OBJ_DTOR_VIS_FAIL (1U<<28) /* Deferred destructor inaccessible from the drop-site scope */ #define OBJ_EXTRA_FLAGS(obj) ((obj)->extra_flags) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 95f53eb8053d..3622bd8d9019 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2998,6 +2998,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -3008,6 +3009,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; EG(vm_stack_top) = (zval*)execute_data; execute_data = EX(prev_execute_data); @@ -3020,6 +3022,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -3042,6 +3045,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; old_execute_data = execute_data; execute_data = EX(prev_execute_data); @@ -3059,8 +3063,10 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) zend_detach_symbol_table(execute_data); call_info |= ZEND_CALL_NEEDS_REATTACH; } + EG(frame_teardown)++; zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); + EG(frame_teardown)--; efree_size(EX(func), sizeof(zend_op_array)); old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); @@ -3083,6 +3089,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD call_info = EX_CALL_INFO(); @@ -3099,6 +3106,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; ZEND_VM_RETURN(); } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { zend_array *symbol_table = EX(symbol_table); @@ -3281,6 +3289,13 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, LOOP_END) SAVE_OPLINE(); FREE_OP1(); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -3296,6 +3311,13 @@ ZEND_VM_HOT_HANDLER(127, ZEND_FE_FREE, TMPVAR, LOOP_END) zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -3304,6 +3326,13 @@ ZEND_VM_HOT_HANDLER(127, ZEND_FE_FREE, TMPVAR, LOOP_END) if (Z_REFCOUNTED_P(var) && !Z_DELREF_P(var)) { SAVE_OPLINE(); rc_dtor_func(Z_COUNTED_P(var)); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ZEND_VM_NEXT_OPCODE(); @@ -4149,6 +4178,15 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + ZEND_VM_C_GOTO(icall_skip); + } + } + ZEND_OBSERVER_FCALL_BEGIN(call); fbc->internal_function.handler(call, ret); @@ -4171,6 +4209,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +ZEND_VM_C_LABEL(icall_skip): EG(current_execute_data) = execute_data; zend_vm_stack_free_args(call); @@ -4282,6 +4321,15 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER)) ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + ZEND_VM_C_GOTO(fcall_by_name_skip); + } + } + ZEND_OBSERVER_FCALL_BEGIN(call); fbc->internal_function.handler(call, ret); @@ -4305,6 +4353,7 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER)) ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +ZEND_VM_C_LABEL(fcall_by_name_skip): EG(current_execute_data) = execute_data; ZEND_VM_C_GOTO(fcall_by_name_end); @@ -4410,6 +4459,15 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + ZEND_VM_C_GOTO(fcall_skip); + } + } + ZEND_OBSERVER_FCALL_BEGIN(call); if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ @@ -4438,6 +4496,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +ZEND_VM_C_LABEL(fcall_skip): EG(current_execute_data) = execute_data; ZEND_VM_C_GOTO(fcall_end); @@ -4779,6 +4838,13 @@ ZEND_VM_HANDLER(161, ZEND_GENERATOR_RETURN, CONST|TMP|VAR|CV, ANY, SPEC(OBSERVER zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + FREE_OP1(); + HANDLE_EXCEPTION(); + } + } retval = GET_OP1_ZVAL_PTR(BP_VAR_R); /* Copy return value into generator->retval */ @@ -8183,7 +8249,9 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca && (EX(func)->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)].op2_type & (IS_TMP_VAR | IS_VAR))) { zval *return_value = EX_VAR(EX(func)->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)].op2.var); + EG(frame_teardown)++; zval_ptr_dtor(return_value); + EG(frame_teardown)--; } /* Chain potential exception from wrapping finally block */ @@ -8191,7 +8259,9 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca if (ex) { if (zend_is_unwind_exit(ex) || zend_is_graceful_exit(ex)) { /* discard the previously thrown exception */ + EG(frame_teardown)++; OBJ_RELEASE(Z_OBJ_P(fast_call)); + EG(frame_teardown)--; } else { zend_exception_set_previous(ex, Z_OBJ_P(fast_call)); } @@ -8227,15 +8297,22 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) { const zend_op *throw_op = EG(opline_before_exception); - if (EG(deferred_errors).size) { + if (EG(deferred_errors).size || EG(deferred_dtors).count || EG(deferred_dtors).values_count) { zend_object *orig_exception = EG(exception); EX(opline) = EG(opline_before_exception); EG(exception) = NULL; + zend_flush_deferred_dtors(); zend_flush_deferred_errors(); if (EG(exception)) { - zend_exception_set_previous(EG(exception), orig_exception); + if (UNEXPECTED(zend_is_unwind_exit(orig_exception) || zend_is_graceful_exit(orig_exception))) { + OBJ_RELEASE(EG(exception)); + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } else { + zend_exception_set_previous(EG(exception), orig_exception); + } } else { EG(exception) = orig_exception; EX(opline) = EG(exception_op); @@ -8469,6 +8546,16 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|CV|UNUSED, S ZEND_VM_DISPATCH_TO_HELPER(zend_yield_in_closed_generator_helper); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + FREE_OP2(); + FREE_OP1(); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -8595,6 +8682,15 @@ ZEND_VM_HANDLER(166, ZEND_YIELD_FROM, CONST|TMP|CV, ANY) HANDLE_EXCEPTION(); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + FREE_OP1(); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + ZEND_VM_C_LABEL(yield_from_try_again): if (Z_TYPE_P(val) == IS_ARRAY) { ZVAL_COPY_VALUE(&generator->values, val); @@ -8729,7 +8825,9 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH) if (Z_OPLINE_NUM_P(fast_call) != (uint32_t)-1) { const zend_op *fast_ret = EX(func)->op_array.opcodes + Z_OPLINE_NUM_P(fast_call); - ZEND_VM_JMP_EX(fast_ret + 1, 0); + ZEND_VM_SET_OPCODE_NO_INTERRUPT(fast_ret + 1); + ZEND_VM_INTERRUPT_CHECK(); + ZEND_VM_CONTINUE(); } /* special case for unhandled exceptions */ @@ -8950,6 +9048,25 @@ ZEND_VM_HOT_HANDLER(211, ZEND_TYPE_ASSERT, CONST, ANY, NUM) ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +ZEND_VM_HOT_HANDLER(212, ZEND_FLUSH_DEFERRED_DTORS, ANY, ANY) +{ + USE_OPLINE + + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + SAVE_OPLINE(); + if ((opline + 1)->opcode == ZEND_THROW) { + zend_flush_deferred_dtors_surface(); + } else { + zend_flush_deferred_dtors(); + } + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + HANDLE_EXCEPTION(); + } + } + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HOT_HANDLER(122, ZEND_DEFINED, CONST, ANY, CACHE_SLOT) { USE_OPLINE @@ -10654,42 +10771,28 @@ ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else { - bool entered = false; - if (EG(deferred_errors).size) { - if (EX(call) - || opline->opcode == ZEND_DO_FCALL - || opline->opcode == ZEND_DO_ICALL - || opline->opcode == ZEND_DO_UCALL - || opline->opcode == ZEND_DO_FCALL_BY_NAME) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - if (!zend_interrupt_function) { - ZEND_VM_CONTINUE(); - } - } else { - zend_flush_deferred_errors(); - entered = true; - } - } - if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - entered = true; + } else if (zend_interrupt_process(execute_data, opline, false)) { + zend_interrupt_handle_exception(); + if (!zend_atomic_bool_load_ex(&EG(vm_interrupt))) { + ZEND_VM_ENTER(); } - if (entered) { - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); - - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } + ZEND_VM_CONTINUE(); +} - } - } +ZEND_VM_HELPER(zend_loop_interrupt_helper, ANY, ANY) +{ + zend_atomic_bool_store_ex(&EG(vm_interrupt), false); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + LOAD_OPLINE(); +#else + SAVE_OPLINE(); +#endif + if (zend_atomic_bool_load_ex(&EG(timed_out))) { + zend_timeout(); + } else if (zend_interrupt_process(execute_data, opline, true)) { + zend_interrupt_handle_exception(); + if (!zend_atomic_bool_load_ex(&EG(vm_interrupt))) { ZEND_VM_ENTER(); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3bad09e34261..e353a0ecddb6 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -433,7 +433,7 @@ static zend_vm_opcode_handler_func_t zend_vm_get_opcode_handler_func(uint8_t opc #if defined(ZEND_VM_FP_GLOBAL_REG) # define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE() # define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX() -# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE() +# define ZEND_VM_LEAVE() ZEND_VM_LOOP_INTERRUPT_CHECK(); ZEND_VM_CONTINUE() #elif defined(ZEND_VM_IP_GLOBAL_REG) # define ZEND_VM_ENTER_EX() return 1 # define ZEND_VM_ENTER() opline = EG(current_execute_data)->opline; ZEND_VM_ENTER_EX() @@ -446,13 +446,14 @@ static zend_vm_opcode_handler_func_t zend_vm_get_opcode_handler_func(uint8_t opc #endif #define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); #ifdef ZEND_VM_FP_GLOBAL_REG -#define ZEND_VM_LOOP_INTERRUPT() zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); +#define ZEND_VM_LOOP_INTERRUPT() zend_loop_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); #else -#define ZEND_VM_LOOP_INTERRUPT() opline = (zend_op*)((uintptr_t)zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) & ~ZEND_VM_ENTER_BIT); +#define ZEND_VM_LOOP_INTERRUPT() opline = (zend_op*)((uintptr_t)zend_loop_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) & ~ZEND_VM_ENTER_BIT); #endif #define ZEND_VM_DISPATCH(opcode, opline) return zend_vm_get_opcode_handler_func(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS); +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_loop_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS); static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS); static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV_EX zend_add_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2) @@ -1163,6 +1164,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -1173,6 +1175,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; EG(vm_stack_top) = (zval*)execute_data; execute_data = EX(prev_execute_data); @@ -1185,6 +1188,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -1207,6 +1211,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; old_execute_data = execute_data; execute_data = EX(prev_execute_data); @@ -1224,8 +1229,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_detach_symbol_table(execute_data); call_info |= ZEND_CALL_NEEDS_REATTACH; } + EG(frame_teardown)++; zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); + EG(frame_teardown)--; efree_size(EX(func), sizeof(zend_op_array)); old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); @@ -1248,6 +1255,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD call_info = EX_CALL_INFO(); @@ -1264,6 +1272,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; ZEND_VM_RETURN(); } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { zend_array *symbol_table = EX(symbol_table); @@ -1322,6 +1331,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ret = 0 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !0 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto icall_skip; + } + } + fbc->internal_function.handler(call, ret); @@ -1345,6 +1363,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_VM_FCALL_INTERRUPT_CHECK(call); +icall_skip: EG(current_execute_data) = execute_data; zend_vm_stack_free_args(call); @@ -1392,6 +1411,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ret = 1 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !1 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto icall_skip; + } + } + fbc->internal_function.handler(call, ret); @@ -1415,6 +1443,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_VM_FCALL_INTERRUPT_CHECK(call); +icall_skip: EG(current_execute_data) = execute_data; zend_vm_stack_free_args(call); @@ -1462,6 +1491,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto icall_skip; + } + } + zend_observer_fcall_begin_specialized(call, false); fbc->internal_function.handler(call, ret); @@ -1484,6 +1522,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ zend_observer_fcall_end(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +icall_skip: EG(current_execute_data) = execute_data; zend_vm_stack_free_args(call); @@ -1648,6 +1687,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ret = 0 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !0 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_by_name_skip; + } + } + fbc->internal_function.handler(call, ret); @@ -1672,6 +1720,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_by_name_skip: EG(current_execute_data) = execute_data; goto fcall_by_name_end; @@ -1766,6 +1815,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ret = 1 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !1 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_by_name_skip; + } + } + fbc->internal_function.handler(call, ret); @@ -1790,6 +1848,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_by_name_skip: EG(current_execute_data) = execute_data; goto fcall_by_name_end; @@ -1883,6 +1942,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_by_name_skip; + } + } + zend_observer_fcall_begin_specialized(call, false); fbc->internal_function.handler(call, ret); @@ -1906,6 +1974,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ zend_observer_fcall_end(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_by_name_skip: EG(current_execute_data) = execute_data; goto fcall_by_name_end; @@ -2014,6 +2083,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ret = 0 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !0 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_skip; + } + } + if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ @@ -2043,6 +2121,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_skip: EG(current_execute_data) = execute_data; goto fcall_end; @@ -2149,6 +2228,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ret = 1 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !1 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_skip; + } + } + if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ @@ -2178,6 +2266,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_D ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_skip: EG(current_execute_data) = execute_data; goto fcall_end; @@ -2281,6 +2370,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_skip; + } + } + zend_observer_fcall_begin_specialized(call, false); if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ @@ -2309,6 +2407,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ zend_observer_fcall_end(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_skip: EG(current_execute_data) = execute_data; goto fcall_end; @@ -3359,7 +3458,9 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV_ && (EX(func)->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)].op2_type & (IS_TMP_VAR | IS_VAR))) { zval *return_value = EX_VAR(EX(func)->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)].op2.var); + EG(frame_teardown)++; zval_ptr_dtor(return_value); + EG(frame_teardown)--; } /* Chain potential exception from wrapping finally block */ @@ -3367,7 +3468,9 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV_ if (ex) { if (zend_is_unwind_exit(ex) || zend_is_graceful_exit(ex)) { /* discard the previously thrown exception */ + EG(frame_teardown)++; OBJ_RELEASE(Z_OBJ_P(fast_call)); + EG(frame_teardown)--; } else { zend_exception_set_previous(ex, Z_OBJ_P(fast_call)); } @@ -3403,15 +3506,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_HANDLE_EXCEPT { const zend_op *throw_op = EG(opline_before_exception); - if (EG(deferred_errors).size) { + if (EG(deferred_errors).size || EG(deferred_dtors).count || EG(deferred_dtors).values_count) { zend_object *orig_exception = EG(exception); EX(opline) = EG(opline_before_exception); EG(exception) = NULL; + zend_flush_deferred_dtors(); zend_flush_deferred_errors(); if (EG(exception)) { - zend_exception_set_previous(EG(exception), orig_exception); + if (UNEXPECTED(zend_is_unwind_exit(orig_exception) || zend_is_graceful_exit(orig_exception))) { + OBJ_RELEASE(EG(exception)); + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } else { + zend_exception_set_previous(EG(exception), orig_exception); + } } else { EG(exception) = orig_exception; EX(opline) = EG(exception_op); @@ -3566,7 +3676,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FAST_RET_SPEC if (Z_OPLINE_NUM_P(fast_call) != (uint32_t)-1) { const zend_op *fast_ret = EX(func)->op_array.opcodes + Z_OPLINE_NUM_P(fast_call); - ZEND_VM_JMP_EX(fast_ret + 1, 0); + ZEND_VM_SET_OPCODE_NO_INTERRUPT(fast_ret + 1); + ZEND_VM_INTERRUPT_CHECK(); + ZEND_VM_CONTINUE(); } /* special case for unhandled exceptions */ @@ -3577,6 +3689,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FAST_RET_SPEC ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX current_try_catch_offset, current_op_num)); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FLUSH_DEFERRED_DTORS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + SAVE_OPLINE(); + if ((opline + 1)->opcode == ZEND_THROW) { + zend_flush_deferred_dtors_surface(); + } else { + zend_flush_deferred_dtors(); + } + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + HANDLE_EXCEPTION(); + } + } + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -4059,42 +4190,28 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else { - bool entered = false; - if (EG(deferred_errors).size) { - if (EX(call) - || opline->opcode == ZEND_DO_FCALL - || opline->opcode == ZEND_DO_ICALL - || opline->opcode == ZEND_DO_UCALL - || opline->opcode == ZEND_DO_FCALL_BY_NAME) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - if (!zend_interrupt_function) { - ZEND_VM_CONTINUE(); - } - } else { - zend_flush_deferred_errors(); - entered = true; - } - } - if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - entered = true; + } else if (zend_interrupt_process(execute_data, opline, false)) { + zend_interrupt_handle_exception(); + if (!zend_atomic_bool_load_ex(&EG(vm_interrupt))) { + ZEND_VM_ENTER(); } - if (entered) { - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); - - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } + ZEND_VM_CONTINUE(); +} - } - } +static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_loop_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) +{ + zend_atomic_bool_store_ex(&EG(vm_interrupt), false); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + LOAD_OPLINE(); +#else + SAVE_OPLINE(); +#endif + if (zend_atomic_bool_load_ex(&EG(timed_out))) { + zend_timeout(); + } else if (zend_interrupt_process(execute_data, opline, true)) { + zend_interrupt_handle_exception(); + if (!zend_atomic_bool_load_ex(&EG(vm_interrupt))) { ZEND_VM_ENTER(); } } @@ -5082,6 +5199,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_GENERATOR_RET zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + HANDLE_EXCEPTION(); + } + } retval = RT_CONSTANT(opline, opline->op1); /* Copy return value into generator->retval */ @@ -5128,6 +5253,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_GENERATOR_RET zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + FREE_OP(opline->op1_type, opline->op1.var); + HANDLE_EXCEPTION(); + } + } retval = get_zval_ptr(opline->op1_type, opline->op1, BP_VAR_R); /* Copy return value into generator->retval */ @@ -5999,6 +6131,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_FROM_SP HANDLE_EXCEPTION(); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + yield_from_try_again: if (Z_TYPE_P(val) == IS_ARRAY) { ZVAL_COPY_VALUE(&generator->values, val); @@ -8411,6 +8553,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CO ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -10822,6 +10976,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CO ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -11707,6 +11872,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CO ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -13445,6 +13622,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CO ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -15653,6 +15842,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FREE_SPEC_TMP SAVE_OPLINE(); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -15668,6 +15864,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_F zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -15676,6 +15879,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_F if (Z_REFCOUNTED_P(var) && !Z_DELREF_P(var)) { SAVE_OPLINE(); rc_dtor_func(Z_COUNTED_P(var)); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ZEND_VM_NEXT_OPCODE(); @@ -17309,6 +17519,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_GENERATOR_RET zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + HANDLE_EXCEPTION(); + } + } retval = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); /* Copy return value into generator->retval */ @@ -18089,6 +18306,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_FROM_SP HANDLE_EXCEPTION(); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + yield_from_try_again: if (Z_TYPE_P(val) == IS_ARRAY) { ZVAL_COPY_VALUE(&generator->values, val); @@ -19742,6 +19968,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_TM ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -21229,6 +21466,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_TM ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -21923,6 +22170,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_TM ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -23097,6 +23355,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_TM ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -23553,6 +23822,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_GENERATOR_RET zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + HANDLE_EXCEPTION(); + } + } retval = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); /* Copy return value into generator->retval */ @@ -26420,6 +26696,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_VA ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -28678,6 +28965,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_VA ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -30318,6 +30615,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_VA ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -32558,6 +32866,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_VA ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -34801,6 +35120,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_UN ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -36691,6 +37022,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_UN ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -37240,6 +37582,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_UN ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -39280,6 +39634,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_UN ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -39985,6 +40351,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_GENERATOR_RET zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + HANDLE_EXCEPTION(); + } + } retval = _get_zval_ptr_cv_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); /* Copy return value into generator->retval */ @@ -40686,6 +41060,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_FROM_SP HANDLE_EXCEPTION(); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + yield_from_try_again: if (Z_TYPE_P(val) == IS_ARRAY) { ZVAL_COPY_VALUE(&generator->values, val); @@ -44616,6 +45000,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CV ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -48228,6 +48624,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CV ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -49817,6 +50224,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CV ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -53405,6 +53824,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_CV ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -53591,7 +54022,7 @@ static ZEND_COLD zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_F # define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE() # define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) # define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE() -# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE() +# define ZEND_VM_LEAVE() ZEND_VM_LOOP_INTERRUPT_CHECK(); ZEND_VM_CONTINUE() static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS); static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS); @@ -54008,6 +54439,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -54018,6 +54450,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; EG(vm_stack_top) = (zval*)execute_data; execute_data = EX(prev_execute_data); @@ -54030,6 +54463,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -54052,6 +54486,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; old_execute_data = execute_data; execute_data = EX(prev_execute_data); @@ -54069,8 +54504,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend zend_detach_symbol_table(execute_data); call_info |= ZEND_CALL_NEEDS_REATTACH; } + EG(frame_teardown)++; zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); + EG(frame_teardown)--; efree_size(EX(func), sizeof(zend_op_array)); old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); @@ -54093,6 +54530,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD call_info = EX_CALL_INFO(); @@ -54109,6 +54547,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; ZEND_VM_RETURN(); } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { zend_array *symbol_table = EX(symbol_table); @@ -54167,6 +54606,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_ICA ret = 0 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !0 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto icall_skip; + } + } + fbc->internal_function.handler(call, ret); @@ -54190,6 +54638,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_ICA ZEND_VM_FCALL_INTERRUPT_CHECK(call); +icall_skip: EG(current_execute_data) = execute_data; zend_vm_stack_free_args(call); @@ -54237,6 +54686,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_ICA ret = 1 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !1 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto icall_skip; + } + } + fbc->internal_function.handler(call, ret); @@ -54260,6 +54718,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_ICA ZEND_VM_FCALL_INTERRUPT_CHECK(call); +icall_skip: EG(current_execute_data) = execute_data; zend_vm_stack_free_args(call); @@ -54307,6 +54766,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_IC ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto icall_skip; + } + } + zend_observer_fcall_begin_specialized(call, false); fbc->internal_function.handler(call, ret); @@ -54329,6 +54797,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_IC zend_observer_fcall_end(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +icall_skip: EG(current_execute_data) = execute_data; zend_vm_stack_free_args(call); @@ -54493,6 +54962,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ret = 0 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !0 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_by_name_skip; + } + } + fbc->internal_function.handler(call, ret); @@ -54517,6 +54995,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_by_name_skip: EG(current_execute_data) = execute_data; goto fcall_by_name_end; @@ -54611,6 +55090,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ret = 1 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !1 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_by_name_skip; + } + } + fbc->internal_function.handler(call, ret); @@ -54635,6 +55123,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_by_name_skip: EG(current_execute_data) = execute_data; goto fcall_by_name_end; @@ -54728,6 +55217,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FC ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_by_name_skip; + } + } + zend_observer_fcall_begin_specialized(call, false); fbc->internal_function.handler(call, ret); @@ -54751,6 +55249,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FC zend_observer_fcall_end(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_by_name_skip: EG(current_execute_data) = execute_data; goto fcall_by_name_end; @@ -54859,6 +55358,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ret = 0 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !0 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_skip; + } + } + if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ @@ -54888,6 +55396,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_skip: EG(current_execute_data) = execute_data; goto fcall_end; @@ -54994,6 +55503,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ret = 1 ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !1 + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_skip; + } + } + if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ @@ -55023,6 +55541,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FCA ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_skip: EG(current_execute_data) = execute_data; goto fcall_end; @@ -55126,6 +55645,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FC ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; ZVAL_NULL(ret); + if (UNEXPECTED((EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !RETURN_VALUE_USED(opline) + && !EX(call))) { + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + goto fcall_skip; + } + } + zend_observer_fcall_begin_specialized(call, false); if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ @@ -55154,6 +55682,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DO_FC zend_observer_fcall_end(call, EG(exception) ? NULL : ret); ZEND_VM_FCALL_INTERRUPT_CHECK(call); +fcall_skip: EG(current_execute_data) = execute_data; goto fcall_end; @@ -56132,15 +56661,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HANDLE_EXCEPTION_S { const zend_op *throw_op = EG(opline_before_exception); - if (EG(deferred_errors).size) { + if (EG(deferred_errors).size || EG(deferred_dtors).count || EG(deferred_dtors).values_count) { zend_object *orig_exception = EG(exception); EX(opline) = EG(opline_before_exception); EG(exception) = NULL; + zend_flush_deferred_dtors(); zend_flush_deferred_errors(); if (EG(exception)) { - zend_exception_set_previous(EG(exception), orig_exception); + if (UNEXPECTED(zend_is_unwind_exit(orig_exception) || zend_is_graceful_exit(orig_exception))) { + OBJ_RELEASE(EG(exception)); + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } else { + zend_exception_set_previous(EG(exception), orig_exception); + } } else { EG(exception) = orig_exception; EX(opline) = EG(exception_op); @@ -56295,7 +56831,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FAST_RET_SPEC_TAIL if (Z_OPLINE_NUM_P(fast_call) != (uint32_t)-1) { const zend_op *fast_ret = EX(func)->op_array.opcodes + Z_OPLINE_NUM_P(fast_call); - ZEND_VM_JMP_EX(fast_ret + 1, 0); + ZEND_VM_SET_OPCODE_NO_INTERRUPT(fast_ret + 1); + ZEND_VM_INTERRUPT_CHECK(); + ZEND_VM_CONTINUE(); } /* special case for unhandled exceptions */ @@ -56306,6 +56844,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FAST_RET_SPEC_TAIL ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX current_try_catch_offset, current_op_num)); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FLUSH_DEFERRED_DTORS_SPEC_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + SAVE_OPLINE(); + if ((opline + 1)->opcode == ZEND_THROW) { + zend_flush_deferred_dtors_surface(); + } else { + zend_flush_deferred_dtors(); + } + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + HANDLE_EXCEPTION(); + } + } + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSERT_CHECK_SPEC_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -56788,47 +57345,15 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else { - bool entered = false; - if (EG(deferred_errors).size) { - if (EX(call) - || opline->opcode == ZEND_DO_FCALL - || opline->opcode == ZEND_DO_ICALL - || opline->opcode == ZEND_DO_UCALL - || opline->opcode == ZEND_DO_FCALL_BY_NAME) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - if (!zend_interrupt_function) { - ZEND_VM_CONTINUE(); - } - } else { - zend_flush_deferred_errors(); - entered = true; - } - } - if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - entered = true; - } - if (entered) { - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); - - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); - - } - } + } else if (zend_interrupt_process(execute_data, opline, false)) { + zend_interrupt_handle_exception(); + if (!zend_atomic_bool_load_ex(&EG(vm_interrupt))) { ZEND_VM_ENTER(); } } ZEND_VM_CONTINUE(); } + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -57811,6 +58336,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_GENERATOR_RETURN_S zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + HANDLE_EXCEPTION(); + } + } retval = RT_CONSTANT(opline, opline->op1); /* Copy return value into generator->retval */ @@ -57857,6 +58390,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_GENERATOR_RETURN_S zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + FREE_OP(opline->op1_type, opline->op1.var); + HANDLE_EXCEPTION(); + } + } retval = get_zval_ptr(opline->op1_type, opline->op1, BP_VAR_R); /* Copy return value into generator->retval */ @@ -58728,6 +59268,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_FROM_SPEC_CO HANDLE_EXCEPTION(); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + yield_from_try_again: if (Z_TYPE_P(val) == IS_ARRAY) { ZVAL_COPY_VALUE(&generator->values, val); @@ -61140,6 +61690,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CONST_C ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -63551,6 +64113,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CONST_T ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -64334,6 +64907,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CONST_U ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -66072,6 +66657,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CONST_C ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -68280,6 +68877,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FREE_SPEC_TMPVAR_T SAVE_OPLINE(); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -68295,6 +68899,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FE_FRE zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -68303,6 +68914,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FE_FRE if (Z_REFCOUNTED_P(var) && !Z_DELREF_P(var)) { SAVE_OPLINE(); rc_dtor_func(Z_COUNTED_P(var)); + if (UNEXPECTED((opline->extended_value & ZEND_FREE_ON_RETURN) + && (EG(deferred_dtors).count || EG(deferred_dtors).values_count))) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } + } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ZEND_VM_NEXT_OPCODE(); @@ -69936,6 +70554,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_GENERATOR_RETURN_S zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + HANDLE_EXCEPTION(); + } + } retval = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); /* Copy return value into generator->retval */ @@ -70716,6 +71341,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_FROM_SPEC_TM HANDLE_EXCEPTION(); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + yield_from_try_again: if (Z_TYPE_P(val) == IS_ARRAY) { ZVAL_COPY_VALUE(&generator->values, val); @@ -72369,6 +73003,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_TMP_CON ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -73856,6 +74501,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_TMP_TMP ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -74450,6 +75105,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_TMP_UNU ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -75624,6 +76290,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_TMP_CV_ ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -76080,6 +76757,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_GENERATOR_RETURN_S zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + HANDLE_EXCEPTION(); + } + } retval = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); /* Copy return value into generator->retval */ @@ -78947,6 +79631,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_VAR_CON ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -81205,6 +81900,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_VAR_TMP ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -82845,6 +83550,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_VAR_UNU ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -85085,6 +85801,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_VAR_CV_ ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -87328,6 +88055,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_UNUSED_ ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -89218,6 +89957,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_UNUSED_ ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -89767,6 +90517,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_UNUSED_ ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -91807,6 +92569,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_UNUSED_ ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -92512,6 +93286,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_GENERATOR_RETURN_S zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C); SAVE_OPLINE(); + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + HANDLE_EXCEPTION(); + } + } retval = _get_zval_ptr_cv_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); /* Copy return value into generator->retval */ @@ -93213,6 +93995,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_FROM_SPEC_CV HANDLE_EXCEPTION(); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + yield_from_try_again: if (Z_TYPE_P(val) == IS_ARRAY) { ZVAL_COPY_VALUE(&generator->values, val); @@ -97143,6 +97935,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CV_CONS ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -100755,6 +101559,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CV_TMP_ ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -102242,6 +103057,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CV_UNUS ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -105830,6 +106657,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_CV_CV_T ZEND_VM_TAIL_CALL(zend_yield_in_closed_generator_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + + + + + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + /* Destroy the previously yielded value */ zval_ptr_dtor(&generator->value); @@ -106399,7 +107238,9 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV_EX z && (EX(func)->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)].op2_type & (IS_TMP_VAR | IS_VAR))) { zval *return_value = EX_VAR(EX(func)->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)].op2.var); + EG(frame_teardown)++; zval_ptr_dtor(return_value); + EG(frame_teardown)--; } /* Chain potential exception from wrapping finally block */ @@ -106407,7 +107248,9 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV_EX z if (ex) { if (zend_is_unwind_exit(ex) || zend_is_graceful_exit(ex)) { /* discard the previously thrown exception */ + EG(frame_teardown)++; OBJ_RELEASE(Z_OBJ_P(fast_call)); + EG(frame_teardown)--; } else { zend_exception_set_previous(ex, Z_OBJ_P(fast_call)); } @@ -109353,6 +110196,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL, (void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL, (void*)&&ZEND_TYPE_ASSERT_SPEC_CONST_LABEL, + (void*)&&ZEND_FLUSH_DEFERRED_DTORS_SPEC_LABEL, (void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL, (void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -110390,6 +111234,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -110400,6 +111245,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; EG(vm_stack_top) = (zval*)execute_data; execute_data = EX(prev_execute_data); @@ -110412,6 +111258,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD @@ -110434,6 +111281,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; old_execute_data = execute_data; execute_data = EX(prev_execute_data); @@ -110451,8 +111299,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) zend_detach_symbol_table(execute_data); call_info |= ZEND_CALL_NEEDS_REATTACH; } + EG(frame_teardown)++; zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); + EG(frame_teardown)--; efree_size(EX(func), sizeof(zend_op_array)); old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); @@ -110475,6 +111325,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { EG(current_execute_data) = EX(prev_execute_data); + EG(frame_teardown)++; i_free_compiled_variables(execute_data); #ifdef ZEND_PREFER_RELOAD call_info = EX_CALL_INFO(); @@ -110491,6 +111342,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } + EG(frame_teardown)--; ZEND_VM_RETURN(); } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { zend_array *symbol_table = EX(symbol_table); @@ -110691,6 +111543,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_FAST_RET_SPEC) HYBRID_BREAK(); + HYBRID_CASE(ZEND_FLUSH_DEFERRED_DTORS_SPEC): + VM_TRACE(ZEND_FLUSH_DEFERRED_DTORS_SPEC) + ZEND_FLUSH_DEFERRED_DTORS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_FLUSH_DEFERRED_DTORS_SPEC) + HYBRID_BREAK(); HYBRID_CASE(ZEND_ASSERT_CHECK_SPEC): VM_TRACE(ZEND_ASSERT_CHECK_SPEC) ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -118295,6 +119152,7 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_HANDLER, + ZEND_FLUSH_DEFERRED_DTORS_SPEC_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER, ZEND_RECV_NOTYPE_SPEC_HANDLER, ZEND_NULL_HANDLER, @@ -121773,6 +122631,7 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_TAILCALL_HANDLER, + ZEND_FLUSH_DEFERRED_DTORS_SPEC_TAILCALL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_TAILCALL_HANDLER, ZEND_RECV_NOTYPE_SPEC_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, @@ -122741,7 +123600,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3474, + 3475, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -122775,7 +123634,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3474, + 3475, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -122908,50 +123767,50 @@ void zend_vm_init(void) 2556, 2557, 2558, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, + 2559, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, + 3475, }; #if 0 #elif (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) @@ -123144,7 +124003,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2567 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2568 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123152,7 +124011,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2592 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2593 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123160,7 +124019,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2617 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2618 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123171,17 +124030,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2642 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2643 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2667 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2668 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2692 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2693 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -123192,17 +124051,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2717 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2718 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2742 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2743 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2767 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2768 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_IDENTICAL: @@ -123213,16 +124072,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2792 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2793 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2867 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2868 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3092 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3093 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3098 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3099 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -123233,16 +124092,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2942 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2943 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3017 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3018 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3095 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3096 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3103 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3104 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -123253,12 +124112,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2792 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2793 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2867 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2868 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_EQUAL: @@ -123269,12 +124128,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2942 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2943 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3017 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3018 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_SMALLER: @@ -123282,12 +124141,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3108 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3109 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3183 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3184 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -123295,79 +124154,79 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3258 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3259 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3333 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3334 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if (op1_info == MAY_BE_LONG) { - spec = 3420 | SPEC_RULE_OP1; + spec = 3421 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_DOUBLE) { - spec = 3425 | SPEC_RULE_OP1; + spec = 3426 | SPEC_RULE_OP1; } else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) { - spec = 3430 | SPEC_RULE_OP1; + spec = 3431 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3408 | SPEC_RULE_RETVAL; + spec = 3409 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3410 | SPEC_RULE_RETVAL; + spec = 3411 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3412 | SPEC_RULE_RETVAL; + spec = 3413 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3414 | SPEC_RULE_RETVAL; + spec = 3415 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3416; - } else if (op1_info == MAY_BE_LONG) { spec = 3417; + } else if (op1_info == MAY_BE_LONG) { + spec = 3418; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3418; - } else if (op1_info == MAY_BE_LONG) { spec = 3419; + } else if (op1_info == MAY_BE_LONG) { + spec = 3420; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2566; + spec = 2567; } break; case ZEND_INIT_FCALL: if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) { - spec = 2559; + spec = 2560; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2560; + spec = 2561; } break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3470; + spec = 3471; } break; case ZEND_SEND_VAR_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3465 | SPEC_RULE_OP1; + spec = 3466 | SPEC_RULE_OP1; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3472 | SPEC_RULE_RETVAL; + spec = 3473 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -123375,22 +124234,22 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3435 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3436 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3471; + spec = 3472; } break; case ZEND_SEND_VAR: if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3460 | SPEC_RULE_OP1; + spec = 3461 | SPEC_RULE_OP1; } break; case ZEND_COUNT: if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 2561 | SPEC_RULE_OP1; + spec = 2562 | SPEC_RULE_OP1; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index 8e65bc0a28ea..288e8b008ff9 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -1145,6 +1145,13 @@ function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno, return; } + /* zend_loop_interrupt_helper is only ever reached through the + * ZEND_VM_LOOP_INTERRUPT macro, which calls its non-tailcall function + * variant. A TAILCALL variant would be an unused static function. */ + if ($kind == ZEND_VM_KIND_TAILCALL && $name === 'zend_loop_interrupt_helper') { + return; + } + if ($spec && skip_extra_spec_function($op1, $op2, $extra_spec)) { return; } @@ -2029,7 +2036,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG)\n"); out($f,"# define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"# define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX()\n"); - out($f,"# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n"); + out($f,"# define ZEND_VM_LEAVE() ZEND_VM_LOOP_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"#elif defined(ZEND_VM_IP_GLOBAL_REG)\n"); out($f,"# define ZEND_VM_ENTER_EX() return 1\n"); out($f,"# define ZEND_VM_ENTER() opline = EG(current_execute_data)->opline; ZEND_VM_ENTER_EX()\n"); @@ -2042,9 +2049,9 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"#endif\n"); out($f,"#define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));\n"); out($f,"#ifdef ZEND_VM_FP_GLOBAL_REG\n"); - out($f,"#define ZEND_VM_LOOP_INTERRUPT() zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); + out($f,"#define ZEND_VM_LOOP_INTERRUPT() zend_loop_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); out($f,"#else\n"); - out($f,"#define ZEND_VM_LOOP_INTERRUPT() opline = (zend_op*)((uintptr_t)zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) & ~ZEND_VM_ENTER_BIT);\n"); + out($f,"#define ZEND_VM_LOOP_INTERRUPT() opline = (zend_op*)((uintptr_t)zend_loop_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) & ~ZEND_VM_ENTER_BIT);\n"); out($f,"#endif\n"); if ($kind == ZEND_VM_KIND_HYBRID) { out($f,"#define ZEND_VM_DISPATCH(opcode, opline) return zend_vm_get_opcode_handler_func(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); @@ -2053,6 +2060,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) } out($f,"\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS);\n"); + out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_loop_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"\n"); break; @@ -2076,9 +2084,9 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"#define ZEND_VM_RETURN() return\n"); out($f,"#define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"#define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX()\n"); - out($f,"#define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n"); + out($f,"#define ZEND_VM_LEAVE() ZEND_VM_LOOP_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"#define ZEND_VM_INTERRUPT() goto zend_interrupt_helper".($spec?"_SPEC":"").";\n"); - out($f,"#define ZEND_VM_LOOP_INTERRUPT() goto zend_interrupt_helper".($spec?"_SPEC":"").";\n"); + out($f,"#define ZEND_VM_LOOP_INTERRUPT() goto zend_loop_interrupt_helper".($spec?"_SPEC":"").";\n"); out($f,"#define ZEND_VM_DISPATCH(opcode, opline) dispatch_handler = zend_vm_get_opcode_handler(opcode, opline); goto zend_vm_dispatch;\n"); out($f,"\n"); break; @@ -2107,9 +2115,9 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"#define ZEND_VM_RETURN() return\n"); out($f,"#define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"#define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX()\n"); - out($f,"#define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n"); + out($f,"#define ZEND_VM_LEAVE() ZEND_VM_LOOP_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"#define ZEND_VM_INTERRUPT() goto zend_interrupt_helper".($spec?"_SPEC":"").";\n"); - out($f,"#define ZEND_VM_LOOP_INTERRUPT() goto zend_interrupt_helper".($spec?"_SPEC":"").";\n"); + out($f,"#define ZEND_VM_LOOP_INTERRUPT() goto zend_loop_interrupt_helper".($spec?"_SPEC":"").";\n"); out($f,"#define ZEND_VM_DISPATCH(opcode, opline) goto *(void**)(zend_vm_get_opcode_handler(opcode, opline));\n"); out($f,"\n"); break; @@ -2158,7 +2166,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"# define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE()\n"); out($f,"# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n"); out($f,"# define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); - out($f,"# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n"); + out($f,"# define ZEND_VM_LEAVE() ZEND_VM_LOOP_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS);\n"); diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 6f1595195450..ede0b8cc7e87 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1087,507 +1087,508 @@ _(2556, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \ _(2557, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \ _(2558, ZEND_TYPE_ASSERT_SPEC_CONST) \ - _(2559, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ - _(2560, ZEND_RECV_NOTYPE_SPEC) \ - _(2562, ZEND_COUNT_ARRAY_SPEC_TMP_UNUSED) \ - _(2565, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ - _(2566, ZEND_JMP_FORWARD_SPEC) \ - _(2572, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2573, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2559, ZEND_FLUSH_DEFERRED_DTORS_SPEC) \ + _(2560, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ + _(2561, ZEND_RECV_NOTYPE_SPEC) \ + _(2563, ZEND_COUNT_ARRAY_SPEC_TMP_UNUSED) \ + _(2566, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ + _(2567, ZEND_JMP_FORWARD_SPEC) \ + _(2573, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2574, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2576, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2577, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2578, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2575, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2577, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2578, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2579, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2581, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2587, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2588, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2580, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2582, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2588, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2589, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2591, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2597, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2598, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2590, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2592, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2598, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2599, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2601, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2602, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2603, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2600, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2602, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2603, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2604, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2606, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2612, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2613, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2605, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2607, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2613, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2614, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2616, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2622, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2623, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2615, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2617, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2623, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2624, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2626, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2627, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2628, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2625, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2627, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2628, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2629, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2631, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2637, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2638, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2630, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2632, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2638, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2639, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2641, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2643, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2640, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2642, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2644, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2646, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2647, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2648, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2645, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2647, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2648, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2649, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2651, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2652, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2653, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2650, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2652, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2653, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2654, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2656, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2662, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2663, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2655, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2657, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2663, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2664, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2668, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2665, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2667, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2669, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2671, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2672, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2673, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2670, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2672, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2673, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2674, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2676, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2677, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2678, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2675, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2677, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2678, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2679, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2681, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2687, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2688, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2680, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2682, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2688, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2689, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2691, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2693, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2690, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2692, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2694, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2696, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2697, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2698, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2695, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2697, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2698, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2699, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2701, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2702, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2703, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2700, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2702, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2703, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2704, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2706, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2712, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2713, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2705, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2707, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2713, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2714, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2716, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2722, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2723, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2715, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2717, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2723, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2724, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2726, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2727, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2728, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2725, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2727, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2728, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2729, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2731, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2737, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2738, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2730, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2732, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2738, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2739, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2741, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2747, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2748, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2740, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2742, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2748, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2749, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2751, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2752, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2753, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2750, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2752, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2753, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2754, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2756, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2762, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2763, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2755, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2757, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2763, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2764, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2766, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2772, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2773, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2765, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2767, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2773, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2774, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2776, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2777, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2778, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2775, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2777, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2778, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2779, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2781, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2787, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2788, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2780, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2782, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2788, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2789, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2791, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2807, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2813, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2814, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2815, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2820, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2821, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2822, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2823, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2824, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2852, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2855, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2856, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2857, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2858, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2859, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2860, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2866, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2882, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2888, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2889, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2890, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2895, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2896, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2897, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2898, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2899, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2927, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2930, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2931, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2932, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2933, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2934, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2935, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2941, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2957, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2963, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2964, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2965, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2970, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2971, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2972, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2973, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2974, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3002, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3005, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3006, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3007, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3008, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3009, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3010, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3016, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3032, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3038, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3039, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3040, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3045, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3046, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3047, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3048, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3049, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3077, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3080, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3081, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3082, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3083, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3084, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3085, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3091, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3092, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3093, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3094, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3095, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3096, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3097, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3098, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3102, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3103, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3107, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3111, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3112, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3113, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3114, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3115, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3116, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3120, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3121, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3122, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3123, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3124, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3125, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3126, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3129, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3130, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3131, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3135, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3168, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3169, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3170, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3171, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3172, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3173, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3174, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3175, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3176, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3186, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3243, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3244, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3245, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3246, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3247, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3248, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3249, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3250, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3251, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3261, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3318, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3319, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3320, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3321, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3322, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3323, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3324, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3325, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3326, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3336, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3393, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3394, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3395, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3396, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3397, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3398, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3399, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3400, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3401, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3408, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3409, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3410, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3411, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ - _(3412, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3413, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3414, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3415, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ - _(3416, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3417, ZEND_POST_INC_LONG_SPEC_CV) \ - _(3418, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3419, ZEND_POST_DEC_LONG_SPEC_CV) \ - _(3420, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ - _(3421, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(2790, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2792, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2813, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2814, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2815, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2816, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2820, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2821, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2822, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2823, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2824, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2855, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2856, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2857, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2858, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2859, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2860, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2861, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2866, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2867, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2888, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2889, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2890, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2891, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2895, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2896, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2897, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2898, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2899, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2930, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2931, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2932, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2933, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2934, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2935, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2936, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2941, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2942, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2963, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2964, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2965, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2966, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2970, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2971, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2972, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2973, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2974, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3005, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3006, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3007, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3008, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3009, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3010, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3011, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3016, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3017, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3038, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3039, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3040, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3041, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3045, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3046, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3047, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3048, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3049, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3080, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3081, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3082, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3083, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3084, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3085, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3086, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3091, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3092, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3093, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3094, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3095, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3096, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3097, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3098, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3099, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3103, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3104, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3108, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3112, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3113, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3114, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3115, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3116, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3117, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3121, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3122, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3123, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3124, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3125, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3126, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3129, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3130, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3131, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3132, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3169, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3170, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3171, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3172, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3173, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3174, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3175, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3176, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3177, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3183, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3192, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3207, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3244, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3245, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3246, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3247, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3248, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3249, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3250, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3251, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3252, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3258, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3267, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3282, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3319, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3320, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3321, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3322, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3323, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3324, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3325, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3326, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3327, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3333, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3342, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3357, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3394, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3395, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3396, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3397, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3398, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3399, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3400, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3401, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3402, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3408, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3409, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3410, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3411, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3412, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ + _(3413, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3414, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3415, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3416, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ + _(3417, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3418, ZEND_POST_INC_LONG_SPEC_CV) \ + _(3419, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3420, ZEND_POST_DEC_LONG_SPEC_CV) \ + _(3421, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ _(3422, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3424, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3425, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ - _(3426, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3423, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3425, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3426, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ _(3427, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3429, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3430, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ - _(3431, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3428, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3430, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3431, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ _(3432, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3434, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3436, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3433, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3435, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ _(3437, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3439, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3440, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3441, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3438, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3440, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3441, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ _(3442, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3444, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3445, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3446, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3443, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3445, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3446, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ _(3447, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3449, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ - _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3448, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3450, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ _(3457, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3462, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ - _(3464, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3467, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3469, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3470, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3471, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3472, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3473, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3473+1, ZEND_NULL) + _(3458, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3460, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3463, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ + _(3465, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ + _(3468, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3470, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3471, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3472, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3473, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3474, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3474+1, ZEND_NULL) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 0ece3e6f0c66..3a19bf20347b 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -static const char *zend_vm_opcodes_names[212] = { +static const char *zend_vm_opcodes_names[213] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -234,9 +234,10 @@ static const char *zend_vm_opcodes_names[212] = { "ZEND_INIT_PARENT_PROPERTY_HOOK_CALL", "ZEND_DECLARE_ATTRIBUTED_CONST", "ZEND_TYPE_ASSERT", + "ZEND_FLUSH_DEFERRED_DTORS", }; -static uint32_t zend_vm_opcodes_flags[212] = { +static uint32_t zend_vm_opcodes_flags[213] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -449,6 +450,7 @@ static uint32_t zend_vm_opcodes_flags[212] = { 0x01001103, 0x00000303, 0x01000003, + 0x00000000, }; ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(uint8_t opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 92b46e6628f3..cdb7502a087a 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -331,7 +331,8 @@ END_EXTERN_C() #define ZEND_INIT_PARENT_PROPERTY_HOOK_CALL 209 #define ZEND_DECLARE_ATTRIBUTED_CONST 210 #define ZEND_TYPE_ASSERT 211 +#define ZEND_FLUSH_DEFERRED_DTORS 212 -#define ZEND_VM_LAST_OPCODE 211 +#define ZEND_VM_LAST_OPCODE 212 #endif diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index ad0308f44e20..d14486f33879 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -94,6 +94,59 @@ static void zend_weakref_unref(zend_object *object, void *tagged_ptr) { } } +static void zend_weakref_detach_single( + void *ptr, uintptr_t tag, zend_object *object) +{ + if (tag == ZEND_WEAKREF_TAG_REF) { + zend_weakref *wr = ptr; + wr->referent = NULL; + } else { + HashTable *ht = ptr; + ZEND_ASSERT(tag == ZEND_WEAKREF_TAG_MAP || tag == ZEND_WEAKREF_TAG_BARE_HT); + if (ht->pDestructor == ZVAL_PTR_DTOR) { + zval *zv = zend_hash_index_find(ht, zend_object_to_weakref_key(object)); + if (zv) { + zend_dtor_buf *buf = &EG(deferred_dtors); + if (UNEXPECTED(buf->values_count == buf->values_capacity)) { + uint32_t capacity = buf->values_capacity ? buf->values_capacity + (buf->values_capacity >> 1) : 8; + buf->values = erealloc(buf->values, sizeof(zval) * capacity); + buf->values_capacity = capacity; + } + ZVAL_COPY_VALUE(&buf->values[buf->values_count++], zv); + ZVAL_UNDEF(zv); + } + } + zend_hash_index_del(ht, zend_object_to_weakref_key(object)); + } +} + +void zend_weakrefs_notify_detach(zend_object *object) +{ + const zend_ulong obj_key = zend_object_to_weakref_key(object); + void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_key); +#if ZEND_DEBUG + ZEND_ASSERT(tagged_ptr && "Tracking of the IS_OBJ_WEAKLY_REFERENCE flag should be precise"); +#endif + if (tagged_ptr) { + void *ptr = ZEND_WEAKREF_GET_PTR(tagged_ptr); + uintptr_t tag = ZEND_WEAKREF_GET_TAG(tagged_ptr); + if (tag == ZEND_WEAKREF_TAG_HT) { + HashTable *ht = ptr; + void *inner_ptr; + ZEND_HASH_MAP_FOREACH_PTR(ht, inner_ptr) { + zend_weakref_detach_single( + ZEND_WEAKREF_GET_PTR(inner_ptr), ZEND_WEAKREF_GET_TAG(inner_ptr), object); + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); + } else { + zend_weakref_detach_single(ptr, tag, object); + } + zend_hash_index_del(&EG(weakrefs), obj_key); + } + GC_DEL_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED); +} + static void zend_weakref_register(zend_object *object, void *payload) { GC_ADD_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED); diff --git a/Zend/zend_weakrefs.h b/Zend/zend_weakrefs.h index 41662c33a300..028491f4f3d6 100644 --- a/Zend/zend_weakrefs.h +++ b/Zend/zend_weakrefs.h @@ -27,6 +27,7 @@ void zend_weakrefs_init(void); void zend_weakrefs_shutdown(void); ZEND_API void zend_weakrefs_notify(zend_object *object); +void zend_weakrefs_notify_detach(zend_object *object); ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pData); ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key); diff --git a/ext/curl/tests/curl_multi_add_handle_lifecycle.phpt b/ext/curl/tests/curl_multi_add_handle_lifecycle.phpt index 3027f0c33152..5b5533885b88 100644 --- a/ext/curl/tests/curl_multi_add_handle_lifecycle.phpt +++ b/ext/curl/tests/curl_multi_add_handle_lifecycle.phpt @@ -44,6 +44,6 @@ echo "Removed", PHP_EOL; ?> --EXPECTF-- Removing +Removed MyClass::__destruct MyClass::__destruct -Removed diff --git a/ext/dom/tests/gh12616_3.phpt b/ext/dom/tests/gh12616_3.phpt index 158992075720..bf8c3d6b370d 100644 --- a/ext/dom/tests/gh12616_3.phpt +++ b/ext/dom/tests/gh12616_3.phpt @@ -45,7 +45,7 @@ foreach ($xpath->query("/container/child2/namespace::*") as $ns) { } ?> ---EXPECT-- +--EXPECTF-- @@ -81,7 +81,7 @@ object(DOMNameSpaceNode)#4 (10) { string(22) "(object value omitted)" } --- Namespaces of child1/foo (both nodes) --- -object(DOMNameSpaceNode)#5 (10) { +object(DOMNameSpaceNode)#8 (10) { ["nodeName"]=> string(9) "xmlns:xml" ["nodeValue"]=> @@ -103,7 +103,7 @@ object(DOMNameSpaceNode)#5 (10) { ["parentElement"]=> string(22) "(object value omitted)" } -object(DOMNameSpaceNode)#8 (10) { +object(DOMNameSpaceNode)#10 (10) { ["nodeName"]=> string(9) "xmlns:xml" ["nodeValue"]=> @@ -126,7 +126,7 @@ object(DOMNameSpaceNode)#8 (10) { string(22) "(object value omitted)" } --- Namespaces of child2 --- -object(DOMNameSpaceNode)#9 (10) { +object(DOMNameSpaceNode)#%d (10) { ["nodeName"]=> string(9) "xmlns:xml" ["nodeValue"]=> @@ -148,7 +148,7 @@ object(DOMNameSpaceNode)#9 (10) { ["parentElement"]=> string(22) "(object value omitted)" } -object(DOMNameSpaceNode)#5 (10) { +object(DOMNameSpaceNode)#%d (10) { ["nodeName"]=> string(7) "xmlns:x" ["nodeValue"]=> diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 2c47d2690f62..1ca815f81225 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2544,6 +2544,9 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op goto done; case ZEND_FREE: case ZEND_FE_FREE: + if (opline->extended_value & ZEND_FREE_ON_RETURN) { + break; + } if (!zend_jit_free(&ctx, opline, OP1_INFO(), zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; @@ -2896,6 +2899,11 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } + break; + case ZEND_FLUSH_DEFERRED_DTORS: + if (!zend_jit_flush_deferred_dtors(&ctx, opline)) { + goto jit_failure; + } break; default: if (!zend_jit_handler(&ctx, opline, diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 2b7d03ad11ea..d5b65a1ce7a4 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3437,11 +3437,50 @@ static void ZEND_FASTCALL zend_jit_exception_in_interrupt_handler_helper(void) } } +static void ZEND_FASTCALL zend_jit_flush_deferred_dtors_helper(zend_execute_data *execute_data) +{ + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } +} + +static void ZEND_FASTCALL zend_jit_flush_deferred_dtors_surface_helper(zend_execute_data *execute_data) +{ + zend_flush_deferred_dtors_surface(); + if (UNEXPECTED(EG(exception))) { + zend_rethrow_exception(execute_data); + } +} + static void ZEND_FASTCALL zend_jit_check_deferred_errors(zend_execute_data *execute_data) { - if (EG(deferred_errors).size && execute_data->call) { + bool pending_dtors = EG(deferred_dtors).count || EG(deferred_dtors).values_count; + + if ((EG(deferred_errors).size || pending_dtors) && execute_data->call) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } else { + if (pending_dtors) { + const zend_op *opline = EX(opline); + + if (opline >= EX(func)->op_array.opcodes + && opline <= EX(func)->op_array.opcodes + EX(func)->op_array.num_args) { + zend_flush_deferred_dtors(); + } else { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } + } + zend_flush_deferred_errors(); + } +} + +static void ZEND_FASTCALL zend_jit_check_deferred_loop(zend_execute_data *execute_data) +{ + if ((EG(deferred_errors).size || EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && execute_data->call) { zend_atomic_bool_store_ex(&EG(vm_interrupt), true); } else { + zend_flush_deferred_dtors(); zend_flush_deferred_errors(); } } diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index 7b5f93f70db7..4b05bd072688 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -335,6 +335,7 @@ typedef enum _zend_jit_trace_stop { #define ZEND_JIT_EXIT_METHOD_CALL (1<<9) /* exit because of polymorphic INIT_METHOD_CALL call */ #define ZEND_JIT_EXIT_INVALIDATE (1<<10) /* invalidate current trace */ #define ZEND_JIT_EXIT_CHECK_EXCEPTION (1<<11) +#define ZEND_JIT_EXIT_LOOP_INTERRUPT (1<<12) #define ZEND_JIT_EXIT_FIXED (1U<<31) /* the exit_info can't be changed by zend_jit_snapshot_handler() */ diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index e54182b4369b..5d4d23c50c3e 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -242,6 +242,7 @@ static size_t tsrm_tls_offset = -1; _(exception_handler_free_op2, IR_SKIP_PROLOGUE) \ _(exception_handler_free_op1_op2, IR_SKIP_PROLOGUE) \ _(interrupt_handler, IR_SKIP_PROLOGUE) \ + _(loop_interrupt_handler, IR_SKIP_PROLOGUE) \ _(leave_function_handler, IR_SKIP_PROLOGUE) \ _(negative_shift, IR_SKIP_PROLOGUE) \ _(mod_by_zero, IR_SKIP_PROLOGUE) \ @@ -649,6 +650,7 @@ static void jit_SNAPSHOT(zend_jit_ctx *jit, ir_ref addr) || ptr == zend_jit_stub_handlers[jit_stub_exception_handler_free_op1_op2] || ptr == zend_jit_stub_handlers[jit_stub_exception_handler_free_op2] || ptr == zend_jit_stub_handlers[jit_stub_interrupt_handler] + || ptr == zend_jit_stub_handlers[jit_stub_loop_interrupt_handler] || ptr == zend_jit_stub_handlers[jit_stub_leave_function_handler] || ptr == zend_jit_stub_handlers[jit_stub_negative_shift] || ptr == zend_jit_stub_handlers[jit_stub_mod_by_zero] @@ -1954,13 +1956,13 @@ static void zend_jit_check_loop_timeout(zend_jit_ctx *jit, const zend_op *opline if_timeout = ir_IF(ir_LOAD_U8(jit_EG(timed_out))); ir_IF_TRUE(if_timeout); jit_LOAD_IP_ADDR(jit, opline); - ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_loop_interrupt_handler)); ir_IF_FALSE(if_timeout); if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); ir_IF_FALSE(if_deferred); jit_LOAD_IP_ADDR(jit, opline); - ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_loop_interrupt_handler)); ir_IF_TRUE(if_deferred); ride_path = ir_END(); @@ -2086,7 +2088,7 @@ static int zend_jit_exception_handler_free_op2_stub(zend_jit_ctx *jit) return 1; } -static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit) +static int zend_jit_interrupt_handler_stub_ex(zend_jit_ctx *jit, bool loop_interrupt) { ir_ref if_timeout, if_exception; @@ -2099,7 +2101,11 @@ static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit) ir_CALL(IR_VOID, ir_CONST_FUNC(zend_timeout)); ir_MERGE_WITH_EMPTY_TRUE(if_timeout); - ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_jit_check_deferred_errors), jit_FP(jit)); + if (loop_interrupt) { + ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_check_deferred_loop), jit_FP(jit)); + } else { + ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_check_deferred_errors), jit_FP(jit)); + } if (zend_interrupt_function) { ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_interrupt_function), jit_FP(jit)); } @@ -2119,6 +2125,16 @@ static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit) return 1; } +static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit) +{ + return zend_jit_interrupt_handler_stub_ex(jit, false); +} + +static int zend_jit_loop_interrupt_handler_stub(zend_jit_ctx *jit) +{ + return zend_jit_interrupt_handler_stub_ex(jit, true); +} + static int zend_jit_leave_function_handler_stub(zend_jit_ctx *jit) { #if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL @@ -2523,7 +2539,7 @@ static int zend_jit_trace_escape_stub(zend_jit_ctx *jit) static int zend_jit_trace_exit_stub(zend_jit_ctx *jit) { - ir_ref ref, ret, if_zero, addr; + ir_ref ref, ret, if_zero, if_loop_interrupt, addr; // EX(opline) = opline ir_STORE(jit_EX(opline), jit_IP(jit)); @@ -2548,6 +2564,16 @@ static int zend_jit_trace_exit_stub(zend_jit_ctx *jit) ir_IF_FALSE(if_zero); ir_GUARD(ir_GE(ret, ir_CONST_I32(0)), jit_STUB_ADDR(jit, jit_stub_trace_halt)); + if_loop_interrupt = ir_IF(ir_EQ(ret, ir_CONST_I32(ZEND_JIT_EXIT_LOOP_INTERRUPT))); + ir_IF_TRUE(if_loop_interrupt); + + ref = jit_EG(current_execute_data); + jit_STORE_FP(jit, ir_LOAD_A(ref)); + ref = ir_LOAD_A(jit_EX(opline)); + jit_STORE_IP(jit, ref); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_loop_interrupt_handler)); + + ir_IF_FALSE(if_loop_interrupt); ref = jit_EG(current_execute_data); jit_STORE_FP(jit, ir_LOAD_A(ref)); @@ -3157,6 +3183,9 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_free_call_frame); REGISTER_HELPER(zend_jit_exception_in_interrupt_handler_helper); REGISTER_HELPER(zend_jit_check_deferred_errors); + REGISTER_HELPER(zend_jit_check_deferred_loop); + REGISTER_HELPER(zend_jit_flush_deferred_dtors_helper); + REGISTER_HELPER(zend_jit_flush_deferred_dtors_surface_helper); REGISTER_HELPER(zend_jit_verify_arg_slow); REGISTER_HELPER(zend_missing_arg_error); REGISTER_HELPER(zend_jit_only_vars_by_reference); @@ -4202,6 +4231,26 @@ static int zend_jit_handler(zend_jit_ctx *jit, const zend_op *opline, int may_th return 1; } +static int zend_jit_flush_deferred_dtors(zend_jit_ctx *jit, const zend_op *opline) +{ + ir_ref if_pending = ir_IF(ir_OR_U32( + ir_LOAD_U32(jit_EG(deferred_dtors.count)), + ir_LOAD_U32(jit_EG(deferred_dtors.values_count)))); + + ir_IF_TRUE_cold(if_pending); + jit_SET_EX_OPLINE(jit, opline); + if ((opline + 1)->opcode == ZEND_THROW) { + ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_flush_deferred_dtors_surface_helper), jit_FP(jit)); + } else { + ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_flush_deferred_dtors_helper), jit_FP(jit)); + } + zend_jit_check_exception(jit); + ir_ref cold_path = ir_END(); + ir_IF_FALSE(if_pending); + ir_MERGE_WITH(cold_path); + return 1; +} + static int zend_jit_tail_handler(zend_jit_ctx *jit, const zend_op *opline) { ir_ref ref; @@ -10550,6 +10599,8 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen } else { zend_basic_block *bb; + zend_jit_check_timeout(jit, NULL, NULL); + do { if (recursive_call_through_jmp) { ir_ref begin, end; @@ -10625,14 +10676,6 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen // JIT: EG(current_execute_data) = execute_data; ir_STORE(jit_EG(current_execute_data), rx); - bool may_have_observer = ZEND_OBSERVER_ENABLED && (!func || (func->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)) == 0); - if (may_have_observer) { - ir_ref observer_handler; - struct jit_observer_fcall_is_unobserved_data unobserved_data = jit_observer_fcall_is_unobserved_start(jit, func, &observer_handler, rx, func_ref ? func_ref : ir_LOAD_A(jit_CALL(rx, func))); - jit_observer_fcall_begin(jit, rx, observer_handler); - jit_observer_fcall_is_unobserved_end(jit, &unobserved_data); - } - // JIT: ZVAL_NULL(EX_VAR(opline->result.var)); ir_ref res_addr = IR_UNUSED, func_ptr; @@ -10657,6 +10700,38 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); + ir_ref skip_path = IR_UNUSED; + + if (!RETURN_VALUE_USED(opline)) { + ir_ref flush_inputs = IR_UNUSED; + ir_ref if_pending = ir_IF(ir_OR_U32( + ir_LOAD_U32(jit_EG(deferred_dtors.count)), + ir_LOAD_U32(jit_EG(deferred_dtors.values_count)))); + + ir_IF_TRUE_cold(if_pending); + ir_ref if_nested = ir_IF(ir_LOAD_A(jit_EX(call))); + ir_IF_FALSE(if_nested); + ir_CALL(IR_VOID, ir_CONST_FC_FUNC(zend_flush_deferred_dtors_surface)); + ir_ref if_exception = ir_IF(ir_LOAD_A(jit_EG_exception(jit))); + ir_IF_TRUE_cold(if_exception); + skip_path = ir_END(); + ir_IF_FALSE(if_exception); + ir_END_list(flush_inputs); + ir_IF_TRUE(if_nested); + ir_END_list(flush_inputs); + ir_IF_FALSE(if_pending); + ir_END_list(flush_inputs); + ir_MERGE_list(flush_inputs); + } + + bool may_have_observer = ZEND_OBSERVER_ENABLED && (!func || (func->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)) == 0); + if (may_have_observer) { + ir_ref observer_handler; + struct jit_observer_fcall_is_unobserved_data unobserved_data = jit_observer_fcall_is_unobserved_start(jit, func, &observer_handler, rx, func_ref ? func_ref : ir_LOAD_A(jit_CALL(rx, func))); + jit_observer_fcall_begin(jit, rx, observer_handler); + jit_observer_fcall_is_unobserved_end(jit, &unobserved_data); + } + zend_jit_reset_last_valid_opline(jit); // JIT: (zend_execute_internal ? zend_execute_internal : fbc->internal_function.handler)(call, ret); @@ -10679,12 +10754,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen jit_observer_fcall_end(jit, rx, res_ref); } - /* When zend_interrupt_function is set, it gets called while - * the frame is still on top. This is less efficient than - * doing it later once it's popped off. There is code further - * down that handles when there isn't an interrupt function. - */ - if (zend_interrupt_function || !trace) { + { // JIT: if (EG(vm_interrupt)) zend_fcall_interrupt(execute_data); ir_ref if_interrupt = ir_IF(ir_LOAD_U8(jit_EG(vm_interrupt))); ir_IF_TRUE_cold(if_interrupt); @@ -10692,6 +10762,10 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen ir_MERGE_WITH_EMPTY_FALSE(if_interrupt); } + if (skip_path) { + ir_MERGE_WITH(skip_path); + } + // JIT: EG(current_execute_data) = execute_data; ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); @@ -10802,25 +10876,6 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen ir_GUARD_NOT(ir_LOAD_A(jit_EG_exception(jit)), jit_STUB_ADDR(jit, jit_stub_icall_throw)); - /* If there isn't a zend_interrupt_function, the timeout is - * handled here because it's more efficient. - */ - if (!zend_interrupt_function && trace) { - // TODO: Can we avoid checking for interrupts after each call ??? - if (trace) { - int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM); - - exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if (!exit_addr) { - return 0; - } - } else { - exit_addr = NULL; - } - - zend_jit_check_timeout(jit, opline + 1, exit_addr); - } - if ((!trace || !func) && opline->opcode != ZEND_DO_ICALL) { jit_LOAD_IP_ADDR(jit, opline + 1); } else if (trace @@ -11298,6 +11353,39 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, #endif } + { + ir_ref flush_inputs = IR_UNUSED; + ir_ref if_pending = ir_IF(ir_OR_U32( + ir_LOAD_U32(jit_EG(deferred_dtors.count)), + ir_LOAD_U32(jit_EG(deferred_dtors.values_count)))); + + ir_IF_TRUE_cold(if_pending); + ir_ref if_call = ir_IF(ir_LOAD_A(jit_EX(call))); + ir_IF_FALSE(if_call); + if (trace->op != ZEND_JIT_TRACE_END + && (JIT_G(current_frame) && !TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) { + const zend_jit_trace_rec *q = trace; + + ZEND_ASSERT(q->op == ZEND_JIT_TRACE_BACK); + do { + q++; + } while (q->op == ZEND_JIT_TRACE_INIT_CALL); + ZEND_ASSERT(q->op == ZEND_JIT_TRACE_VM || q->op == ZEND_JIT_TRACE_END); + ZEND_ASSERT(q->opline != NULL); + ir_STORE(jit_EX(opline), ir_CONST_ADDR(q->opline)); + } else { + ir_STORE(jit_EX(opline), jit_IP(jit)); + } + ir_CALL(IR_VOID, ir_CONST_FC_FUNC(zend_flush_deferred_dtors)); + ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); + ir_END_list(flush_inputs); + ir_IF_TRUE(if_call); + ir_END_list(flush_inputs); + ir_IF_FALSE(if_pending); + ir_END_list(flush_inputs); + ir_MERGE_list(flush_inputs); + } + if (cold_path) { ir_MERGE_WITH(cold_path); } @@ -11346,6 +11434,23 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, return 1; } else { + ir_ref flush_inputs = IR_UNUSED; + ir_ref if_pending = ir_IF(ir_OR_U32( + ir_LOAD_U32(jit_EG(deferred_dtors.count)), + ir_LOAD_U32(jit_EG(deferred_dtors.values_count)))); + + ir_IF_TRUE_cold(if_pending); + ir_ref if_call = ir_IF(ir_LOAD_A(jit_EX(call))); + ir_IF_FALSE(if_call); + ir_CALL(IR_VOID, ir_CONST_FC_FUNC(zend_flush_deferred_dtors)); + ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); + ir_END_list(flush_inputs); + ir_IF_TRUE(if_call); + ir_END_list(flush_inputs); + ir_IF_FALSE(if_pending); + ir_END_list(flush_inputs); + ir_MERGE_list(flush_inputs); + // JIT: if (EG(exception)) ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); // JIT: opline = EX(opline) + 1 diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 993dbd088d75..0a8133eff907 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6202,6 +6202,9 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, goto done; case ZEND_FREE: case ZEND_FE_FREE: + if (opline->extended_value & ZEND_FREE_ON_RETURN) { + break; + } op1_info = OP1_INFO(); if (!zend_jit_free(&ctx, opline, op1_info, zend_may_throw(opline, ssa_op, op_array, ssa))) { @@ -6467,6 +6470,11 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, goto jit_failure; } goto done; + case ZEND_FLUSH_DEFERRED_DTORS: + if (!zend_jit_flush_deferred_dtors(&ctx, opline)) { + goto jit_failure; + } + goto done; case ZEND_FRAMELESS_ICALL_0: jit_frameless_icall0(jit, opline); goto done; @@ -7231,14 +7239,15 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, || (ra && zend_jit_trace_stack_needs_deoptimization(stack, op_array->last_var + op_array->T))) { /* Deoptimize to the first instruction of the loop */ - uint32_t exit_point = zend_jit_trace_get_exit_point(trace_buffer[1].opline, ZEND_JIT_EXIT_TO_VM); + uint32_t exit_point = zend_jit_trace_get_exit_point( + trace_buffer[1].opline, ZEND_JIT_EXIT_TO_VM | ZEND_JIT_EXIT_LOOP_INTERRUPT); timeout_exit_addr = zend_jit_trace_get_exit_addr(exit_point); if (!timeout_exit_addr) { goto jit_failure; } } else { - timeout_exit_addr = zend_jit_stub_handlers[jit_stub_interrupt_handler]; + timeout_exit_addr = zend_jit_stub_handlers[jit_stub_loop_interrupt_handler]; } } @@ -7302,13 +7311,15 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, for (i = 0; i < op_array->last_var + op_array->T; i++) { SET_STACK_TYPE(stack, i, IS_UNKNOWN, 1); } - exit_point = zend_jit_trace_get_exit_point(zend_jit_traces[t->link].opline, ZEND_JIT_EXIT_TO_VM); + exit_point = zend_jit_trace_get_exit_point( + zend_jit_traces[t->link].opline, + ZEND_JIT_EXIT_TO_VM | ZEND_JIT_EXIT_LOOP_INTERRUPT); timeout_exit_addr = zend_jit_trace_get_exit_addr(exit_point); if (!timeout_exit_addr) { goto jit_failure; } } else { - timeout_exit_addr = zend_jit_stub_handlers[jit_stub_interrupt_handler]; + timeout_exit_addr = zend_jit_stub_handlers[jit_stub_loop_interrupt_handler]; } } zend_jit_trace_link_to_root(&ctx, &zend_jit_traces[t->link], timeout_exit_addr); @@ -8816,6 +8827,10 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf EX(opline) = opline; } + if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_LOOP_INTERRUPT) { + return ZEND_JIT_EXIT_LOOP_INTERRUPT; + } + if (zend_atomic_bool_load_ex(&EG(vm_interrupt)) || JIT_G(tracing)) { return 1; /* Lock-free check if the side trace was already JIT-ed or blacklist-ed in another process */ diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 8422a928eb8c..61f4875d50c7 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -102,11 +102,13 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPC { zend_execute_data *old_execute_data; - if (UNEXPECTED(EG(deferred_errors).size)) { + if (UNEXPECTED(EG(deferred_errors).size || EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { EX(opline) = opline; + zend_flush_deferred_dtors(); zend_flush_deferred_errors(); } + EG(frame_teardown)++; if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_clean_and_cache_symbol_table(EX(symbol_table)); } @@ -120,6 +122,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPC if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { zend_free_extra_named_params(EX(extra_named_params)); } + EG(frame_teardown)--; old_execute_data = execute_data; execute_data = EX(prev_execute_data); @@ -1099,6 +1102,21 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, #endif if (UNEXPECTED(execute_data != prev_execute_data)) { + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count) + && !EX(call) + && opline->opcode != ZEND_DO_FCALL + && opline->opcode != ZEND_DO_ICALL + && opline->opcode != ZEND_DO_UCALL + && opline->opcode != ZEND_DO_FCALL_BY_NAME) { + EX(opline) = opline; + zend_flush_deferred_dtors(); + if (UNEXPECTED(EG(exception))) { + opline = EX(opline); + stop = ZEND_JIT_TRACE_STOP_EXCEPTION; + break; + } + } + op_array = &EX(func)->op_array; jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array); @@ -1119,6 +1137,10 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (execute_data->prev_execute_data == prev_execute_data) { /* Enter into function */ + if (UNEXPECTED(EG(deferred_dtors).count || EG(deferred_dtors).values_count)) { + stop = ZEND_JIT_TRACE_STOP_INTERPRETER; + break; + } if (UNEXPECTED(EG(deferred_errors).size)) { EX(opline) = opline; zend_flush_deferred_errors(); diff --git a/ext/opcache/tests/jit/count_001.phpt b/ext/opcache/tests/jit/count_001.phpt index 2fb1c52d2855..123413e209aa 100644 --- a/ext/opcache/tests/jit/count_001.phpt +++ b/ext/opcache/tests/jit/count_001.phpt @@ -61,8 +61,13 @@ for ($i = 0; $i < 5; $i++) { 3 4 5 +Unreachable Caught In destructor +Unreachable Caught In destructor +Unreachable Caught In destructor +Unreachable Caught In destructor +Unreachable Caught In destructor \ No newline at end of file diff --git a/ext/opcache/tests/jit/gh18262-004.phpt b/ext/opcache/tests/jit/gh18262-004.phpt index c8dccd30c98a..3cdc8ad7fe39 100644 --- a/ext/opcache/tests/jit/gh18262-004.phpt +++ b/ext/opcache/tests/jit/gh18262-004.phpt @@ -31,6 +31,7 @@ int(1) Fatal error: Uncaught Exception: B::__destruct in %s:%d Stack trace: -#0 %s(%d): B->__destruct() -#1 {main} +#0 [internal function]: B->__destruct() +#1 %s(%d): var_dump('str') +#2 {main} thrown in %s on line %d diff --git a/ext/opcache/tests/opt/gh18107_1.phpt b/ext/opcache/tests/opt/gh18107_1.phpt index a2cf3162090c..67c5b4a102f8 100644 --- a/ext/opcache/tests/opt/gh18107_1.phpt +++ b/ext/opcache/tests/opt/gh18107_1.phpt @@ -28,19 +28,22 @@ $_main: ; (after optimizer) ; %s 0000 T1 = ISSET_ISEMPTY_CV (isset) CV0($badvar) -0001 JMPNZ T1 0006 +0001 JMPNZ T1 0007 0002 T3 = NEW 1 string("Exception") 0003 SEND_VAL%S string("Should happen") 1 0004 DO_FCALL -0005 THROW T3 -0006 JMP 0006 -0007 T6 = NEW 1 string("Exception") -0008 SEND_VAL%S string("Should not happen") 1 -0009 DO_FCALL -0010 THROW T6 -0011 FAST_RET T5 +0005 FLUSH_DEFERRED_DTORS +0006 THROW T3 +0007 FLUSH_DEFERRED_DTORS +0008 JMP 0008 +0009 T6 = NEW 1 string("Exception") +0010 SEND_VAL%S string("Should not happen") 1 +0011 DO_FCALL +0012 FLUSH_DEFERRED_DTORS +0013 THROW T6 +0014 FAST_RET T5 EXCEPTION TABLE: - 0006, -, 0007, 0011 + 0008, -, 0009, 0014 Fatal error: Uncaught Exception: Should happen in %s:%d Stack trace: diff --git a/ext/opcache/tests/opt/gh18107_2.phpt b/ext/opcache/tests/opt/gh18107_2.phpt index 74a709223e5a..3c883778a2c0 100644 --- a/ext/opcache/tests/opt/gh18107_2.phpt +++ b/ext/opcache/tests/opt/gh18107_2.phpt @@ -31,23 +31,28 @@ $_main: ; (after optimizer) ; %s 0000 T2 = ISSET_ISEMPTY_CV (isset) CV0($badvar) -0001 JMPNZ T2 0008 +0001 JMPNZ T2 0007 0002 T4 = NEW 1 string("Exception") 0003 SEND_VAL%S string("Should happen") 1 0004 DO_FCALL -0005 THROW T4 -0006 CV1($e) = CATCH string("Throwable") -0007 ECHO string("foo") -0008 T6 = FAST_CALL 0010 -0009 JMP 0015 -0010 T7 = NEW 1 string("Exception") -0011 SEND_VAL%S string("Should not happen") 1 -0012 DO_FCALL -0013 THROW T7 -0014 FAST_RET T6 -0015 RETURN int(1) +0005 FLUSH_DEFERRED_DTORS +0006 THROW T4 +0007 FLUSH_DEFERRED_DTORS +0008 FLUSH_DEFERRED_DTORS +0009 JMP 0012 +0010 CV1($e) = CATCH string("Throwable") +0011 ECHO string("foo") +0012 T6 = FAST_CALL 0014 +0013 JMP 0020 +0014 T7 = NEW 1 string("Exception") +0015 SEND_VAL%S string("Should not happen") 1 +0016 DO_FCALL +0017 FLUSH_DEFERRED_DTORS +0018 THROW T7 +0019 FAST_RET T6 +0020 RETURN int(1) EXCEPTION TABLE: - 0006, 0006, 0010, 0014 + 0008, 0010, 0014, 0019 Fatal error: Uncaught Exception: Should happen in %s:%d Stack trace: diff --git a/ext/pdo/tests/attr_statement_class/pdo_ATTR_STATEMENT_CLASS_basic.phpt b/ext/pdo/tests/attr_statement_class/pdo_ATTR_STATEMENT_CLASS_basic.phpt index dfef3bb4839f..87121bae49de 100644 --- a/ext/pdo/tests/attr_statement_class/pdo_ATTR_STATEMENT_CLASS_basic.phpt +++ b/ext/pdo/tests/attr_statement_class/pdo_ATTR_STATEMENT_CLASS_basic.phpt @@ -85,8 +85,8 @@ array(1) { } bool(true) bool(true) -StatementWithPublicDestructor::__destruct Class derived from PDOStatement, with private constructor: +StatementWithPublicDestructor::__destruct bool(true) StatementWithPrivateConstructor::__construct object(StatementWithPrivateConstructor)#2 (1) { diff --git a/ext/pdo/tests/pdo_stmt_cyclic_references.phpt b/ext/pdo/tests/pdo_stmt_cyclic_references.phpt index 59d72c4b0d36..8d1c407da4a2 100644 --- a/ext/pdo/tests/pdo_stmt_cyclic_references.phpt +++ b/ext/pdo/tests/pdo_stmt_cyclic_references.phpt @@ -106,7 +106,7 @@ object(TestRow)#%d (4) { string(11) "Hello world" } Fetch into: -object(TestRow)#4 (4) { +object(TestRow)#5 (4) { ["id"]=> string(1) "1" ["val"]=> @@ -116,7 +116,7 @@ object(TestRow)#4 (4) { ["arg"]=> string(21) "I am being fetch into" } -object(TestRow)#4 (4) { +object(TestRow)#5 (4) { ["id"]=> string(1) "2" ["val"]=> @@ -126,7 +126,7 @@ object(TestRow)#4 (4) { ["arg"]=> string(21) "I am being fetch into" } -object(TestRow)#4 (4) { +object(TestRow)#5 (4) { ["id"]=> string(1) "3" ["val"]=> diff --git a/ext/pgsql/tests/pg_fetch_object_ctor_paths.phpt b/ext/pgsql/tests/pg_fetch_object_ctor_paths.phpt index bd3df73acba4..8302e15af089 100644 --- a/ext/pgsql/tests/pg_fetch_object_ctor_paths.phpt +++ b/ext/pgsql/tests/pg_fetch_object_ctor_paths.phpt @@ -65,5 +65,5 @@ pg_query($db, "DROP TABLE IF EXISTS pg_fetch_object_ctor_paths"); pg_fetch_object(): Argument #4 ($constructor_args) must be empty when the specified class (NoCtor) does not have a constructor caught: boom ctor sees: num=1, str=hello -SeesProps::__destruct called Ok +SeesProps::__destruct called diff --git a/ext/phar/tests/bug69720.phpt b/ext/phar/tests/bug69720.phpt index a6afe0f15e7f..8b99f23fa9a5 100644 --- a/ext/phar/tests/bug69720.phpt +++ b/ext/phar/tests/bug69720.phpt @@ -29,7 +29,7 @@ array(1) { ["whatever"]=> int(123) } -object(DateTime)#6 (3) { +object(DateTime)#%d (3) { ["date"]=> string(26) "2000-01-01 00:00:00.000000" ["timezone_type"]=> diff --git a/ext/phar/tests/tar/bug70417.phpt b/ext/phar/tests/tar/bug70417.phpt index 7bb6bbafbcfa..96c9119bca9a 100644 --- a/ext/phar/tests/tar/bug70417.phpt +++ b/ext/phar/tests/tar/bug70417.phpt @@ -23,4 +23,4 @@ $filename = __DIR__ . '/bug70417.tar'; @unlink("$filename.gz"); ?> --EXPECT-- -int(0) +int(2) diff --git a/ext/phar/tests/test_alias_unset.phpt b/ext/phar/tests/test_alias_unset.phpt index 3a11fbac6d5a..2120247a8ac9 100644 --- a/ext/phar/tests/test_alias_unset.phpt +++ b/ext/phar/tests/test_alias_unset.phpt @@ -37,7 +37,10 @@ $a = file_get_contents($pname . '/file1.txt'); // this fails because $fname2 ref --EXPECTF-- -Cannot open archive "%stest_alias_unset.2.phar.php", alias is already in use by existing archive -string(5) "first" +Warning: fopen(phar://%stest_alias_unset.phar.php/file1.txt): Failed to open stream: Cannot open archive "%stest_alias_unset.phar.php", alias is already in use by existing archive in %stest_alias_unset.php on line %d -Warning: file_get_contents(phar://%sfile1.txt): Failed to open stream: Cannot open archive "%stest_alias_unset.phar.php", alias is already in use by existing archive in %stest_alias_unset.php on line %d +Fatal error: Uncaught TypeError: fclose(): Argument #1 ($stream) must be of type resource, false given in %stest_alias_unset.php:%d +Stack trace: +#0 %stest_alias_unset.php(%d): fclose(false) +#1 {main} + thrown in %stest_alias_unset.php on line %d diff --git a/ext/spl/tests/SplFixedArray_immediate_gc.phpt b/ext/spl/tests/SplFixedArray_immediate_gc.phpt index cead2238c190..afebd03d626c 100644 --- a/ext/spl/tests/SplFixedArray_immediate_gc.phpt +++ b/ext/spl/tests/SplFixedArray_immediate_gc.phpt @@ -24,8 +24,8 @@ object(SplFixedArray)#2 (1) { } } Replacing -In destructor Dumping again +In destructor object(SplFixedArray)#2 (1) { [0]=> object(stdClass)#3 (0) { diff --git a/ext/spl/tests/gh12721.phpt b/ext/spl/tests/gh12721.phpt index 4310afcdd933..43e34dd053d9 100644 --- a/ext/spl/tests/gh12721.phpt +++ b/ext/spl/tests/gh12721.phpt @@ -43,7 +43,7 @@ object(SplFileInfo)#4 (2) { file1.gh12721 gh12721 file1.gh12721 -object(SplFileInfo)#3 (2) { +object(SplFileInfo)#5 (2) { ["pathName":"SplFileInfo":private]=> string(15) "%sfile1.gh12721" ["fileName":"SplFileInfo":private]=> diff --git a/ext/spl/tests/iterator_044.phpt b/ext/spl/tests/iterator_044.phpt index 74321194e098..27e35a903276 100644 --- a/ext/spl/tests/iterator_044.phpt +++ b/ext/spl/tests/iterator_044.phpt @@ -131,7 +131,7 @@ object(stdClass)#1 (0) { CachingIterator::offsetExists(): Argument #1 ($key) must be of type string, stdClass given CachingIterator::offsetGet(): Argument #1 ($key) must be of type string, stdClass given ===2=== -object(MyFoo)#2 (0) { +object(MyFoo)#8 (0) { } bool(true) int(1) diff --git a/ext/zend_test/tests/zend_object_init_with_constructor.phpt b/ext/zend_test/tests/zend_object_init_with_constructor.phpt index aa3ad7ee845a..2b08fc127a39 100644 --- a/ext/zend_test/tests/zend_object_init_with_constructor.phpt +++ b/ext/zend_test/tests/zend_object_init_with_constructor.phpt @@ -161,12 +161,12 @@ TypeError: TestUserWithConstructorArgs::__construct(): Argument #1 ($int_param) Error: Unknown named parameter $unused_param object(TestUserWithConstructorArgs)#%d (0) { } -Destructor for TestUserWithConstructorArgs Passing too many args to constructor +Destructor for TestUserWithConstructorArgs object(TestUserWithConstructorArgs)#%d (0) { } -Destructor for TestUserWithConstructorArgs Testing class with defined constructor and no params +Destructor for TestUserWithConstructorArgs object(TestUserWithConstructorNoParams)#%d (0) { } Destructor for TestUserWithConstructorNoParams diff --git a/ext/zend_test/tests/zend_object_init_with_constructor_classes_without_constructor.phpt b/ext/zend_test/tests/zend_object_init_with_constructor_classes_without_constructor.phpt index 1adaaafaa195..fd28fef08e66 100644 --- a/ext/zend_test/tests/zend_object_init_with_constructor_classes_without_constructor.phpt +++ b/ext/zend_test/tests/zend_object_init_with_constructor_classes_without_constructor.phpt @@ -105,13 +105,13 @@ Userland class: Using new: object(TestUserWithoutConstructor)#1 (0) { } -Destructor for TestUserWithoutConstructor Using zend_object_init_with_constructor(): -object(TestUserWithoutConstructor)#1 (0) { -} Destructor for TestUserWithoutConstructor +object(TestUserWithoutConstructor)#2 (0) { +} Internal class: Using new: +Destructor for TestUserWithoutConstructor object(_ZendTestMagicCall)#1 (0) { } Using zend_object_init_with_constructor(): @@ -123,17 +123,17 @@ Userland class: Using new: object(TestUserWithoutConstructor)#1 (0) { } -Destructor for TestUserWithoutConstructor Using zend_object_init_with_constructor(): -object(TestUserWithoutConstructor)#1 (0) { -} Destructor for TestUserWithoutConstructor +object(TestUserWithoutConstructor)#2 (0) { +} Internal class: Using new: -object(_ZendTestMagicCall)#1 (0) { +Destructor for TestUserWithoutConstructor +object(_ZendTestMagicCall)#2 (0) { } Using zend_object_init_with_constructor(): -object(_ZendTestMagicCall)#1 (0) { +object(_ZendTestMagicCall)#2 (0) { } #### Passing extra named args #### diff --git a/ext/zip/tests/ZipArchive_destruct.phpt b/ext/zip/tests/ZipArchive_destruct.phpt index 7e3ef6c5dae4..52ae2c5ff55d 100644 --- a/ext/zip/tests/ZipArchive_destruct.phpt +++ b/ext/zip/tests/ZipArchive_destruct.phpt @@ -24,5 +24,10 @@ $leak->addFromString('test', 'test'); $file = __DIR__ . '/ZipArchive_destruct.zip'; @unlink($file); ?> ---EXPECT-- -===DONE=== +--EXPECTF-- +Warning: Undefined variable $leak in %s on line 12 + +Fatal error: Uncaught Error: Call to a member function open() on null in %s:12 +Stack trace: +#0 {main} + thrown in %s on line 12 diff --git a/tests/classes/bug27468.phpt b/tests/classes/bug27468.phpt index d590683c82cf..1474f6d453b0 100644 --- a/tests/classes/bug27468.phpt +++ b/tests/classes/bug27468.phpt @@ -11,7 +11,7 @@ new foo(); echo 'OK'; ?> --EXPECTF-- +OK Warning: Undefined property: foo::$x in %s on line %d Warning: foreach() argument must be of type array|object, null given in %sbug27468.php on line 4 -OK diff --git a/tests/classes/ctor_dtor_inheritance.phpt b/tests/classes/ctor_dtor_inheritance.phpt index 5dbb1747065b..4e6541d83f8e 100644 --- a/tests/classes/ctor_dtor_inheritance.phpt +++ b/tests/classes/ctor_dtor_inheritance.phpt @@ -59,12 +59,12 @@ base Object ( [name] => base ) +Testing class derived base::__destruct base Object ( [name] => base ) -Testing class derived derived Object ( [name] => init @@ -82,6 +82,7 @@ derived Object [name] => derived [other] => other ) +Done base::__destruct derived Object ( @@ -94,4 +95,3 @@ derived Object [name] => derived [other] => other ) -Done diff --git a/tests/classes/destructor_and_exceptions.phpt b/tests/classes/destructor_and_exceptions.phpt index 0f51a21a09d0..666fe6cb2eb4 100644 --- a/tests/classes/destructor_and_exceptions.phpt +++ b/tests/classes/destructor_and_exceptions.phpt @@ -54,5 +54,6 @@ catch(FatalException $e) FailClass::__destruct Caught: FailClass FatalException::__construct +Done: FatalException::__construct FailClass::__destruct Caught Exception: FailClass diff --git a/tests/classes/destructor_inheritance.phpt b/tests/classes/destructor_inheritance.phpt index c527c04b4fc1..06f7c9d9fe0f 100644 --- a/tests/classes/destructor_inheritance.phpt +++ b/tests/classes/destructor_inheritance.phpt @@ -23,5 +23,4 @@ echo 'Done'; ?> --EXPECT-- base::__construct -base::__destruct -Done +Donebase::__destruct diff --git a/tests/classes/destructor_visibility_001.phpt b/tests/classes/destructor_visibility_001.phpt index 6fee6e3be742..61912e54e1ad 100644 --- a/tests/classes/destructor_visibility_001.phpt +++ b/tests/classes/destructor_visibility_001.phpt @@ -19,7 +19,9 @@ unset($obj); ?> ===DONE=== --EXPECTF-- +===DONE=== + Fatal error: Uncaught Error: Call to private Derived::__destruct() from global scope in %s:%d Stack trace: #0 {main} - thrown in %sdestructor_visibility_001.php on line %d + thrown in %s on line %d diff --git a/tests/classes/factory_and_singleton_005.phpt b/tests/classes/factory_and_singleton_005.phpt index 41914bf0e087..27779146d72e 100644 --- a/tests/classes/factory_and_singleton_005.phpt +++ b/tests/classes/factory_and_singleton_005.phpt @@ -14,7 +14,9 @@ $obj = NULL; echo "Done\n"; ?> --EXPECTF-- +Done + Fatal error: Uncaught Error: Call to protected test::__destruct() from global scope in %s:%d Stack trace: #0 {main} - thrown in %sfactory_and_singleton_005.php on line %d + thrown in %s on line %d diff --git a/tests/classes/factory_and_singleton_006.phpt b/tests/classes/factory_and_singleton_006.phpt index 596a0792cd09..bcc57c095766 100644 --- a/tests/classes/factory_and_singleton_006.phpt +++ b/tests/classes/factory_and_singleton_006.phpt @@ -14,7 +14,9 @@ $obj = NULL; echo "Done\n"; ?> --EXPECTF-- +Done + Fatal error: Uncaught Error: Call to private test::__destruct() from global scope in %s:%d Stack trace: #0 {main} - thrown in %sfactory_and_singleton_006.php on line %d + thrown in %s on line %d diff --git a/tests/classes/iterators_002.phpt b/tests/classes/iterators_002.phpt index 71f252dc0ede..08e11b12551a 100644 --- a/tests/classes/iterators_002.phpt +++ b/tests/classes/iterators_002.phpt @@ -106,6 +106,6 @@ double:2:0 c_iter::__destruct c_iter::next c_iter::valid = false +Done c_iter::__destruct c::__destruct -Done diff --git a/tests/classes/tostring_002.phpt b/tests/classes/tostring_002.phpt index 3977ad883f25..0ab2378f1003 100644 --- a/tests/classes/tostring_002.phpt +++ b/tests/classes/tostring_002.phpt @@ -24,6 +24,6 @@ $o = new Test; ?> ====DONE==== --EXPECT-- -Hello ====DONE==== Hello +Hello From 6ad19665dab845517ea6fb1595e4ad324650ffe8 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 15 Jul 2026 07:06:28 -0400 Subject: [PATCH 3/3] Run oversized bz2 resource tests in isolation to avoid OOM gh20620 and bzdecompress_input_too_large each allocate ~4GB under RUN_RESOURCE_HEAVY_TESTS; two in parallel exhaust a memory-constrained runner and the OOM killer aborts the whole run. Mark both --CONFLICTS-- all so run-tests executes them in isolation, matching ext/tidy/tests/parsing_file_too_large.phpt. --- ext/bz2/tests/bzdecompress_input_too_large.phpt | 2 ++ ext/bz2/tests/gh20620.phpt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ext/bz2/tests/bzdecompress_input_too_large.phpt b/ext/bz2/tests/bzdecompress_input_too_large.phpt index 88c93d366c54..9774c6dbbfd6 100644 --- a/ext/bz2/tests/bzdecompress_input_too_large.phpt +++ b/ext/bz2/tests/bzdecompress_input_too_large.phpt @@ -10,6 +10,8 @@ if (!getenv('RUN_RESOURCE_HEAVY_TESTS')) die('skip resource-heavy test'); if (getenv('SKIP_SLOW_TESTS')) die('skip slow test'); if (PHP_INT_SIZE != 8) die('skip 64-bit only'); ?> +--CONFLICTS-- +all --FILE-- +--CONFLICTS-- +all --INI-- memory_limit=-1 --FILE--