diff options
author | Sammy Kaye Powers <sammyk@php.net> | 2020-11-11 14:25:39 -0800 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-11-17 10:28:47 +0100 |
commit | 58d41b8c4f9e8006f6c136186ef4788b1cc901dc (patch) | |
tree | 1e95e86bf5cbd5929876f2d539f96e488a9297cf | |
parent | 9cfb5261e4b48e0520fc7e085e6eb189eeb33b3d (diff) | |
download | php-git-58d41b8c4f9e8006f6c136186ef4788b1cc901dc.tar.gz |
Provide unused retvals to observers
Make sure that the return value is available to observers, even if
it is not used by the caller.
Closes GH-6422.
-rw-r--r-- | Zend/zend_vm_def.h | 26 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 117 | ||||
-rwxr-xr-x | Zend/zend_vm_gen.php | 3 | ||||
-rw-r--r-- | ext/zend_test/test.c | 9 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_01.phpt | 29 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_02.phpt | 32 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_03.phpt | 32 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_04.phpt | 52 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_05.phpt | 33 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_06.phpt | 30 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_07.phpt | 39 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_by_ref_01.phpt | 30 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_by_ref_02.phpt | 34 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_retval_by_ref_03.phpt | 42 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_shutdown_01.phpt | 44 | ||||
-rw-r--r-- | ext/zend_test/tests/observer_shutdown_02.phpt | 50 |
16 files changed, 555 insertions, 47 deletions
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c8e7774efc..2932bfbdfa 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4239,9 +4239,11 @@ ZEND_VM_INLINE_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY, SPEC(OBSERVER)) USE_OPLINE zval *retval_ptr; zval *return_value; + ZEND_OBSERVER_USE_RETVAL; retval_ptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); return_value = EX(return_value); + ZEND_OBSERVER_SET_RETVAL(); if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -4305,6 +4307,7 @@ ZEND_VM_INLINE_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY, SPEC(OBSERVER)) } ZEND_OBSERVER_SAVE_OPLINE(); ZEND_OBSERVER_FCALL_END(execute_data, return_value); + ZEND_OBSERVER_FREE_RETVAL(); ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } @@ -4312,9 +4315,13 @@ ZEND_VM_COLD_CONST_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY, SRC, { USE_OPLINE zval *retval_ptr; + zval *return_value; + ZEND_OBSERVER_USE_RETVAL; SAVE_OPLINE(); + return_value = EX(return_value); + ZEND_OBSERVER_SET_RETVAL(); do { if ((OP1_TYPE & (IS_CONST|IS_TMP_VAR)) || (OP1_TYPE == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) { @@ -4322,15 +4329,15 @@ ZEND_VM_COLD_CONST_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY, SRC, zend_error(E_NOTICE, "Only variable references should be returned by reference"); retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); - if (!EX(return_value)) { + if (!return_value) { FREE_OP1(); } else { if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) { - ZVAL_COPY_VALUE(EX(return_value), retval_ptr); + ZVAL_COPY_VALUE(return_value, retval_ptr); break; } - ZVAL_NEW_REF(EX(return_value), retval_ptr); + ZVAL_NEW_REF(return_value, retval_ptr); if (OP1_TYPE == IS_CONST) { Z_TRY_ADDREF_P(retval_ptr); } @@ -4344,8 +4351,8 @@ ZEND_VM_COLD_CONST_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY, SRC, ZEND_ASSERT(retval_ptr != &EG(uninitialized_zval)); if (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr)) { zend_error(E_NOTICE, "Only variable references should be returned by reference"); - if (EX(return_value)) { - ZVAL_NEW_REF(EX(return_value), retval_ptr); + if (return_value) { + ZVAL_NEW_REF(return_value, retval_ptr); } else { FREE_OP1_VAR_PTR(); } @@ -4353,19 +4360,20 @@ ZEND_VM_COLD_CONST_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY, SRC, } } - if (EX(return_value)) { + if (return_value) { if (Z_ISREF_P(retval_ptr)) { Z_ADDREF_P(retval_ptr); } else { ZVAL_MAKE_REF_EX(retval_ptr, 2); } - ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr)); + ZVAL_REF(return_value, Z_REF_P(retval_ptr)); } FREE_OP1_VAR_PTR(); } while (0); - ZEND_OBSERVER_FCALL_END(execute_data, EX(return_value)); + ZEND_OBSERVER_FCALL_END(execute_data, return_value); + ZEND_OBSERVER_FREE_RETVAL(); ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } @@ -7710,7 +7718,7 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca /* Uncaught exception */ if (zend_observer_fcall_op_array_extension != -1) { - zend_observer_fcall_end(execute_data, EX(return_value)); + zend_observer_fcall_end(execute_data, NULL); } cleanup_live_vars(execute_data, op_num, 0); if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 6fca6f4d13..0d14ff3bab 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2905,7 +2905,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try /* Uncaught exception */ if (zend_observer_fcall_op_array_extension != -1) { - zend_observer_fcall_end(execute_data, EX(return_value)); + zend_observer_fcall_end(execute_data, NULL); } cleanup_live_vars(execute_data, op_num, 0); if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) { @@ -4021,6 +4021,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_ retval_ptr = RT_CONSTANT(opline, opline->op1); return_value = EX(return_value); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -4084,6 +4085,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_ } + ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -4092,9 +4094,11 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_OBSER USE_OPLINE zval *retval_ptr; zval *return_value; + zval observer_retval; retval_ptr = get_zval_ptr_undef(opline->op1_type, opline->op1, BP_VAR_R); return_value = EX(return_value); + if (!return_value) { return_value = &observer_retval; }; if (opline->op1_type == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -4158,6 +4162,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_OBSER } SAVE_OPLINE(); zend_observer_fcall_end(execute_data, return_value); + if (return_value == &observer_retval) { zval_ptr_dtor_nogc(&observer_retval); }; ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -4165,9 +4170,12 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPE { USE_OPLINE zval *retval_ptr; + zval *return_value; SAVE_OPLINE(); + return_value = EX(return_value); + do { if ((IS_CONST & (IS_CONST|IS_TMP_VAR)) || (IS_CONST == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) { @@ -4175,15 +4183,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPE zend_error(E_NOTICE, "Only variable references should be returned by reference"); retval_ptr = RT_CONSTANT(opline, opline->op1); - if (!EX(return_value)) { + if (!return_value) { } else { if (IS_CONST == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) { - ZVAL_COPY_VALUE(EX(return_value), retval_ptr); + ZVAL_COPY_VALUE(return_value, retval_ptr); break; } - ZVAL_NEW_REF(EX(return_value), retval_ptr); + ZVAL_NEW_REF(return_value, retval_ptr); if (IS_CONST == IS_CONST) { Z_TRY_ADDREF_P(retval_ptr); } @@ -4197,8 +4205,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPE ZEND_ASSERT(retval_ptr != &EG(uninitialized_zval)); if (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr)) { zend_error(E_NOTICE, "Only variable references should be returned by reference"); - if (EX(return_value)) { - ZVAL_NEW_REF(EX(return_value), retval_ptr); + if (return_value) { + ZVAL_NEW_REF(return_value, retval_ptr); } else { } @@ -4206,17 +4214,18 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPE } } - if (EX(return_value)) { + if (return_value) { if (Z_ISREF_P(retval_ptr)) { Z_ADDREF_P(retval_ptr); } else { ZVAL_MAKE_REF_EX(retval_ptr, 2); } - ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr)); + ZVAL_REF(return_value, Z_REF_P(retval_ptr)); } } while (0); + ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -4224,9 +4233,13 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPE { USE_OPLINE zval *retval_ptr; + zval *return_value; + zval observer_retval; SAVE_OPLINE(); + return_value = EX(return_value); + if (!return_value) { return_value = &observer_retval; }; do { if ((opline->op1_type & (IS_CONST|IS_TMP_VAR)) || (opline->op1_type == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) { @@ -4234,15 +4247,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPE zend_error(E_NOTICE, "Only variable references should be returned by reference"); retval_ptr = get_zval_ptr(opline->op1_type, opline->op1, BP_VAR_R); - if (!EX(return_value)) { + if (!return_value) { FREE_OP(opline->op1_type, opline->op1.var); } else { if (opline->op1_type == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) { - ZVAL_COPY_VALUE(EX(return_value), retval_ptr); + ZVAL_COPY_VALUE(return_value, retval_ptr); break; } - ZVAL_NEW_REF(EX(return_value), retval_ptr); + ZVAL_NEW_REF(return_value, retval_ptr); if (opline->op1_type == IS_CONST) { Z_TRY_ADDREF_P(retval_ptr); } @@ -4256,8 +4269,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPE ZEND_ASSERT(retval_ptr != &EG(uninitialized_zval)); if (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr)) { zend_error(E_NOTICE, "Only variable references should be returned by reference"); - if (EX(return_value)) { - ZVAL_NEW_REF(EX(return_value), retval_ptr); + if (return_value) { + ZVAL_NEW_REF(return_value, retval_ptr); } else { if (opline->op1_type == IS_VAR) {zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));}; } @@ -4265,19 +4278,20 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPE } } - if (EX(return_value)) { + if (return_value) { if (Z_ISREF_P(retval_ptr)) { Z_ADDREF_P(retval_ptr); } else { ZVAL_MAKE_REF_EX(retval_ptr, 2); } - ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr)); + ZVAL_REF(return_value, Z_REF_P(retval_ptr)); } if (opline->op1_type == IS_VAR) {zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));}; } while (0); - zend_observer_fcall_end(execute_data, EX(return_value)); + zend_observer_fcall_end(execute_data, return_value); + if (return_value == &observer_retval) { zval_ptr_dtor_nogc(&observer_retval); }; ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -18528,6 +18542,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HA retval_ptr = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); return_value = EX(return_value); + if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -18591,6 +18606,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HA } + ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -18598,9 +18614,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER { USE_OPLINE zval *retval_ptr; + zval *return_value; SAVE_OPLINE(); + return_value = EX(return_value); + do { if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR)) || (IS_TMP_VAR == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) { @@ -18608,15 +18627,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER zend_error(E_NOTICE, "Only variable references should be returned by reference"); retval_ptr = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); - if (!EX(return_value)) { + if (!return_value) { zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); } else { if (IS_TMP_VAR == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) { - ZVAL_COPY_VALUE(EX(return_value), retval_ptr); + ZVAL_COPY_VALUE(return_value, retval_ptr); break; } - ZVAL_NEW_REF(EX(return_value), retval_ptr); + ZVAL_NEW_REF(return_value, retval_ptr); if (IS_TMP_VAR == IS_CONST) { Z_TRY_ADDREF_P(retval_ptr); } @@ -18630,8 +18649,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER ZEND_ASSERT(retval_ptr != &EG(uninitialized_zval)); if (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr)) { zend_error(E_NOTICE, "Only variable references should be returned by reference"); - if (EX(return_value)) { - ZVAL_NEW_REF(EX(return_value), retval_ptr); + if (return_value) { + ZVAL_NEW_REF(return_value, retval_ptr); } else { } @@ -18639,17 +18658,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER } } - if (EX(return_value)) { + if (return_value) { if (Z_ISREF_P(retval_ptr)) { Z_ADDREF_P(retval_ptr); } else { ZVAL_MAKE_REF_EX(retval_ptr, 2); } - ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr)); + ZVAL_REF(return_value, Z_REF_P(retval_ptr)); } } while (0); + ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -21094,6 +21114,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HA retval_ptr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); return_value = EX(return_value); + if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -21157,6 +21178,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HA } + ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -21164,9 +21186,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER { USE_OPLINE zval *retval_ptr; + zval *return_value; SAVE_OPLINE(); + return_value = EX(return_value); + do { if ((IS_VAR & (IS_CONST|IS_TMP_VAR)) || (IS_VAR == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) { @@ -21174,15 +21199,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER zend_error(E_NOTICE, "Only variable references should be returned by reference"); retval_ptr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); - if (!EX(return_value)) { + if (!return_value) { zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); } else { if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) { - ZVAL_COPY_VALUE(EX(return_value), retval_ptr); + ZVAL_COPY_VALUE(return_value, retval_ptr); break; } - ZVAL_NEW_REF(EX(return_value), retval_ptr); + ZVAL_NEW_REF(return_value, retval_ptr); if (IS_VAR == IS_CONST) { Z_TRY_ADDREF_P(retval_ptr); } @@ -21196,8 +21221,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER ZEND_ASSERT(retval_ptr != &EG(uninitialized_zval)); if (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr)) { zend_error(E_NOTICE, "Only variable references should be returned by reference"); - if (EX(return_value)) { - ZVAL_NEW_REF(EX(return_value), retval_ptr); + if (return_value) { + ZVAL_NEW_REF(return_value, retval_ptr); } else { zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); } @@ -21205,18 +21230,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER } } - if (EX(return_value)) { + if (return_value) { if (Z_ISREF_P(retval_ptr)) { Z_ADDREF_P(retval_ptr); } else { ZVAL_MAKE_REF_EX(retval_ptr, 2); } - ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr)); + ZVAL_REF(return_value, Z_REF_P(retval_ptr)); } zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); } while (0); + ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -37626,6 +37652,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HAN retval_ptr = EX_VAR(opline->op1.var); return_value = EX(return_value); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -37689,6 +37716,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HAN } + ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -37696,9 +37724,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER( { USE_OPLINE zval *retval_ptr; + zval *return_value; SAVE_OPLINE(); + return_value = EX(return_value); + do { if ((IS_CV & (IS_CONST|IS_TMP_VAR)) || (IS_CV == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) { @@ -37706,15 +37737,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER( zend_error(E_NOTICE, "Only variable references should be returned by reference"); retval_ptr = _get_zval_ptr_cv_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); - if (!EX(return_value)) { + if (!return_value) { } else { if (IS_CV == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) { - ZVAL_COPY_VALUE(EX(return_value), retval_ptr); + ZVAL_COPY_VALUE(return_value, retval_ptr); break; } - ZVAL_NEW_REF(EX(return_value), retval_ptr); + ZVAL_NEW_REF(return_value, retval_ptr); if (IS_CV == IS_CONST) { Z_TRY_ADDREF_P(retval_ptr); } @@ -37728,8 +37759,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER( ZEND_ASSERT(retval_ptr != &EG(uninitialized_zval)); if (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr)) { zend_error(E_NOTICE, "Only variable references should be returned by reference"); - if (EX(return_value)) { - ZVAL_NEW_REF(EX(return_value), retval_ptr); + if (return_value) { + ZVAL_NEW_REF(return_value, retval_ptr); } else { } @@ -37737,17 +37768,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER( } } - if (EX(return_value)) { + if (return_value) { if (Z_ISREF_P(retval_ptr)) { Z_ADDREF_P(retval_ptr); } else { ZVAL_MAKE_REF_EX(retval_ptr, 2); } - ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr)); + ZVAL_REF(return_value, Z_REF_P(retval_ptr)); } } while (0); + ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } @@ -54693,6 +54725,7 @@ zend_leave_helper_SPEC_LABEL: retval_ptr = RT_CONSTANT(opline, opline->op1); return_value = EX(return_value); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -54756,6 +54789,7 @@ zend_leave_helper_SPEC_LABEL: } + goto zend_leave_helper_SPEC_LABEL; } @@ -54765,9 +54799,11 @@ zend_leave_helper_SPEC_LABEL: USE_OPLINE zval *retval_ptr; zval *return_value; + zval observer_retval; retval_ptr = get_zval_ptr_undef(opline->op1_type, opline->op1, BP_VAR_R); return_value = EX(return_value); + if (!return_value) { return_value = &observer_retval; }; if (opline->op1_type == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -54831,6 +54867,7 @@ zend_leave_helper_SPEC_LABEL: } SAVE_OPLINE(); zend_observer_fcall_end(execute_data, return_value); + if (return_value == &observer_retval) { zval_ptr_dtor_nogc(&observer_retval); }; goto zend_leave_helper_SPEC_LABEL; } @@ -56303,6 +56340,7 @@ zend_leave_helper_SPEC_LABEL: retval_ptr = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); return_value = EX(return_value); + if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -56366,6 +56404,7 @@ zend_leave_helper_SPEC_LABEL: } + goto zend_leave_helper_SPEC_LABEL; } @@ -56602,6 +56641,7 @@ zend_leave_helper_SPEC_LABEL: retval_ptr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); return_value = EX(return_value); + if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -56665,6 +56705,7 @@ zend_leave_helper_SPEC_LABEL: } + goto zend_leave_helper_SPEC_LABEL; } @@ -57717,6 +57758,7 @@ zend_leave_helper_SPEC_LABEL: retval_ptr = EX_VAR(opline->op1.var); return_value = EX(return_value); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = ZVAL_UNDEFINED_OP1(); @@ -57780,6 +57822,7 @@ zend_leave_helper_SPEC_LABEL: } + goto zend_leave_helper_SPEC_LABEL; } diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index f8baea0d05..4958189ce4 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -794,6 +794,9 @@ function gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec=null) ($extra_spec['ISSET'] == 0 ? "\\0" : "opline->extended_value") : "\\0", "/ZEND_OBSERVER_ENABLED/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "1" : "0", + "/ZEND_OBSERVER_USE_RETVAL/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "zval observer_retval" : "", + "/ZEND_OBSERVER_SET_RETVAL\(\)/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "if (!return_value) { return_value = &observer_retval; }" : "", + "/ZEND_OBSERVER_FREE_RETVAL\(\)/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "if (return_value == &observer_retval) { zval_ptr_dtor_nogc(&observer_retval); }" : "", "/ZEND_OBSERVER_SAVE_OPLINE\(\)/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "SAVE_OPLINE()" : "", "/ZEND_OBSERVER_FCALL_BEGIN\(\s*(.*)\s*\)/" => isset($extra_spec['OBSERVER']) ? ($extra_spec['OBSERVER'] == 0 ? "" : "zend_observer_fcall_begin(\\1)") diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index ef6d45b849..b6f848c5b7 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -480,7 +480,14 @@ static void get_retval_info(zval *retval, smart_str *buf) if (retval == NULL) { smart_str_appendl(buf, "NULL", 4); } else if (ZT_G(observer_show_return_value)) { - php_var_export_ex(retval, 2 * ZT_G(observer_nesting_depth) + 3, buf); + if (Z_TYPE_P(retval) == IS_OBJECT) { + smart_str_appendl(buf, "object(", 7); + smart_str_append(buf, Z_OBJCE_P(retval)->name); + smart_str_appendl(buf, ")#", 2); + smart_str_append_long(buf, Z_OBJ_HANDLE_P(retval)); + } else { + php_var_export_ex(retval, 2 * ZT_G(observer_nesting_depth) + 3, buf); + } } else if (ZT_G(observer_show_return_type)) { smart_str_appends(buf, zend_zval_type_name(retval)); } diff --git a/ext/zend_test/tests/observer_retval_01.phpt b/ext/zend_test/tests/observer_retval_01.phpt new file mode 100644 index 0000000000..d58cac807d --- /dev/null +++ b/ext/zend_test/tests/observer_retval_01.phpt @@ -0,0 +1,29 @@ +--TEST-- +Observer: Retvals are observable that are: IS_CONST +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function foo() { + return 'I should be observable'; // IS_CONST +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_%d.php' --> +<file '%s/observer_retval_%d.php'> + <!-- init foo() --> + <foo> + </foo:'I should be observable'> + <foo> + </foo:'I should be observable'> +Done +</file '%s/observer_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_02.phpt b/ext/zend_test/tests/observer_retval_02.phpt new file mode 100644 index 0000000000..6b2e3548a2 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_02.phpt @@ -0,0 +1,32 @@ +--TEST-- +Observer: Unused retvals from generators are still observable +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function foo() { + yield 'I should be observable'; + yield 'Me too!'; +} + +$gen = foo(); +$gen->current(); +$gen->next(); +$gen->current(); + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_%d.php' --> +<file '%s/observer_retval_%d.php'> + <!-- init foo() --> + <foo> + </foo:'I should be observable'> + <foo> + </foo:'Me too!'> +Done +</file '%s/observer_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_03.phpt b/ext/zend_test/tests/observer_retval_03.phpt new file mode 100644 index 0000000000..a21ed97c25 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_03.phpt @@ -0,0 +1,32 @@ +--TEST-- +Observer: Retvals are observable that are: refcounted, IS_CV +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +class MyRetval {} + +function foo() { + $retval = new MyRetval(); // Refcounted + return $retval; // IS_CV +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_%d.php' --> +<file '%s/observer_retval_%d.php'> + <!-- init foo() --> + <foo> + </foo:object(MyRetval)#%d> + <foo> + </foo:object(MyRetval)#%d> +Done +</file '%s/observer_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_04.phpt b/ext/zend_test/tests/observer_retval_04.phpt new file mode 100644 index 0000000000..883dd85498 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_04.phpt @@ -0,0 +1,52 @@ +--TEST-- +Observer: Retvals are observable that are: refcounted, IS_VAR +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +class MyRetval {} + +function getObj() { + return new MyRetval(); // Refcounted +} + +function foo() { + return getObj(); // IS_VAR +} + +$res = foo(); // Retval used +foo(); // Retval unused + +function bar($what) { + return 'This gets ' . $what . ' in the return handler when unused'; // Refcounted + IS_VAR +} + +$res = bar('freed'); // Retval used +bar('freed'); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_%d.php' --> +<file '%s/observer_retval_%d.php'> + <!-- init foo() --> + <foo> + <!-- init getObj() --> + <getObj> + </getObj:object(MyRetval)#%d> + </foo:object(MyRetval)#%d> + <foo> + <getObj> + </getObj:object(MyRetval)#%d> + </foo:object(MyRetval)#%d> + <!-- init bar() --> + <bar> + </bar:'This gets freed in the return handler when unused'> + <bar> + </bar:'This gets freed in the return handler when unused'> +Done +</file '%s/observer_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_05.phpt b/ext/zend_test/tests/observer_retval_05.phpt new file mode 100644 index 0000000000..45fe981f29 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_05.phpt @@ -0,0 +1,33 @@ +--TEST-- +Observer: Retvals are observable that are: IS_CV, IS_UNDEF +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function foo() { + return $i_do_not_exist; // IS_CV && IS_UNDEF +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_%d.php' --> +<file '%s/observer_retval_%d.php'> + <!-- init foo() --> + <foo> + +Warning: Undefined variable $i_do_not_exist in %s on line %d + </foo:NULL> + <foo> + +Warning: Undefined variable $i_do_not_exist in %s on line %d + </foo:NULL> +Done +</file '%s/observer_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_06.phpt b/ext/zend_test/tests/observer_retval_06.phpt new file mode 100644 index 0000000000..f5d2988725 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_06.phpt @@ -0,0 +1,30 @@ +--TEST-- +Observer: Retvals are observable that are: IS_CV +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function foo() { + $retval = 'I should be observable'; + return $retval; // IS_CV +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_%d.php' --> +<file '%s/observer_retval_%d.php'> + <!-- init foo() --> + <foo> + </foo:'I should be observable'> + <foo> + </foo:'I should be observable'> +Done +</file '%s/observer_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_07.phpt b/ext/zend_test/tests/observer_retval_07.phpt new file mode 100644 index 0000000000..abd518b0e4 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_07.phpt @@ -0,0 +1,39 @@ +--TEST-- +Observer: Retvals are observable that are: IS_REFERENCE, IS_VAR +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function &getMessage() { + $retval = 'I should be observable'; + return $retval; +} + +function foo() { + return getMessage(); // IS_REFERENCE + IS_VAR +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_%d.php' --> +<file '%s/observer_retval_%d.php'> + <!-- init foo() --> + <foo> + <!-- init getMessage() --> + <getMessage> + </getMessage:'I should be observable'> + </foo:'I should be observable'> + <foo> + <getMessage> + </getMessage:'I should be observable'> + </foo:'I should be observable'> +Done +</file '%s/observer_retval_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_by_ref_01.phpt b/ext/zend_test/tests/observer_retval_by_ref_01.phpt new file mode 100644 index 0000000000..4e96ab010b --- /dev/null +++ b/ext/zend_test/tests/observer_retval_by_ref_01.phpt @@ -0,0 +1,30 @@ +--TEST-- +Observer: Retvals by reference are observable that are: IS_CV +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function &foo() { + $retval = 'I should be observable'; + return $retval; // IS_CV +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_by_ref_%d.php' --> +<file '%s/observer_retval_by_ref_%d.php'> + <!-- init foo() --> + <foo> + </foo:'I should be observable'> + <foo> + </foo:'I should be observable'> +Done +</file '%s/observer_retval_by_ref_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_by_ref_02.phpt b/ext/zend_test/tests/observer_retval_by_ref_02.phpt new file mode 100644 index 0000000000..b056a80ce7 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_by_ref_02.phpt @@ -0,0 +1,34 @@ +--TEST-- +Observer: Retvals by reference are observable that are: IS_TMP_VAR +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function &foo() { + $retval = 'I should be '; + return $retval . 'observable'; // IS_TMP_VAR +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_by_ref_%d.php' --> +<file '%s/observer_retval_by_ref_%d.php'> + <!-- init foo() --> + <foo> + +Notice: Only variable references should be returned by reference in %s on line %d + </foo:'I should be observable'> + <foo> + +Notice: Only variable references should be returned by reference in %s on line %d + </foo:'I should be observable'> +Done +</file '%s/observer_retval_by_ref_%d.php'> diff --git a/ext/zend_test/tests/observer_retval_by_ref_03.phpt b/ext/zend_test/tests/observer_retval_by_ref_03.phpt new file mode 100644 index 0000000000..50fe23add1 --- /dev/null +++ b/ext/zend_test/tests/observer_retval_by_ref_03.phpt @@ -0,0 +1,42 @@ +--TEST-- +Observer: Retvals by reference are observable that are: IS_VAR, ZEND_RETURNS_FUNCTION +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +function getMessage() { + return 'I should be observable'; +} + +function &foo() { + return getMessage(); // IS_VAR + ZEND_RETURNS_FUNCTION +} + +$res = foo(); // Retval used +foo(); // Retval unused + +echo 'Done' . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_retval_by_ref_%d.php' --> +<file '%s/observer_retval_by_ref_%d.php'> + <!-- init foo() --> + <foo> + <!-- init getMessage() --> + <getMessage> + </getMessage:'I should be observable'> + +Notice: Only variable references should be returned by reference in %s on line %d + </foo:'I should be observable'> + <foo> + <getMessage> + </getMessage:'I should be observable'> + +Notice: Only variable references should be returned by reference in %s on line %d + </foo:'I should be observable'> +Done +</file '%s/observer_retval_by_ref_%d.php'> diff --git a/ext/zend_test/tests/observer_shutdown_01.phpt b/ext/zend_test/tests/observer_shutdown_01.phpt new file mode 100644 index 0000000000..16ea9cef0e --- /dev/null +++ b/ext/zend_test/tests/observer_shutdown_01.phpt @@ -0,0 +1,44 @@ +--TEST-- +Observer: Function calls from a shutdown handler are observable +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +register_shutdown_function(function () { + echo 'Shutdown: ' . foo() . PHP_EOL; +}); + +function bar() { + return 42; +} + +function foo() { + bar(); + return bar(); +} + +echo 'Done: ' . bar() . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_shutdown_%d.php' --> +<file '%s/observer_shutdown_%d.php'> + <!-- init bar() --> + <bar> + </bar:42> +Done: 42 +</file '%s/observer_shutdown_%d.php'> +<!-- init {closure}() --> +<{closure}> + <!-- init foo() --> + <foo> + <bar> + </bar:42> + <bar> + </bar:42> + </foo:42> +Shutdown: 42 +</{closure}:NULL> diff --git a/ext/zend_test/tests/observer_shutdown_02.phpt b/ext/zend_test/tests/observer_shutdown_02.phpt new file mode 100644 index 0000000000..ad6c906585 --- /dev/null +++ b/ext/zend_test/tests/observer_shutdown_02.phpt @@ -0,0 +1,50 @@ +--TEST-- +Observer: Function calls from a __destruct during shutdown are observable +--SKIPIF-- +<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?> +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +<?php +class MyClass +{ + public function __destruct() + { + echo 'Shutdown: ' . foo() . PHP_EOL; + } +} + +function bar() { + return 42; +} + +function foo() { + bar(); + return bar(); +} + +$mc = new MyClass(); + +echo 'Done: ' . bar() . PHP_EOL; +?> +--EXPECTF-- +<!-- init '%s/observer_shutdown_%d.php' --> +<file '%s/observer_shutdown_%d.php'> + <!-- init bar() --> + <bar> + </bar:42> +Done: 42 +</file '%s/observer_shutdown_%d.php'> +<!-- init MyClass::__destruct() --> +<MyClass::__destruct> + <!-- init foo() --> + <foo> + <bar> + </bar:42> + <bar> + </bar:42> + </foo:42> +Shutdown: 42 +</MyClass::__destruct:NULL> |