From c5a5391f5416595e3c35da6e828a964be9953b67 Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Sat, 2 Sep 2023 20:55:54 +0200 Subject: [PATCH 1/3] tests --- Zend/zend_compile.c | 211 +++++++++++++++++++++++++----------- Zend/zend_language_parser.y | 2 + 2 files changed, 152 insertions(+), 61 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 3334851c4f5a5..e31ae776633f0 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5677,89 +5677,178 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */ zend_ast *value_ast = ast->child[1]; zend_ast *key_ast = ast->child[2]; zend_ast *stmt_ast = ast->child[3]; - bool by_ref = value_ast->kind == ZEND_AST_REF; - bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast); - znode expr_node, reset_node, value_node, key_node; - zend_op *opline; - uint32_t opnum_reset, opnum_fetch; + if(!value_ast && !key_ast) { + bool by_ref = value_ast && value_ast->kind == ZEND_AST_REF; + bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast); - if (key_ast) { - if (key_ast->kind == ZEND_AST_REF) { - zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference"); + znode expr_node, reset_node, value_node, key_node; + zend_op * opline; + uint32_t opnum_reset, opnum_fetch; + + if (key_ast) { + if (key_ast->kind == ZEND_AST_REF) { + zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference"); + } + if (key_ast->kind == ZEND_AST_ARRAY) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element"); + } } - if (key_ast->kind == ZEND_AST_ARRAY) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element"); + + if (by_ref) { + value_ast = value_ast->child[0]; } - } - if (by_ref) { - value_ast = value_ast->child[0]; - } + if (value_ast && value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) { + by_ref = 1; + } - if (value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) { - by_ref = 1; - } + if (by_ref && is_variable) { + zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1); + } else { + zend_compile_expr(&expr_node, expr_ast); + } - if (by_ref && is_variable) { - zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1); - } else { - zend_compile_expr(&expr_node, expr_ast); - } + if (by_ref) { + zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W); + } - if (by_ref) { - zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W); - } + opnum_reset = get_next_op_number(); + opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); - opnum_reset = get_next_op_number(); - opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); + zend_begin_loop(ZEND_FE_FREE, &reset_node, 0); - zend_begin_loop(ZEND_FE_FREE, &reset_node, 0); + opnum_fetch = get_next_op_number(); + opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); - opnum_fetch = get_next_op_number(); - opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); + if (value_ast && is_this_fetch(value_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } else if (value_ast && value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { + SET_NODE(opline->op2, &value_node); + } else { + opline->op2_type = IS_VAR; + opline->op2.var = get_temporary_variable(); + GET_NODE(&value_node, opline->op2); + if (value_ast->kind == ZEND_AST_ARRAY) { + zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr); + } else if (by_ref) { + zend_emit_assign_ref_znode(value_ast, &value_node); + } else { + zend_emit_assign_znode(value_ast, &value_node); + } + } - if (is_this_fetch(value_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); - } else if (value_ast->kind == ZEND_AST_VAR && - zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { - SET_NODE(opline->op2, &value_node); + if (key_ast) { + opline = &CG(active_op_array)->opcodes[opnum_fetch]; + zend_make_tmp_result(&key_node, opline); + zend_emit_assign_znode(key_ast, &key_node); + } + + zend_compile_stmt(stmt_ast); + + /* Place JMP and FE_FREE on the line where foreach starts. It would be + * better to use the end line, but this information is not available + * currently. */ + CG(zend_lineno) = ast->lineno; + zend_emit_jump(opnum_fetch); + + opline = &CG(active_op_array)->opcodes[opnum_reset]; + opline->op2.opline_num = get_next_op_number(); + + opline = &CG(active_op_array)->opcodes[opnum_fetch]; + opline->extended_value = get_next_op_number(); + + zend_end_loop(opnum_fetch, &reset_node); + + opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL); } else { - opline->op2_type = IS_VAR; - opline->op2.var = get_temporary_variable(); - GET_NODE(&value_node, opline->op2); - if (value_ast->kind == ZEND_AST_ARRAY) { - zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr); - } else if (by_ref) { - zend_emit_assign_ref_znode(value_ast, &value_node); + bool by_ref = value_ast && value_ast->kind == ZEND_AST_REF; + bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast); + + znode expr_node, reset_node, value_node, key_node; + zend_op * opline; + uint32_t opnum_reset, opnum_fetch; + + if (key_ast) { + if (key_ast->kind == ZEND_AST_REF) { + zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference"); + } + if (key_ast->kind == ZEND_AST_ARRAY) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element"); + } + } + + if (by_ref) { + value_ast = value_ast->child[0]; + } + + if (value_ast && value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) { + by_ref = 1; + } + + if (by_ref && is_variable) { + zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1); } else { - zend_emit_assign_znode(value_ast, &value_node); + zend_compile_expr(&expr_node, expr_ast); } - } - if (key_ast) { - opline = &CG(active_op_array)->opcodes[opnum_fetch]; - zend_make_tmp_result(&key_node, opline); - zend_emit_assign_znode(key_ast, &key_node); - } + if (by_ref) { + zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W); + } - zend_compile_stmt(stmt_ast); + opnum_reset = get_next_op_number(); + opline = zend_emit_op(&reset_node, ZEND_FE_RESET_R, &expr_node, NULL); - /* Place JMP and FE_FREE on the line where foreach starts. It would be - * better to use the end line, but this information is not available - * currently. */ - CG(zend_lineno) = ast->lineno; - zend_emit_jump(opnum_fetch); + zend_begin_loop(ZEND_FE_FREE, &reset_node, 0); - opline = &CG(active_op_array)->opcodes[opnum_reset]; - opline->op2.opline_num = get_next_op_number(); + opnum_fetch = get_next_op_number(); + opline = zend_emit_op(NULL, ZEND_FE_FETCH_R, &reset_node, NULL); + if (value_ast) { + if (is_this_fetch(value_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } else + if (value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { + SET_NODE(opline->op2, &value_node); + } + else { + opline->op2_type = IS_VAR; + opline->op2.var = get_temporary_variable(); + GET_NODE(&value_node, opline->op2); + if (value_ast->kind == ZEND_AST_ARRAY) { + zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr); + } else + if (by_ref) { + zend_emit_assign_ref_znode(value_ast, &value_node); + } else { + zend_emit_assign_znode(value_ast, &value_node); + } + } + } - opline = &CG(active_op_array)->opcodes[opnum_fetch]; - opline->extended_value = get_next_op_number(); + if (key_ast) { + opline = &CG(active_op_array)->opcodes[opnum_fetch]; + zend_make_tmp_result(&key_node, opline); + zend_emit_assign_znode(key_ast, &key_node); + } - zend_end_loop(opnum_fetch, &reset_node); + zend_compile_stmt(stmt_ast); + + /* Place JMP and FE_FREE on the line where foreach starts. It would be + * better to use the end line, but this information is not available + * currently. */ + CG(zend_lineno) = ast->lineno; + zend_emit_jump(opnum_fetch); + + opline = &CG(active_op_array)->opcodes[opnum_reset]; + opline->op2.opline_num = get_next_op_number(); - opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL); + opline = &CG(active_op_array)->opcodes[opnum_fetch]; + opline->extended_value = get_next_op_number(); + + zend_end_loop(opnum_fetch, &reset_node); + + opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL); + } } /* }}} */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 298eaf95ad055..0eb880e946e9e 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -515,6 +515,8 @@ statement: | T_UNSET '(' unset_variables possible_comma ')' ';' { $$ = $3; } | T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement { $$ = zend_ast_create(ZEND_AST_FOREACH, $3, $5, NULL, $7); } + | T_FOREACH '(' expr ')' foreach_statement + { $$ = zend_ast_create(ZEND_AST_FOREACH, $3, NULL, NULL, $5); } | T_FOREACH '(' expr T_AS foreach_variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement { $$ = zend_ast_create(ZEND_AST_FOREACH, $3, $7, $5, $9); } From bba615c6fd6fa54fbad0bf1e5dfdcd66cbcbe0f6 Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Mon, 29 Jan 2024 23:58:22 +0100 Subject: [PATCH 2/3] Implement short foreach --- Zend/tests/short_foreach/short_foreach.phpt | 31 +++++ .../short_foreach_generator.phpt | 27 ++++ .../short_foreach/short_foreach_iterator.phpt | 24 ++++ Zend/zend_compile.c | 118 +++++------------- 4 files changed, 112 insertions(+), 88 deletions(-) create mode 100644 Zend/tests/short_foreach/short_foreach.phpt create mode 100644 Zend/tests/short_foreach/short_foreach_generator.phpt create mode 100644 Zend/tests/short_foreach/short_foreach_iterator.phpt diff --git a/Zend/tests/short_foreach/short_foreach.phpt b/Zend/tests/short_foreach/short_foreach.phpt new file mode 100644 index 0000000000000..a776c7a02f879 --- /dev/null +++ b/Zend/tests/short_foreach/short_foreach.phpt @@ -0,0 +1,31 @@ +--TEST-- +array test +--FILE-- + +--EXPECT-- +aaaacccccccc diff --git a/Zend/tests/short_foreach/short_foreach_generator.phpt b/Zend/tests/short_foreach/short_foreach_generator.phpt new file mode 100644 index 0000000000000..7fe519457e485 --- /dev/null +++ b/Zend/tests/short_foreach/short_foreach_generator.phpt @@ -0,0 +1,27 @@ +--TEST-- +array test +--FILE-- + +--EXPECT-- + diff --git a/Zend/tests/short_foreach/short_foreach_iterator.phpt b/Zend/tests/short_foreach/short_foreach_iterator.phpt new file mode 100644 index 0000000000000..1cf96a565cb38 --- /dev/null +++ b/Zend/tests/short_foreach/short_foreach_iterator.phpt @@ -0,0 +1,24 @@ +--TEST-- +array test +--FILE-- + +--EXPECT-- + + diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e31ae776633f0..92ca5c05843ee 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5677,78 +5677,27 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */ zend_ast *value_ast = ast->child[1]; zend_ast *key_ast = ast->child[2]; zend_ast *stmt_ast = ast->child[3]; + znode expr_node, reset_node, value_node, key_node; + zend_op *opline; + uint32_t opnum_reset, opnum_fetch; - if(!value_ast && !key_ast) { - bool by_ref = value_ast && value_ast->kind == ZEND_AST_REF; - bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast); - - znode expr_node, reset_node, value_node, key_node; - zend_op * opline; - uint32_t opnum_reset, opnum_fetch; - - if (key_ast) { - if (key_ast->kind == ZEND_AST_REF) { - zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference"); - } - if (key_ast->kind == ZEND_AST_ARRAY) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element"); - } - } - - if (by_ref) { - value_ast = value_ast->child[0]; - } - - if (value_ast && value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) { - by_ref = 1; - } - - if (by_ref && is_variable) { - zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1); - } else { - zend_compile_expr(&expr_node, expr_ast); - } - - if (by_ref) { - zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W); - } + if(!value_ast) { + zend_compile_expr(&expr_node, expr_ast); opnum_reset = get_next_op_number(); - opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); + opline = zend_emit_op(&reset_node, ZEND_FE_RESET_R, &expr_node, NULL); zend_begin_loop(ZEND_FE_FREE, &reset_node, 0); opnum_fetch = get_next_op_number(); - opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); - - if (value_ast && is_this_fetch(value_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); - } else if (value_ast && value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { - SET_NODE(opline->op2, &value_node); - } else { - opline->op2_type = IS_VAR; - opline->op2.var = get_temporary_variable(); - GET_NODE(&value_node, opline->op2); - if (value_ast->kind == ZEND_AST_ARRAY) { - zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr); - } else if (by_ref) { - zend_emit_assign_ref_znode(value_ast, &value_node); - } else { - zend_emit_assign_znode(value_ast, &value_node); - } - } + opline = zend_emit_op(NULL, ZEND_FE_FETCH_R, &reset_node, NULL); - if (key_ast) { - opline = &CG(active_op_array)->opcodes[opnum_fetch]; - zend_make_tmp_result(&key_node, opline); - zend_emit_assign_znode(key_ast, &key_node); - } + opline->op2_type = IS_VAR; + opline->op2.var = get_temporary_variable(); + GET_NODE(&value_node, opline->op2); zend_compile_stmt(stmt_ast); - /* Place JMP and FE_FREE on the line where foreach starts. It would be - * better to use the end line, but this information is not available - * currently. */ CG(zend_lineno) = ast->lineno; zend_emit_jump(opnum_fetch); @@ -5761,14 +5710,11 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */ zend_end_loop(opnum_fetch, &reset_node); opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL); + } else { - bool by_ref = value_ast && value_ast->kind == ZEND_AST_REF; + bool by_ref = value_ast->kind == ZEND_AST_REF; bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast); - znode expr_node, reset_node, value_node, key_node; - zend_op * opline; - uint32_t opnum_reset, opnum_fetch; - if (key_ast) { if (key_ast->kind == ZEND_AST_REF) { zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference"); @@ -5782,7 +5728,7 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */ value_ast = value_ast->child[0]; } - if (value_ast && value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) { + if (value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) { by_ref = 1; } @@ -5797,31 +5743,27 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */ } opnum_reset = get_next_op_number(); - opline = zend_emit_op(&reset_node, ZEND_FE_RESET_R, &expr_node, NULL); + opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); zend_begin_loop(ZEND_FE_FREE, &reset_node, 0); opnum_fetch = get_next_op_number(); - opline = zend_emit_op(NULL, ZEND_FE_FETCH_R, &reset_node, NULL); - if (value_ast) { - if (is_this_fetch(value_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); - } else - if (value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { - SET_NODE(opline->op2, &value_node); - } - else { - opline->op2_type = IS_VAR; - opline->op2.var = get_temporary_variable(); - GET_NODE(&value_node, opline->op2); - if (value_ast->kind == ZEND_AST_ARRAY) { - zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr); - } else - if (by_ref) { - zend_emit_assign_ref_znode(value_ast, &value_node); - } else { - zend_emit_assign_znode(value_ast, &value_node); - } + opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); + if (is_this_fetch(value_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } else if (value_ast->kind == ZEND_AST_VAR && + zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { + SET_NODE(opline->op2, &value_node); + } else { + opline->op2_type = IS_VAR; + opline->op2.var = get_temporary_variable(); + GET_NODE(&value_node, opline->op2); + if (value_ast->kind == ZEND_AST_ARRAY) { + zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr); + } else if (by_ref) { + zend_emit_assign_ref_znode(value_ast, &value_node); + } else { + zend_emit_assign_znode(value_ast, &value_node); } } From 41338b5102ce9f9344d01f672bfd5b58c53d2708 Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Fri, 9 Feb 2024 00:32:43 +0100 Subject: [PATCH 3/3] Fixed generator test --- Zend/tests/short_foreach/short_foreach_generator.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/tests/short_foreach/short_foreach_generator.phpt b/Zend/tests/short_foreach/short_foreach_generator.phpt index 7fe519457e485..ff5b3167adc32 100644 --- a/Zend/tests/short_foreach/short_foreach_generator.phpt +++ b/Zend/tests/short_foreach/short_foreach_generator.phpt @@ -12,7 +12,7 @@ foreach(generator()) { } function generator2() { - for($i=0;$i<0;$i++) { + for($i=0;$i<4;$i++) { yield $i; } } @@ -24,4 +24,4 @@ foreach(generator2()) { ?> --EXPECT-- - +aaaabbbb