diff options
author | Nikita Popov <nikic@php.net> | 2015-02-19 20:17:37 +0100 |
---|---|---|
committer | Nikita Popov <nikic@php.net> | 2015-02-19 20:25:49 +0100 |
commit | 79f26e9ca562cc4e692913432299cb9bbf906478 (patch) | |
tree | d333901d478f18e628d9cd1884c097e69fe64053 /Zend | |
parent | d428bf2d4edab232690b64977aaf0a3b15585843 (diff) | |
download | php-git-79f26e9ca562cc4e692913432299cb9bbf906478.tar.gz |
Implement Generator::getReturn()
Diffstat (limited to 'Zend')
-rw-r--r-- | Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt | 13 | ||||
-rw-r--r-- | Zend/tests/generators/errors/generator_cannot_return_error.phpt | 13 | ||||
-rw-r--r-- | Zend/tests/generators/get_return.phpt | 81 | ||||
-rw-r--r-- | Zend/tests/generators/get_return_and_finally.phpt | 47 | ||||
-rw-r--r-- | Zend/tests/generators/get_return_errors.phpt | 79 | ||||
-rw-r--r-- | Zend/tests/generators/get_return_types.phpt | 59 | ||||
-rw-r--r-- | Zend/zend_generators.c | 35 | ||||
-rw-r--r-- | Zend/zend_generators.h | 2 | ||||
-rw-r--r-- | Zend/zend_opcode.c | 5 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 42 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 225 |
11 files changed, 526 insertions, 75 deletions
diff --git a/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt b/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt deleted file mode 100644 index ad618d20ba..0000000000 --- a/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt +++ /dev/null @@ -1,13 +0,0 @@ ---TEST-- -Generators cannot return values (even before yield) ---FILE-- -<?php - -function gen() { - return $foo; - yield; -} - -?> ---EXPECTF-- -Fatal error: Generators cannot return values using "return" in %s on line 4 diff --git a/Zend/tests/generators/errors/generator_cannot_return_error.phpt b/Zend/tests/generators/errors/generator_cannot_return_error.phpt deleted file mode 100644 index 51149062a7..0000000000 --- a/Zend/tests/generators/errors/generator_cannot_return_error.phpt +++ /dev/null @@ -1,13 +0,0 @@ ---TEST-- -Generators cannot return values ---FILE-- -<?php - -function gen() { - yield; - return $abc; -} - -?> ---EXPECTF-- -Fatal error: Generators cannot return values using "return" in %s on line 5 diff --git a/Zend/tests/generators/get_return.phpt b/Zend/tests/generators/get_return.phpt new file mode 100644 index 0000000000..c996eb4101 --- /dev/null +++ b/Zend/tests/generators/get_return.phpt @@ -0,0 +1,81 @@ +--TEST-- +Generator::getReturn() success cases +--FILE-- +<?php + +function gen1() { + return 42; + yield 24; +} + +$gen = gen1(); +// Calling getReturn() directly here is okay due to auto-priming +var_dump($gen->getReturn()); + +function gen2() { + yield 24; + return 42; +} + +$gen = gen2(); +var_dump($gen->current()); +$gen->next(); +var_dump($gen->getReturn()); + +// & for generators specifies by-reference yield, not return +// so it's okay to return a literal +function &gen3() { + $var = 24; + yield $var; + return 42; +} + +$gen = gen3(); +var_dump($gen->current()); +$gen->next(); +var_dump($gen->getReturn()); + +// Return types for generators specify the return of the function, +// not of the generator return value, so this code is okay +function gen4() : Generator { + yield 24; + return 42; +} + +$gen = gen4(); +var_dump($gen->current()); +$gen->next(); +var_dump($gen->getReturn()); + +// Has no explicit return, but implicitly return NULL at the end +function gen5() { + yield 24; +} + +$gen = gen5(); +var_dump($gen->current()); +$gen->next(); +var_dump($gen->getReturn()); + +// Explicit value-less return also results in a NULL generator +// return value and there is no interference with type hints +function gen6() : Generator { + return; + yield 24; +} + +$gen = gen6(); +var_dump($gen->getReturn()); + +?> +--EXPECTF-- +int(42) +int(24) +int(42) +int(24) +int(42) +int(24) +int(42) +int(24) +NULL +NULL diff --git a/Zend/tests/generators/get_return_and_finally.phpt b/Zend/tests/generators/get_return_and_finally.phpt new file mode 100644 index 0000000000..150e5b83c4 --- /dev/null +++ b/Zend/tests/generators/get_return_and_finally.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test interaction of Generator::getReturn() and finally +--FILE-- +<?php + +function gen1() { + try { + throw new Exception("gen1() throw"); + } finally { + return 42; + } + yield; +} + +// The exception was discarded, so this works +$gen = gen1(); +var_dump($gen->getReturn()); + +function gen2() { + try { + return 42; + } finally { + throw new Exception("gen2() throw"); + } + yield; +} + +$gen = gen2(); +try { + // This will throw an exception (from the finally) + // during auto-priming, so fails + var_dump($gen->getReturn()); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} +try { + // This fails, because the return value was discarded + var_dump($gen->getReturn()); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +int(42) +gen2() throw +Cannot get return value of a generator that hasn't returned diff --git a/Zend/tests/generators/get_return_errors.phpt b/Zend/tests/generators/get_return_errors.phpt new file mode 100644 index 0000000000..f8d50aa3de --- /dev/null +++ b/Zend/tests/generators/get_return_errors.phpt @@ -0,0 +1,79 @@ +--TEST-- +Generator::getReturn() error cases +--FILE-- +<?php + +function gen1() { + yield 1; + yield 2; + return 3; +} + +$gen = gen1(); +try { + // Generator hasn't reached the "return" yet + $gen->getReturn(); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +function gen2() { + throw new Exception("gen2() throw"); + yield 1; + return 2; +} + +$gen = gen2(); +try { + $gen->next(); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} +try { + // Generator has been aborted as a result of an exception + $gen->getReturn(); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +function gen3() { + throw new Exception("gen3() throw"); + return 1; + yield 2; +} + +$gen = gen3(); +try { + // Generator throws during auto-priming of getReturn() call + $gen->getReturn(); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +function gen4() { + yield; + return 1; +} + +$gen = gen4(); +try { + $gen->throw(new Exception("gen4() throw")); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} +try { + // Generator has been aborted as a result of an exception + // that was injected using throw() + $gen->getReturn(); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Cannot get return value of a generator that hasn't returned +gen2() throw +Cannot get return value of a generator that hasn't returned +gen3() throw +gen4() throw +Cannot get return value of a generator that hasn't returned diff --git a/Zend/tests/generators/get_return_types.phpt b/Zend/tests/generators/get_return_types.phpt new file mode 100644 index 0000000000..fa7f286814 --- /dev/null +++ b/Zend/tests/generators/get_return_types.phpt @@ -0,0 +1,59 @@ +--TEST-- +Test different types of generator return values (VM operands) +--FILE-- +<?php + +function gen1() { + return; // CONST + yield; +} + +$gen = gen1(); +var_dump($gen->getReturn()); + +function gen2() { + return "str"; // CONST + yield; +} + +$gen = gen2(); +var_dump($gen->getReturn()); + +function gen3($var) { + return $var; // CV + yield; +} + +$gen = gen3([1, 2, 3]); +var_dump($gen->getReturn()); + +function gen4($obj) { + return $obj->prop; // VAR + yield; +} + +$gen = gen4((object) ['prop' => 321]); +var_dump($gen->getReturn()); + +function gen5($val) { + return (int) $val; // TMP + yield; +} + +$gen = gen5("42"); +var_dump($gen->getReturn()); + +?> +--EXPECT-- +NULL +string(3) "str" +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +int(321) +int(42) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 971d4e7bc8..7ebbca1030 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -186,6 +186,10 @@ static void zend_generator_free_storage(zend_object *object) /* {{{ */ zend_generator_close(generator, 0); + if (!Z_ISUNDEF(generator->retval)) { + zval_ptr_dtor(&generator->retval); + } + zend_object_std_dtor(&generator->std); if (generator->iterator) { @@ -204,6 +208,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; + ZVAL_UNDEF(&generator->retval); + zend_object_std_init(&generator->std, class_type); generator->std.handlers = &zend_generator_handlers; @@ -535,6 +541,34 @@ ZEND_METHOD(Generator, throw) } /* }}} */ +/* {{{ proto mixed Generator::getReturn() + * Retrieves the return value of the generator */ +ZEND_METHOD(Generator, getReturn) +{ + zend_generator *generator; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + generator = (zend_generator *) Z_OBJ_P(getThis()); + + zend_generator_ensure_initialized(generator); + if (EG(exception)) { + return; + } + + if (Z_ISUNDEF(generator->retval)) { + /* Generator hasn't returned yet -> error! */ + zend_throw_exception(NULL, + "Cannot get return value of a generator that hasn't returned", 0); + return; + } + + ZVAL_COPY(return_value, &generator->retval); +} +/* }}} */ + /* {{{ proto void Generator::__wakeup() * Throws an Exception as generators can't be serialized */ ZEND_METHOD(Generator, __wakeup) @@ -668,6 +702,7 @@ static const zend_function_entry generator_functions[] = { ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC) ZEND_ME(Generator, throw, arginfo_generator_throw, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, getReturn,arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 91408ea4b1..2e70bc5a2f 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -40,6 +40,8 @@ typedef struct _zend_generator { zval value; /* Current key */ zval key; + /* Return value */ + zval retval; /* Variable to put sent value into */ zval *send_target; /* Largest used integer key for auto-incrementing keys */ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index c1e3adb3ec..e44e586d9c 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -780,11 +780,6 @@ ZEND_API int pass_two(zend_op_array *op_array) case ZEND_RETURN: case ZEND_RETURN_BY_REF: if (op_array->fn_flags & ZEND_ACC_GENERATOR) { - if (opline->op1_type != IS_CONST || Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) != IS_NULL) { - CG(zend_lineno) = opline->lineno; - zend_error_noreturn(E_COMPILE_ERROR, "Generators cannot return values using \"return\""); - } - opline->opcode = ZEND_GENERATOR_RETURN; } break; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 0285f77038..76d7f4774c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3076,11 +3076,36 @@ ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY) ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } -ZEND_VM_HANDLER(161, ZEND_GENERATOR_RETURN, ANY, ANY) +ZEND_VM_HANDLER(161, ZEND_GENERATOR_RETURN, CONST|TMP|VAR|CV, ANY) { + USE_OPLINE + zval *retval; + zend_free_op free_op1; + /* The generator object is stored in EX(return_value) */ zend_generator *generator = (zend_generator *) EX(return_value); + SAVE_OPLINE(); + retval = GET_OP1_ZVAL_PTR(BP_VAR_R); + + /* Copy return value into generator->retval */ + if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (OP1_TYPE == IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) { + zval_copy_ctor_func(&generator->retval); + } + } + } else if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && Z_ISREF_P(retval)) { + ZVAL_COPY(&generator->retval, Z_REFVAL_P(retval)); + FREE_OP1_IF_VAR(); + } else { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (OP1_TYPE == IS_CV) { + if (Z_OPT_REFCOUNTED_P(retval)) Z_ADDREF_P(retval); + } + } + /* Close the generator to free up resources */ zend_generator_close(generator, 1); @@ -6085,7 +6110,10 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]); ZEND_VM_CONTINUE(); } else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) { - ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN); + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + zend_generator_close(generator, 1); + ZEND_VM_RETURN(); } else { ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } @@ -6116,7 +6144,10 @@ ZEND_VM_HANDLER(150, ZEND_USER_OPCODE, ANY, ANY) ZEND_VM_CONTINUE(); case ZEND_USER_OPCODE_RETURN: if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) { - ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN); + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + zend_generator_close(generator, 1); + ZEND_VM_RETURN(); } else { ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } @@ -6400,7 +6431,10 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, ANY) ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->op2.opline_num]); ZEND_VM_CONTINUE(); } else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) { - ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN); + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + zend_generator_close(generator, 1); + ZEND_VM_RETURN(); } else { ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 1e1f62e16e..8e7094fed7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -675,18 +675,6 @@ fcall_end: ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - /* The generator object is stored in EX(return_value) */ - zend_generator *generator = (zend_generator *) EX(return_value); - - /* Close the generator to free up resources */ - zend_generator_close(generator, 1); - - /* Pass execution back to handling code */ - ZEND_VM_RETURN(); -} - static int ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -1396,7 +1384,10 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]); ZEND_VM_CONTINUE(); } else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) { - return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + zend_generator_close(generator, 1); + ZEND_VM_RETURN(); } else { return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } @@ -1427,7 +1418,10 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS ZEND_VM_CONTINUE(); case ZEND_USER_OPCODE_RETURN: if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) { - return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + zend_generator_close(generator, 1); + ZEND_VM_RETURN(); } else { return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } @@ -1502,7 +1496,10 @@ static int ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->op2.opline_num]); ZEND_VM_CONTINUE(); } else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) { - return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + zend_generator_close(generator, 1); + ZEND_VM_RETURN(); } else { return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } @@ -2608,6 +2605,43 @@ static int ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *retval; + + + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + + SAVE_OPLINE(); + retval = EX_CONSTANT(opline->op1); + + /* Copy return value into generator->retval */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (IS_CONST == IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) { + zval_copy_ctor_func(&generator->retval); + } + } + } else if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && Z_ISREF_P(retval)) { + ZVAL_COPY(&generator->retval, Z_REFVAL_P(retval)); + + } else { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (IS_CONST == IS_CV) { + if (Z_OPT_REFCOUNTED_P(retval)) Z_ADDREF_P(retval); + } + } + + /* Close the generator to free up resources */ + zend_generator_close(generator, 1); + + /* Pass execution back to handling code */ + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_THROW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8878,6 +8912,43 @@ static int ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLE return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *retval; + zend_free_op free_op1; + + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + + SAVE_OPLINE(); + retval = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + + /* Copy return value into generator->retval */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (IS_TMP_VAR == IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) { + zval_copy_ctor_func(&generator->retval); + } + } + } else if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && Z_ISREF_P(retval)) { + ZVAL_COPY(&generator->retval, Z_REFVAL_P(retval)); + + } else { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (IS_TMP_VAR == IS_CV) { + if (Z_OPT_REFCOUNTED_P(retval)) Z_ADDREF_P(retval); + } + } + + /* Close the generator to free up resources */ + zend_generator_close(generator, 1); + + /* Pass execution back to handling code */ + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_THROW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -11555,6 +11626,43 @@ static int ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLE return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *retval; + zend_free_op free_op1; + + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + + SAVE_OPLINE(); + retval = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + + /* Copy return value into generator->retval */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (IS_VAR == IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) { + zval_copy_ctor_func(&generator->retval); + } + } + } else if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && Z_ISREF_P(retval)) { + ZVAL_COPY(&generator->retval, Z_REFVAL_P(retval)); + zval_ptr_dtor_nogc(free_op1); + } else { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (IS_VAR == IS_CV) { + if (Z_OPT_REFCOUNTED_P(retval)) Z_ADDREF_P(retval); + } + } + + /* Close the generator to free up resources */ + zend_generator_close(generator, 1); + + /* Pass execution back to handling code */ + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_THROW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -23874,6 +23982,43 @@ static int ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *retval; + + + /* The generator object is stored in EX(return_value) */ + zend_generator *generator = (zend_generator *) EX(return_value); + + SAVE_OPLINE(); + retval = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + + /* Copy return value into generator->retval */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (IS_CV == IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) { + zval_copy_ctor_func(&generator->retval); + } + } + } else if ((IS_CV == IS_CV || IS_CV == IS_VAR) && Z_ISREF_P(retval)) { + ZVAL_COPY(&generator->retval, Z_REFVAL_P(retval)); + + } else { + ZVAL_COPY_VALUE(&generator->retval, retval); + if (IS_CV == IS_CV) { + if (Z_OPT_REFCOUNTED_P(retval)) Z_ADDREF_P(retval); + } + } + + /* Close the generator to free up resources */ + zend_generator_close(generator, 1); + + /* Pass execution back to handling code */ + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_THROW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -40838,31 +40983,31 @@ void zend_init_opcodes_handlers(void) ZEND_YIELD_SPEC_CV_VAR_HANDLER, ZEND_YIELD_SPEC_CV_UNUSED_HANDLER, ZEND_YIELD_SPEC_CV_CV_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, - ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER, ZEND_FAST_CALL_SPEC_HANDLER, ZEND_FAST_CALL_SPEC_HANDLER, ZEND_FAST_CALL_SPEC_HANDLER, |