diff options
-rw-r--r-- | Zend/tests/generators/send_returns_current.phpt | 2 | ||||
-rw-r--r-- | Zend/tests/generators/yield_array_offset_by_ref.phpt | 26 | ||||
-rw-r--r-- | Zend/tests/generators/yield_in_parenthesis.phpt | 23 | ||||
-rw-r--r-- | Zend/zend_compile.c | 12 | ||||
-rw-r--r-- | Zend/zend_compile.h | 2 | ||||
-rw-r--r-- | Zend/zend_language_parser.y | 91 |
6 files changed, 109 insertions, 47 deletions
diff --git a/Zend/tests/generators/send_returns_current.phpt b/Zend/tests/generators/send_returns_current.phpt index fc260c0af0..27ba74bc1b 100644 --- a/Zend/tests/generators/send_returns_current.phpt +++ b/Zend/tests/generators/send_returns_current.phpt @@ -6,7 +6,7 @@ $generator->send() returns the yielded value function reverseEchoGenerator() { $data = yield; while (true) { - $data = yield strrev($data); + $data = (yield strrev($data)); } } diff --git a/Zend/tests/generators/yield_array_offset_by_ref.phpt b/Zend/tests/generators/yield_array_offset_by_ref.phpt new file mode 100644 index 0000000000..544108e64d --- /dev/null +++ b/Zend/tests/generators/yield_array_offset_by_ref.phpt @@ -0,0 +1,26 @@ +--TEST-- +Array offsets can be yielded by reference +--FILE-- +<?php + +function &gen(array &$array) { + yield $array[0]; +} + +$array = [1, 2, 3]; +$gen = gen($array); +foreach ($gen as &$val) { + $val *= -1; +} +var_dump($array); + +?> +--EXPECT-- +array(3) { + [0]=> + &int(-1) + [1]=> + int(2) + [2]=> + int(3) +} diff --git a/Zend/tests/generators/yield_in_parenthesis.phpt b/Zend/tests/generators/yield_in_parenthesis.phpt new file mode 100644 index 0000000000..4a603f4cc1 --- /dev/null +++ b/Zend/tests/generators/yield_in_parenthesis.phpt @@ -0,0 +1,23 @@ +--TEST-- +No additional parenthesis are required around yield if they are already present +--FILE-- +<?php + +function gen() { + if (yield $foo); elseif (yield $foo); + if (yield $foo): elseif (yield $foo): endif; + while (yield $foo); + do {} while (yield $foo); + switch (yield $foo) {} + (yield $foo); + die(yield $foo); + func(yield $foo); + $foo->func(yield $foo); + new Foo(yield $foo); +} + +echo "Done"; + +?> +--EXPECT-- +Done diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e030f94c78..51fc8c3e5b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2663,7 +2663,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC) /* {{{ */ +void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -2673,6 +2673,14 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR; + if (is_variable) { + if ((CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !zend_is_function_or_method_call(value)) { + zend_do_end_variable_parse(value, BP_VAR_W, 0 TSRMLS_CC); + } else { + zend_do_end_variable_parse(value, BP_VAR_R, 0 TSRMLS_CC); + } + } + opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_YIELD; @@ -2680,7 +2688,7 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC if (value) { SET_NODE(opline->op1, value); - if (zend_is_function_or_method_call(value)) { + if (is_variable && zend_is_function_or_method_call(value)) { opline->extended_value = ZEND_RETURNS_FUNCTION; } } else { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index c4355e0dde..1972f85c66 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -490,7 +490,7 @@ void zend_do_build_full_name(znode *result, znode *prefix, znode *name, int is_c int zend_do_begin_class_member_function_call(znode *class_name, znode *method_name TSRMLS_DC); void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); -void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC); +void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC); void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC); void zend_do_handle_exception(TSRMLS_D); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index b705d60d9a..e5f31b5d14 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -278,10 +278,10 @@ statement: unticked_statement: '{' inner_statement_list '}' - | T_IF '(' expr ')' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } statement { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); } - | T_IF '(' expr ')' ':' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); } - | T_WHILE '(' { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' { zend_do_while_cond(&$4, &$5 TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$5 TSRMLS_CC); } - | T_DO { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_do_while_begin(TSRMLS_C); } statement T_WHILE '(' { $5.u.op.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' ';' { zend_do_do_while_end(&$1, &$5, &$7 TSRMLS_CC); } + | T_IF parenthesis_expr { zend_do_if_cond(&$2, &$1 TSRMLS_CC); } statement { zend_do_if_after_statement(&$1, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); } + | T_IF parenthesis_expr ':' { zend_do_if_cond(&$2, &$1 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$1, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); } + | T_WHILE { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); } parenthesis_expr { zend_do_while_cond(&$3, &$$ TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$4 TSRMLS_CC); } + | T_DO { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_do_while_begin(TSRMLS_C); } statement T_WHILE { $4.u.op.opline_num = get_next_op_number(CG(active_op_array)); } parenthesis_expr ';' { zend_do_do_while_end(&$1, &$4, &$6 TSRMLS_CC); } | T_FOR '(' for_expr @@ -291,7 +291,7 @@ unticked_statement: for_expr ')' { zend_do_free(&$9 TSRMLS_CC); zend_do_for_before_statement(&$4, &$7 TSRMLS_CC); } for_statement { zend_do_for_end(&$7 TSRMLS_CC); } - | T_SWITCH '(' expr ')' { zend_do_switch_cond(&$3 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$6 TSRMLS_CC); } + | T_SWITCH parenthesis_expr { zend_do_switch_cond(&$2 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$4 TSRMLS_CC); } | T_BREAK ';' { zend_do_brk_cont(ZEND_BRK, NULL TSRMLS_CC); } | T_BREAK expr ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } | T_CONTINUE ';' { zend_do_brk_cont(ZEND_CONT, NULL TSRMLS_CC); } @@ -299,7 +299,7 @@ unticked_statement: | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } - | T_YIELD expr T_DOUBLE_ARROW expr ';' { zend_do_yield(&$$, &$4, &$2 TSRMLS_CC); } + | yield_expr ';' { $$ = $1; } | T_GLOBAL global_var_list ';' | T_STATIC static_var_list ';' | T_ECHO echo_expr_list ';' @@ -484,13 +484,13 @@ while_statement: elseif_list: /* empty */ - | elseif_list T_ELSEIF '(' expr ')' { zend_do_if_cond(&$4, &$5 TSRMLS_CC); } statement { zend_do_if_after_statement(&$5, 0 TSRMLS_CC); } + | elseif_list T_ELSEIF parenthesis_expr { zend_do_if_cond(&$3, &$2 TSRMLS_CC); } statement { zend_do_if_after_statement(&$2, 0 TSRMLS_CC); } ; new_elseif_list: /* empty */ - | new_elseif_list T_ELSEIF '(' expr ')' ':' { zend_do_if_cond(&$4, &$5 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$5, 0 TSRMLS_CC); } + | new_elseif_list T_ELSEIF parenthesis_expr ':' { zend_do_if_cond(&$3, &$2 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$2, 0 TSRMLS_CC); } ; @@ -533,8 +533,9 @@ optional_class_type: function_call_parameter_list: - non_empty_function_call_parameter_list { $$ = $1; } - | /* empty */ { Z_LVAL($$.u.constant) = 0; } + '(' ')' { Z_LVAL($$.u.constant) = 0; } + | '(' non_empty_function_call_parameter_list ')' { $$ = $2; } + | '(' yield_expr ')' { Z_LVAL($$.u.constant) = 1; zend_do_pass_param(&$2, ZEND_SEND_VAL, Z_LVAL($$.u.constant) TSRMLS_CC); } ; @@ -774,7 +775,7 @@ expr_without_variable: | expr '>' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 TSRMLS_CC); } | expr T_IS_GREATER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1 TSRMLS_CC); } | expr T_INSTANCEOF class_name_reference { zend_do_instanceof(&$$, &$1, &$3, 0 TSRMLS_CC); } - | '(' expr ')' { $$ = $2; } + | parenthesis_expr { $$ = $1; } | new_expr { $$ = $1; } | '(' new_expr ')' { $$ = $2; } instance_call { $$ = $5; } | expr '?' { zend_do_begin_qm_op(&$1, &$2 TSRMLS_CC); } @@ -797,8 +798,9 @@ expr_without_variable: | combined_scalar { $$ = $1; } | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } - | T_YIELD { zend_do_yield(&$$, NULL, NULL TSRMLS_CC); } - | T_YIELD expr { zend_do_yield(&$$, &$2, NULL TSRMLS_CC); } + | T_YIELD { zend_do_yield(&$$, NULL, NULL, 0 TSRMLS_CC); } + /*| T_YIELD expr_without_variable { zend_do_yield(&$$, &$2, NULL, 0 TSRMLS_CC); } + | T_YIELD variable { zend_do_yield(&$$, &$2, NULL, 1 TSRMLS_CC); }*/ | T_YIELD '*' expr { zend_do_delegate_yield(&$$, &$3 TSRMLS_CC); } | function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); } '(' parameter_list ')' lexical_vars @@ -808,6 +810,13 @@ expr_without_variable: '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $4; } ; +yield_expr: + T_YIELD expr_without_variable { zend_do_yield(&$$, &$2, NULL, 0 TSRMLS_CC); } + | T_YIELD variable { zend_do_yield(&$$, &$2, NULL, 1 TSRMLS_CC); } + | T_YIELD expr T_DOUBLE_ARROW expr_without_variable { zend_do_yield(&$$, &$4, &$2, 0 TSRMLS_CC); } + | T_YIELD expr T_DOUBLE_ARROW variable { zend_do_yield(&$$, &$4, &$2, 1 TSRMLS_CC); } +; + combined_scalar_offset: combined_scalar '[' dim_offset ']' { zend_do_begin_variable_parse(TSRMLS_C); fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); } | combined_scalar_offset '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); } @@ -834,30 +843,22 @@ lexical_var_list: ; function_call: - namespace_name '(' { $2.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(&$1, &$$, &$4, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | T_NAMESPACE T_NS_SEPARATOR namespace_name '(' { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant); zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $4.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(&$1, &$$, &$6, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | T_NS_SEPARATOR namespace_name '(' { $3.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(&$2, &$$, &$5, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { $4.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, &$6, $4.u.op.opline_num, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | variable_without_objects '(' { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); } - function_call_parameter_list ')' - { zend_do_end_function_call(&$1, &$$, &$4, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$3, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } + | T_NAMESPACE T_NS_SEPARATOR namespace_name { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant); zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $$.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$5, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } + | T_NS_SEPARATOR namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$2, &$$, &$4, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } + | class_name T_PAAMAYIM_NEKUDOTAYIM variable_name { $$.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, &$5, $4.u.op.opline_num, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_name { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | variable_without_objects { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$3, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} ; class_name: @@ -902,7 +903,7 @@ dynamic_class_name_variable_property: exit_expr: /* empty */ { memset(&$$, 0, sizeof(znode)); $$.op_type = IS_UNUSED; } | '(' ')' { memset(&$$, 0, sizeof(znode)); $$.op_type = IS_UNUSED; } - | '(' expr ')' { $$ = $2; } + | parenthesis_expr { $$ = $1; } ; backticks_expr: @@ -913,8 +914,8 @@ backticks_expr: ctor_arguments: - /* empty */ { Z_LVAL($$.u.constant)=0; } - | '(' function_call_parameter_list ')' { $$ = $2; } + /* empty */ { Z_LVAL($$.u.constant) = 0; } + | function_call_parameter_list { $$ = $1; } ; @@ -986,6 +987,11 @@ expr: | expr_without_variable { $$ = $1; } ; +parenthesis_expr: + '(' expr ')' { $$ = $2; } + | '(' yield_expr ')' { $$ = $2; } +; + r_variable: variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; } @@ -1025,9 +1031,8 @@ array_method_dereference: ; method: - '(' { zend_do_pop_object(&$1 TSRMLS_CC); zend_do_begin_method_call(&$1 TSRMLS_CC); } - function_call_parameter_list ')' - { zend_do_end_function_call(&$1, &$$, &$3, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } + { zend_do_pop_object(&$$ TSRMLS_CC); zend_do_begin_method_call(&$$ TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$2, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } ; method_or_not: |