Skip to content

Commit 37f2f2e

Browse files
committed
tests
1 parent 0d7ef87 commit 37f2f2e

File tree

2 files changed

+152
-61
lines changed

2 files changed

+152
-61
lines changed

Zend/zend_compile.c

Lines changed: 150 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5501,89 +5501,178 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
55015501
zend_ast *value_ast = ast->child[1];
55025502
zend_ast *key_ast = ast->child[2];
55035503
zend_ast *stmt_ast = ast->child[3];
5504-
bool by_ref = value_ast->kind == ZEND_AST_REF;
5505-
bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast);
55065504

5507-
znode expr_node, reset_node, value_node, key_node;
5508-
zend_op *opline;
5509-
uint32_t opnum_reset, opnum_fetch;
5505+
if(!value_ast && !key_ast) {
5506+
bool by_ref = value_ast && value_ast->kind == ZEND_AST_REF;
5507+
bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast);
55105508

5511-
if (key_ast) {
5512-
if (key_ast->kind == ZEND_AST_REF) {
5513-
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
5509+
znode expr_node, reset_node, value_node, key_node;
5510+
zend_op * opline;
5511+
uint32_t opnum_reset, opnum_fetch;
5512+
5513+
if (key_ast) {
5514+
if (key_ast->kind == ZEND_AST_REF) {
5515+
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
5516+
}
5517+
if (key_ast->kind == ZEND_AST_ARRAY) {
5518+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
5519+
}
55145520
}
5515-
if (key_ast->kind == ZEND_AST_ARRAY) {
5516-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
5521+
5522+
if (by_ref) {
5523+
value_ast = value_ast->child[0];
55175524
}
5518-
}
55195525

5520-
if (by_ref) {
5521-
value_ast = value_ast->child[0];
5522-
}
5526+
if (value_ast && value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) {
5527+
by_ref = 1;
5528+
}
55235529

5524-
if (value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) {
5525-
by_ref = 1;
5526-
}
5530+
if (by_ref && is_variable) {
5531+
zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1);
5532+
} else {
5533+
zend_compile_expr(&expr_node, expr_ast);
5534+
}
55275535

5528-
if (by_ref && is_variable) {
5529-
zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1);
5530-
} else {
5531-
zend_compile_expr(&expr_node, expr_ast);
5532-
}
5536+
if (by_ref) {
5537+
zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W);
5538+
}
55335539

5534-
if (by_ref) {
5535-
zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W);
5536-
}
5540+
opnum_reset = get_next_op_number();
5541+
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
55375542

5538-
opnum_reset = get_next_op_number();
5539-
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
5543+
zend_begin_loop(ZEND_FE_FREE, &reset_node, 0);
55405544

5541-
zend_begin_loop(ZEND_FE_FREE, &reset_node, 0);
5545+
opnum_fetch = get_next_op_number();
5546+
opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL);
55425547

5543-
opnum_fetch = get_next_op_number();
5544-
opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL);
5548+
if (value_ast && is_this_fetch(value_ast)) {
5549+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
5550+
} else if (value_ast && value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
5551+
SET_NODE(opline->op2, &value_node);
5552+
} else {
5553+
opline->op2_type = IS_VAR;
5554+
opline->op2.var = get_temporary_variable();
5555+
GET_NODE(&value_node, opline->op2);
5556+
if (value_ast->kind == ZEND_AST_ARRAY) {
5557+
zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr);
5558+
} else if (by_ref) {
5559+
zend_emit_assign_ref_znode(value_ast, &value_node);
5560+
} else {
5561+
zend_emit_assign_znode(value_ast, &value_node);
5562+
}
5563+
}
55455564

5546-
if (is_this_fetch(value_ast)) {
5547-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
5548-
} else if (value_ast->kind == ZEND_AST_VAR &&
5549-
zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
5550-
SET_NODE(opline->op2, &value_node);
5565+
if (key_ast) {
5566+
opline = &CG(active_op_array)->opcodes[opnum_fetch];
5567+
zend_make_tmp_result(&key_node, opline);
5568+
zend_emit_assign_znode(key_ast, &key_node);
5569+
}
5570+
5571+
zend_compile_stmt(stmt_ast);
5572+
5573+
/* Place JMP and FE_FREE on the line where foreach starts. It would be
5574+
* better to use the end line, but this information is not available
5575+
* currently. */
5576+
CG(zend_lineno) = ast->lineno;
5577+
zend_emit_jump(opnum_fetch);
5578+
5579+
opline = &CG(active_op_array)->opcodes[opnum_reset];
5580+
opline->op2.opline_num = get_next_op_number();
5581+
5582+
opline = &CG(active_op_array)->opcodes[opnum_fetch];
5583+
opline->extended_value = get_next_op_number();
5584+
5585+
zend_end_loop(opnum_fetch, &reset_node);
5586+
5587+
opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
55515588
} else {
5552-
opline->op2_type = IS_VAR;
5553-
opline->op2.var = get_temporary_variable();
5554-
GET_NODE(&value_node, opline->op2);
5555-
if (value_ast->kind == ZEND_AST_ARRAY) {
5556-
zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr);
5557-
} else if (by_ref) {
5558-
zend_emit_assign_ref_znode(value_ast, &value_node);
5589+
bool by_ref = value_ast && value_ast->kind == ZEND_AST_REF;
5590+
bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast);
5591+
5592+
znode expr_node, reset_node, value_node, key_node;
5593+
zend_op * opline;
5594+
uint32_t opnum_reset, opnum_fetch;
5595+
5596+
if (key_ast) {
5597+
if (key_ast->kind == ZEND_AST_REF) {
5598+
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
5599+
}
5600+
if (key_ast->kind == ZEND_AST_ARRAY) {
5601+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
5602+
}
5603+
}
5604+
5605+
if (by_ref) {
5606+
value_ast = value_ast->child[0];
5607+
}
5608+
5609+
if (value_ast && value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) {
5610+
by_ref = 1;
5611+
}
5612+
5613+
if (by_ref && is_variable) {
5614+
zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1);
55595615
} else {
5560-
zend_emit_assign_znode(value_ast, &value_node);
5616+
zend_compile_expr(&expr_node, expr_ast);
55615617
}
5562-
}
55635618

