From: Nikita Popov Date: Fri, 11 Jul 2014 10:16:21 +0000 (+0200) Subject: Port foreach X-Git-Tag: POST_AST_MERGE^2~155 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f12c1482a3b1785fc319b950c95e36a225f8ee9b;p=php Port foreach --- diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 2f07cf3b42..3a956717ab 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -80,6 +80,8 @@ enum _zend_ast_kind { ZEND_AST_WHILE, ZEND_AST_DO_WHILE, ZEND_AST_FOR, + ZEND_AST_FOREACH, + ZEND_AST_REF, ZEND_AST_IF, ZEND_AST_IF_ELEM, }; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0556707ca8..8de27b4430 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7164,6 +7164,118 @@ void zend_compile_for(zend_ast *ast TSRMLS_DC) { do_end_loop(opnum_loop, 0 TSRMLS_CC); } +void zend_compile_foreach(zend_ast *ast TSRMLS_DC) { + zend_ast *expr_ast = ast->child[0]; + zend_ast *value_ast = ast->child[1]; + zend_ast *key_ast = ast->child[2]; + zend_ast *stmt_ast = ast->child[3]; + zend_bool by_ref = value_ast->kind == ZEND_AST_REF; + zend_bool is_variable = zend_is_variable(expr_ast) && !zend_is_call(expr_ast) + && zend_can_write_to_variable(expr_ast); + + znode expr_node, reset_node, value_node, key_node, dummy_node; + zend_op *opline; + zend_uint opnum_reset, opnum_fetch; + zend_op foreach_stack_opline; + + 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_LIST) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element"); + } + } + + if (by_ref) { + value_ast = value_ast->child[0]; + } + + if (by_ref && is_variable) { + zend_compile_var(&expr_node, expr_ast, BP_VAR_W TSRMLS_CC); + } else { + zend_compile_expr(&expr_node, expr_ast TSRMLS_CC); + } + + opnum_reset = get_next_op_number(CG(active_op_array)); + opline = emit_op(&reset_node, ZEND_FE_RESET, &expr_node, NULL TSRMLS_CC); + if (by_ref && is_variable) { + opline->extended_value = ZEND_FE_RESET_VARIABLE; // ??? + } + + SET_NODE(foreach_stack_opline.result, &reset_node); + zend_stack_push(&CG(foreach_copy_stack), &foreach_stack_opline); + + opnum_fetch = get_next_op_number(CG(active_op_array)); + opline = emit_op(&value_node, ZEND_FE_FETCH, &reset_node, NULL TSRMLS_CC); + if (by_ref) { + opline->extended_value |= ZEND_FE_FETCH_BYREF; + } + if (key_ast) { + opline->extended_value |= ZEND_FE_FETCH_WITH_KEY; + } + + opline = emit_op(NULL, ZEND_OP_DATA, NULL, NULL TSRMLS_CC); + + /* Allocate enough space to keep HashPointer on VM stack */ + opline->op1_type = IS_TMP_VAR; + opline->op1.var = get_temporary_variable(CG(active_op_array)); + if (sizeof(HashPointer) > sizeof(zval)) { + /* Make sure 1 zval is enough for HashPointer (2 must be enough) */ + get_temporary_variable(CG(active_op_array)); + } + + if (key_ast) { + opline->result_type = IS_TMP_VAR; + opline->result.opline_num = get_temporary_variable(CG(active_op_array)); + GET_NODE(&key_node, opline->result); + } + + if (value_ast->attr == ZEND_AST_LIST) { + zend_compile_list_assign(&dummy_node, value_ast, &value_node TSRMLS_CC); + zend_do_free(&dummy_node TSRMLS_CC); + } else if (by_ref) { + zend_compile_assign_ref_common(NULL, value_ast, &value_node TSRMLS_CC); + } else { + zend_ast *znode_ast = zend_ast_create_znode(&value_node); + zend_ast *assign_ast = zend_ast_create_binary(ZEND_AST_ASSIGN, value_ast, znode_ast); + zend_compile_expr(&dummy_node, assign_ast TSRMLS_CC); + zend_do_free(&dummy_node TSRMLS_CC); + efree(znode_ast); + efree(assign_ast); + } + + if (key_ast) { + zend_ast *znode_ast = zend_ast_create_znode(&key_node); + zend_ast *assign_ast = zend_ast_create_binary(ZEND_AST_ASSIGN, key_ast, znode_ast); + zend_compile_expr(&dummy_node, assign_ast TSRMLS_CC); + zend_do_free(&dummy_node TSRMLS_CC); + efree(znode_ast); + efree(assign_ast); + } + + do_begin_loop(TSRMLS_C); + + zend_compile_stmt(stmt_ast TSRMLS_CC); + + opline = emit_op(NULL, ZEND_JMP, NULL, NULL TSRMLS_CC); + opline->op1.opline_num = opnum_fetch; + + opline = &CG(active_op_array)->opcodes[opnum_reset]; + opline->op2.opline_num = get_next_op_number(CG(active_op_array)); + + opline = &CG(active_op_array)->opcodes[opnum_fetch]; + opline->op2.opline_num = get_next_op_number(CG(active_op_array)); + + do_end_loop(opnum_fetch, 1 TSRMLS_CC); + + { + zend_op *container_ptr = zend_stack_top(&CG(foreach_copy_stack)); + generate_free_foreach_copy(container_ptr TSRMLS_CC); + zend_stack_del_top(&CG(foreach_copy_stack)); + } +} + void zend_compile_if(zend_ast *ast TSRMLS_DC) { zend_uint i; zend_uint *jmp_opnums = safe_emalloc(sizeof(zend_uint), ast->children - 1, 0); @@ -7998,6 +8110,9 @@ void zend_compile_stmt(zend_ast *ast TSRMLS_DC) { case ZEND_AST_FOR: zend_compile_for(ast TSRMLS_CC); break; + case ZEND_AST_FOREACH: + zend_compile_foreach(ast TSRMLS_CC); + break; case ZEND_AST_IF: zend_compile_if(ast TSRMLS_CC); break; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index c83b2510d9..eeead1e414 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -349,14 +349,17 @@ unticked_statement: | T_INLINE_HTML { $$.u.ast = zend_ast_create_unary(ZEND_ECHO, AST_ZVAL(&$1)); } | expr ';' { $$.u.ast = $1.u.ast; } | T_UNSET '(' unset_variables ')' ';' { $$.u.ast = $3.u.ast; } - | T_FOREACH '(' variable T_AS - { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 1 TSRMLS_CC); } - foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); } - foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); AN($$); } - | T_FOREACH '(' expr_without_variable T_AS + | T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement + { $$.u.ast = zend_ast_create(4, ZEND_AST_FOREACH, + $3.u.ast, $5.u.ast, NULL, $7.u.ast); } + | T_FOREACH '(' expr T_AS foreach_variable T_DOUBLE_ARROW foreach_variable ')' + foreach_statement + { $$.u.ast = zend_ast_create(4, ZEND_AST_FOREACH, + $3.u.ast, $7.u.ast, $5.u.ast, $9.u.ast); } + /*| T_FOREACH '(' expr_without_variable T_AS { AC($3); zend_do_foreach_begin(&$1, &$2, &$3, &$4, 0 TSRMLS_CC); } foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); } - foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); AN($$); } + foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); AN($$); }*/ | T_DECLARE { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(&$1 TSRMLS_CC); AN($$); } | ';' /* empty statement */ { AN($$); } | T_TRY { zend_do_try(&$1 TSRMLS_CC); } '{' inner_statement_list '}' { AS($4); } @@ -473,15 +476,10 @@ interface_list: | interface_list ',' fully_qualified_class_name { zend_do_implements_interface(&$3 TSRMLS_CC); } ; -foreach_optional_arg: - /* empty */ { $$.op_type = IS_UNUSED; } - | T_DOUBLE_ARROW foreach_variable { $$ = $2; } -; - foreach_variable: - variable { $$.u.ast = $1.u.ast; $$.EA = 0; } - | '&' variable { $$.u.ast = $2.u.ast; $$.EA = ZEND_PARSED_REFERENCE_VARIABLE; } - | T_LIST '(' assignment_list ')' { $$.u.ast = $3.u.ast; $$.EA = ZEND_PARSED_LIST_EXPR; } + variable { $$.u.ast = $1.u.ast; } + | '&' variable { $$.u.ast = zend_ast_create_unary(ZEND_AST_REF, $2.u.ast); } + | T_LIST '(' assignment_list ')' { $$.u.ast = $3.u.ast; } ; for_statement: @@ -491,8 +489,8 @@ for_statement: foreach_statement: - statement { AS($1); } - | ':' inner_statement_list T_ENDFOREACH ';' { AS($2); } + statement { $$.u.ast = $1.u.ast; } + | ':' inner_statement_list T_ENDFOREACH ';' { $$.u.ast = $2.u.ast; } ;