Port foreach

This commit is contained in:
Nikita Popov 2014-07-11 12:16:21 +02:00
parent 7f151465b2
commit f12c1482a3
3 changed files with 131 additions and 16 deletions

View file

@ -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,
};

View file

@ -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;

View file

@ -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; }
;