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);
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;
| 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); }
| 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:
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; }
;