5564-
if (key_ast) {
5565-
opline = &CG(active_op_array)->opcodes[opnum_fetch];
5566-
zend_make_tmp_result(&key_node, opline);
5567-
zend_emit_assign_znode(key_ast, &key_node);
5568-
}
5619+
if (by_ref) {
5620+
zend_separate_if_call_and_write(&expr_node, expr_ast, BP_VAR_W);
5621+
}
55695622

5570-
zend_compile_stmt(stmt_ast);
5623+
opnum_reset = get_next_op_number();
5624+
opline = zend_emit_op(&reset_node, ZEND_FE_RESET_R, &expr_node, NULL);
55715625

5572-
/* Place JMP and FE_FREE on the line where foreach starts. It would be
5573-
* better to use the end line, but this information is not available
5574-
* currently. */
5575-
CG(zend_lineno) = ast->lineno;
5576-
zend_emit_jump(opnum_fetch);
5626+
zend_begin_loop(ZEND_FE_FREE, &reset_node, 0);
55775627

5578-
opline = &CG(active_op_array)->opcodes[opnum_reset];
5579-
opline->op2.opline_num = get_next_op_number();
5628+
opnum_fetch = get_next_op_number();
5629+
opline = zend_emit_op(NULL, ZEND_FE_FETCH_R, &reset_node, NULL);
5630+
if (value_ast) {
5631+
if (is_this_fetch(value_ast)) {
5632+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
5633+
} else
5634+
if (value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
5635+
SET_NODE(opline->op2, &value_node);
5636+
}
5637+
else {
5638+
opline->op2_type = IS_VAR;
5639+
opline->op2.var = get_temporary_variable();
5640+
GET_NODE(&value_node, opline->op2);
5641+
if (value_ast->kind == ZEND_AST_ARRAY) {
5642+
zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr);
5643+
} else
5644+
if (by_ref) {
5645+
zend_emit_assign_ref_znode(value_ast, &value_node);
5646+
} else {
5647+
zend_emit_assign_znode(value_ast, &value_node);
5648+
}
5649+
}
5650+
}
55805651

5581-
opline = &CG(active_op_array)->opcodes[opnum_fetch];
5582-
opline->extended_value = get_next_op_number();
5652+
if (key_ast) {
5653+
opline = &CG(active_op_array)->opcodes[opnum_fetch];
5654+
zend_make_tmp_result(&key_node, opline);
5655+
zend_emit_assign_znode(key_ast, &key_node);
5656+
}
55835657

5584-
zend_end_loop(opnum_fetch, &reset_node);
5658+
zend_compile_stmt(stmt_ast);
5659+
5660+
/* Place JMP and FE_FREE on the line where foreach starts. It would be
5661+
* better to use the end line, but this information is not available
5662+
* currently. */
5663+
CG(zend_lineno) = ast->lineno;
5664+
zend_emit_jump(opnum_fetch);
5665+
5666+
opline = &CG(active_op_array)->opcodes[opnum_reset];
5667+
opline->op2.opline_num = get_next_op_number();
55855668

5586-
opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
5669+
opline = &CG(active_op_array)->opcodes[opnum_fetch];
5670+
opline->extended_value = get_next_op_number();
5671+
5672+
zend_end_loop(opnum_fetch, &reset_node);
5673+
5674+
opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
5675+
}
55875676
}
55885677
/* }}} */
55895678

Zend/zend_language_parser.y

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,8 @@ statement:
515515
| T_UNSET '(' unset_variables possible_comma ')' ';' { $$ = $3; }
516516
| T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
517517
{ $$ = zend_ast_create(ZEND_AST_FOREACH, $3, $5, NULL, $7); }
518+
| T_FOREACH '(' expr ')' foreach_statement
519+
{ $$ = zend_ast_create(ZEND_AST_FOREACH, $3, NULL, NULL, $5); }
518520
| T_FOREACH '(' expr T_AS foreach_variable T_DOUBLE_ARROW foreach_variable ')'
519521
foreach_statement
520522
{ $$ = zend_ast_create(ZEND_AST_FOREACH, $3, $7, $5, $9); }

0 commit comments

Comments
 (0)