cvs: ZendEngine2 / zend_compile.c zend_compile.h zend_globals.h zend_language_parser.y /tests break_label01.phpt break_label02.phpt
break_label03.phpt break_label04.phpt break_label05.phpt break_label06.phpt break_label07.phpt break_label08.inc break_label08.phpt break_label09.phpt
php-src NEWS
| From: | Dmitry Stogov | Date: | Fri, 03 Mar 2006 13:09:13 +0000 |
| Subject: | cvs: ZendEngine2 / zend_compile.c zend_compile.h zend_globals.h zend_language_parser.y /tests break_label01.phpt break_label02.phpt break_label03.phpt break_label04.phpt break_label05.phpt break_label06.phpt break_label07.phpt break_label08.inc break_label08.phpt break_label09.phpt php-src NEWS |
||
| Groups: | php.zend-engine.cvs | ||
| Request: | Send a blank email to php-zend-engine-cvs+get-4642@lists.php.net to get a copy of this message | ||
dmitry Fri Mar 3 13:09:13 2006 UTC
Added files:
/ZendEngine2/tests break_label01.phpt break_label02.phpt
break_label03.phpt break_label04.phpt
break_label05.phpt break_label06.phpt
break_label07.phpt break_label08.inc
break_label08.phpt break_label09.phpt
Modified files:
/php-src NEWS
/ZendEngine2 zend_compile.c zend_compile.h zend_globals.h
zend_language_parser.y
Log:
Added support for "continue" and "break" operators with labels. Each loop or
switch statement can be marked by label and then it is possible to write "break
<label>" instead of "break <number>".
http://cvs.php.net/viewcvs.cgi/php-src/NEWS?r1=1.2090&r2=1.2091&diff_format=u Index: php-src/NEWS diff -u php-src/NEWS:1.2090 php-src/NEWS:1.2091 --- php-src/NEWS:1.2090 Sat Dec 24 11:36:52 2005 +++ php-src/NEWS Fri Mar 3 13:09:13 2006 @@ -6,6 +6,9 @@ for more details. (Dmitry) - Removed support for "continue" and "break" operators with non-constant operands. (Dmitry) +- Added support for "continue" and "break" operators with labels. Each loop or + switch statement can be marked by label and then it is possible to write + "break <label>" instead of "break <number>". (Dmitry, Sara) - Changed __toString() behavior to call it in all necessary places (Marcus, Dmitry) - Changed "instanceof" and "catch" operators, is_a() and is_subclass_of() http://cvs.php.net/viewcvs.cgi/ZendEngine2/zend_compile.c?r1=1.688&r2=1.689&diff_format=u Index: ZendEngine2/zend_compile.c diff -u ZendEngine2/zend_compile.c:1.688 ZendEngine2/zend_compile.c:1.689 --- ZendEngine2/zend_compile.c:1.688 Sun Feb 26 11:57:14 2006 +++ ZendEngine2/zend_compile.c Fri Mar 3 13:09:13 2006 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_compile.c,v 1.688 2006/02/26 11:57:14 dmitry Exp $ */ +/* $Id: zend_compile.c,v 1.689 2006/03/03 13:09:13 dmitry Exp $ */ #include <zend_language_parser.h> #include "zend.h" @@ -148,6 +148,9 @@ CG(start_lineno) = 0; init_compiler_declarables(TSRMLS_C); zend_hash_apply(CG(auto_globals), (apply_func_t) zend_auto_global_arm TSRMLS_CC); + zend_stack_init(&CG(labels_stack)); + CG(labels) = NULL; + CG(last_label) = NULL; } @@ -174,6 +177,7 @@ zend_hash_destroy(&CG(script_encodings_table)); zend_hash_destroy(&CG(filenames_table)); zend_llist_destroy(&CG(open_files)); + zend_stack_destroy(&CG(labels_stack)); } @@ -675,6 +679,10 @@ CG(active_op_array)->current_brk_cont = CG(active_op_array)->last_brk_cont; brk_cont_element = get_next_brk_cont_element(CG(active_op_array)); brk_cont_element->parent = parent; + if (CG(last_label)) { + CG(last_label)->loop = CG(active_op_array)->current_brk_cont; + CG(last_label) = NULL; + } } @@ -1241,6 +1249,10 @@ CG(doc_comment) = NULL; CG(doc_comment_len) = 0; } + + zend_stack_push(&CG(labels_stack), (void *) &CG(labels), sizeof(HashTable*)); + CG(labels) = NULL; + CG(last_label) = NULL; } void zend_do_handle_exception(TSRMLS_D) @@ -1250,6 +1262,21 @@ opline->opcode = ZEND_HANDLE_EXCEPTION; SET_UNUSED(opline->op1); SET_UNUSED(opline->op2); + + if (CG(labels)) { + zend_hash_destroy(CG(labels)); + FREE_HASHTABLE(CG(labels)); + } + if (!zend_stack_is_empty(&CG(labels_stack))) { + HashTable **pht; + + zend_stack_top(&CG(labels_stack), (void**)&pht); + CG(labels) = *pht; + zend_stack_del_top(&CG(labels_stack)); + } else { + CG(labels) = NULL; + } + CG(last_label) = NULL; } @@ -2665,8 +2692,37 @@ if (expr) { if (expr->op_type != IS_CONST) { zend_error(E_COMPILE_ERROR, "'%s' operator with non-constant operand is no longer supported", op == ZEND_BRK ? "break" : "continue"); - } else if (Z_TYPE(expr->u.constant) != IS_LONG || Z_LVAL(expr->u.constant) < 1) { - zend_error(E_COMPILE_ERROR, "'%s' operator accepts only positive numbers", op == ZEND_BRK ? "break" : "continue"); + } else { + if (Z_TYPE(expr->u.constant) == IS_STRING || + Z_TYPE(expr->u.constant) == IS_UNICODE) { + zend_label *label; + + if (CG(labels) == NULL || + zend_u_hash_find(CG(labels), Z_TYPE(expr->u.constant), Z_UNIVAL(expr->u.constant), Z_UNILEN(expr->u.constant)+1, (void**)&label) == FAILURE) { + zend_error(E_COMPILE_ERROR, "%s to undefined label '%R'", op == ZEND_BRK ? "break" : "continue", Z_TYPE(expr->u.constant), Z_UNIVAL(expr->u.constant)); + } + + if (label->loop != -1) { + long distance = 1; + long current = CG(active_op_array)->current_brk_cont; + + while (current != -1) { + if (label->loop == current) { + zval_dtor(&expr->u.constant); + Z_TYPE(expr->u.constant) = IS_LONG; + Z_LVAL(expr->u.constant) = distance; + break; + } + distance++; + current = CG(active_op_array)->brk_cont_array[current].parent; + } + } + if (Z_TYPE(expr->u.constant) != IS_LONG) { + zend_error(E_COMPILE_ERROR, "%s to label '%R', that doesn't mark outer loop", op == ZEND_BRK ? "break" : "continue", Z_TYPE(expr->u.constant), Z_UNIVAL(expr->u.constant)); + } + } else if (Z_TYPE(expr->u.constant) != IS_LONG || Z_LVAL(expr->u.constant) < 1) { + zend_error(E_COMPILE_ERROR, "'%s' operator accepts only positive numbers and labels", op == ZEND_BRK ? "break" : "continue"); + } } opline->op2 = *expr; } else { @@ -4315,6 +4371,30 @@ *result = opline->result; } +void zend_do_label(znode *label TSRMLS_DC) +{ + zend_op_array *oparray = CG(active_op_array); + zend_label dest; + + if (!CG(labels)) { + ALLOC_HASHTABLE(CG(labels)); + zend_hash_init(CG(labels), 4, NULL, NULL, 0); + } + + dest.brk_cont = oparray->current_brk_cont; + dest.loop = -1; + dest.opline_num = get_next_op_number(oparray); + + if (zend_u_hash_add(CG(labels), Z_TYPE(label->u.constant), Z_UNIVAL(label->u.constant), + Z_UNILEN(label->u.constant) + 1, (void**)&dest, sizeof(zend_label), (void**)&CG(last_label)) == FAILURE) { + CG(last_label) = NULL; + zend_error(E_COMPILE_ERROR, "Label '%R' already defined", Z_TYPE(label->u.constant), Z_UNIVAL(label->u.constant)); + } + + /* Done with label now */ + zval_dtor(&label->u.constant); +} + /* * Local variables: * tab-width: 4 http://cvs.php.net/viewcvs.cgi/ZendEngine2/zend_compile.h?r1=1.334&r2=1.335&diff_format=u Index: ZendEngine2/zend_compile.h diff -u ZendEngine2/zend_compile.h:1.334 ZendEngine2/zend_compile.h:1.335 --- ZendEngine2/zend_compile.h:1.334 Sun Feb 26 11:57:14 2006 +++ ZendEngine2/zend_compile.h Fri Mar 3 13:09:13 2006 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_compile.h,v 1.334 2006/02/26 11:57:14 dmitry Exp $ */ +/* $Id: zend_compile.h,v 1.335 2006/03/03 13:09:13 dmitry Exp $ */ #ifndef ZEND_COMPILE_H #define ZEND_COMPILE_H @@ -94,6 +94,11 @@ int parent; } zend_brk_cont_element; +typedef struct _zend_label { + int brk_cont; + int loop; + zend_uint opline_num; +} zend_label; typedef struct _zend_try_catch_element { zend_uint try_op; @@ -513,6 +518,8 @@ void zend_do_normalization(znode *result, znode *str TSRMLS_DC); +void zend_do_label(znode *label TSRMLS_DC); + #define INITIAL_OP_ARRAY_SIZE 64 http://cvs.php.net/viewcvs.cgi/ZendEngine2/zend_globals.h?r1=1.146&r2=1.147&diff_format=u Index: ZendEngine2/zend_globals.h diff -u ZendEngine2/zend_globals.h:1.146 ZendEngine2/zend_globals.h:1.147 --- ZendEngine2/zend_globals.h:1.146 Mon Feb 13 10:23:57 2006 +++ ZendEngine2/zend_globals.h Fri Mar 3 13:09:13 2006 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_globals.h,v 1.146 2006/02/13 10:23:57 dmitry Exp $ */ +/* $Id: zend_globals.h,v 1.147 2006/03/03 13:09:13 dmitry Exp $ */ #ifndef ZEND_GLOBALS_H #define ZEND_GLOBALS_H @@ -136,6 +136,10 @@ HashTable script_encodings_table; char *script_encoding; + HashTable *labels; + zend_label *last_label; + zend_stack labels_stack; + #ifdef ZTS HashTable **static_members; int last_static_member; http://cvs.php.net/viewcvs.cgi/ZendEngine2/zend_language_parser.y?r1=1.169&r2=1.170&diff_format=u Index: ZendEngine2/zend_language_parser.y diff -u ZendEngine2/zend_language_parser.y:1.169 ZendEngine2/zend_language_parser.y:1.170 --- ZendEngine2/zend_language_parser.y:1.169 Thu Feb 23 18:06:36 2006 +++ ZendEngine2/zend_language_parser.y Fri Mar 3 13:09:13 2006 @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_language_parser.y,v 1.169 2006/02/23 18:06:36 andrei Exp $ */ +/* $Id: zend_language_parser.y,v 1.170 2006/03/03 13:09:13 dmitry Exp $ */ /* * LALR shift/reduce conflicts and how they are resolved: @@ -183,11 +183,12 @@ statement: - unticked_statement { zend_do_ticks(TSRMLS_C); } + unticked_statement { CG(last_label) = NULL; zend_do_ticks(TSRMLS_C); } + | T_STRING ':' { zend_do_label(&$1 TSRMLS_CC); } ; unticked_statement: - '{' inner_statement_list '}' + '{' { CG(last_label) = NULL; } inner_statement_list '}' | T_IF '(' expr ')' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } statement { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); } | T_IF '(' expr ')' ':' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); } | T_WHILE '(' { $1.u.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' { zend_do_while_cond(&$4, &$5 TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$5 TSRMLS_CC); } @@ -203,9 +204,11 @@ for_statement { zend_do_for_end(&$7 TSRMLS_CC); } | T_SWITCH '(' expr ')' { zend_do_switch_cond(&$3 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$6 TSRMLS_CC); } | T_BREAK ';' { zend_do_brk_cont(ZEND_BRK, NULL TSRMLS_CC); } - | T_BREAK expr ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } + | T_BREAK T_LNUMBER ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } + | T_BREAK T_STRING ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } | T_CONTINUE ';' { zend_do_brk_cont(ZEND_CONT, NULL TSRMLS_CC); } - | T_CONTINUE expr ';' { zend_do_brk_cont(ZEND_CONT, &$2 TSRMLS_CC); } + | T_CONTINUE T_LNUMBER ';' { zend_do_brk_cont(ZEND_CONT, &$2 TSRMLS_CC); } + | T_CONTINUE T_STRING ';' { zend_do_brk_cont(ZEND_CONT, &$2 TSRMLS_CC); } | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label01.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label01.phpt +++ ZendEngine2/tests/break_label01.phpt --TEST-- labeled break 01: Old style "break <number>" --FILE-- <?php while (1) { echo "1: "; break 1; echo "bug\n"; } echo "ok\n"; while (1) { echo "2: "; while (1) { break 2; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; while (1) { echo "3: "; while (1) { break 1; echo "bug\n"; } break 1; echo "bug\n"; } echo "ok\n"; while (1) { echo "4: "; switch (1) { case 1: break 2; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; while (1) { while (1) { break 3; } } ?> --EXPECTF-- 1: ok 2: ok 3: ok 4: ok Fatal error: Cannot break/continue 3 levels in %sbreak_label01.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label02.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label02.phpt +++ ZendEngine2/tests/break_label02.phpt --TEST-- labeled break 02: New style "break <label>" --FILE-- <?php L1: while (1) { echo "1: "; break L1; echo "bug\n"; } echo "ok\n"; L2: while (1) { echo "2: "; while (1) { break L2; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; L3: while (1) { echo "3: "; L4: while (1) { break L4; echo "bug\n"; } break L3; echo "bug\n"; } echo "ok\n"; L5: while (1) { echo "4: "; switch (1) { case 1: break L5; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; L6: while (1) { echo "5: "; L7: switch (1) { case 1: break L7; echo "bug\n"; } break L6; echo "bug\n"; } echo "ok\n"; ?> --EXPECT-- 1: ok 2: ok 3: ok 4: ok 5: ok http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label03.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label03.phpt +++ ZendEngine2/tests/break_label03.phpt --TEST-- labeled break 03: Undefined label --FILE-- <?php echo "bug\n"; L1: while (1) { break L2; } ?> --EXPECTF-- Fatal error: break to undefined label 'L2' in %sbreak_label03.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label04.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label04.phpt +++ ZendEngine2/tests/break_label04.phpt --TEST-- labeled break 04: Invalid label (another loop) --FILE-- <?php echo "bug\n"; L1: while (1) { break; } L2: while (1) { break L1; } ?> --EXPECTF-- Fatal error: break to label 'L1', that doesn't mark outer loop in %sbreak_label04.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label05.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label05.phpt +++ ZendEngine2/tests/break_label05.phpt --TEST-- labeled break 05: Invalid label (non loop) --FILE-- <?php L1: echo "bug\n"; while (1) { break L1; } ?> --EXPECTF-- Fatal error: break to label 'L1', that doesn't mark outer loop in %sbreak_label05.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label06.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label06.phpt +++ ZendEngine2/tests/break_label06.phpt --TEST-- labeled break 06: Invalid label (non declared yet) --FILE-- <?php echo "bug\n"; while (1) { break L1; } L1: ?> --EXPECTF-- Fatal error: break to undefined label 'L1' in %sbreak_label06.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label07.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label07.phpt +++ ZendEngine2/tests/break_label07.phpt --TEST-- labeled break 07: Label redefinition --FILE-- <?php echo "bug\n"; L1: while (1) { L2: while (1) { break L1; } } L3: while (1) { L1: while (1) { break L3; } } --EXPECTF-- Fatal error: Label 'L1' already defined in %sbreak_label07.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label08.inc?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label08.inc +++ ZendEngine2/tests/break_label08.inc <?php L1: while (1) { echo "2: "; L2: while (1) { break L1; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; ?> http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label08.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label08.phpt +++ ZendEngine2/tests/break_label08.phpt --TEST-- labeled break 08: Use the same <label> in different files --FILE-- <?php L1: while (1) { echo "1: "; L2: while (1) { break L1; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; include(dirname(__FILE__)."/break_label08.inc"); ?> --EXPECT-- 1: ok 2: ok http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label09.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label09.phpt +++ ZendEngine2/tests/break_label09.phpt --TEST-- labeled break 09: Use the same <label> name in several copies of one file --FILE-- <?php if (isset($n)) { ++$n; } else { $n = 1; } L1: while (1) { echo "$n: "; L2: while (1) { break L1; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; if ($n < 3) { include(__FILE__); } ?> --EXPECTF-- 1: ok 2: ok 3: ok
http://cvs.php.net/viewcvs.cgi/php-src/NEWS?r1=1.2090&r2=1.2091&diff_format=u Index: php-src/NEWS diff -u php-src/NEWS:1.2090 php-src/NEWS:1.2091 --- php-src/NEWS:1.2090 Sat Dec 24 11:36:52 2005 +++ php-src/NEWS Fri Mar 3 13:09:13 2006 @@ -6,6 +6,9 @@ for more details. (Dmitry) - Removed support for "continue" and "break" operators with non-constant operands. (Dmitry) +- Added support for "continue" and "break" operators with labels. Each loop or + switch statement can be marked by label and then it is possible to write + "break <label>" instead of "break <number>". (Dmitry, Sara) - Changed __toString() behavior to call it in all necessary places (Marcus, Dmitry) - Changed "instanceof" and "catch" operators, is_a() and is_subclass_of() http://cvs.php.net/viewcvs.cgi/ZendEngine2/zend_compile.c?r1=1.688&r2=1.689&diff_format=u Index: ZendEngine2/zend_compile.c diff -u ZendEngine2/zend_compile.c:1.688 ZendEngine2/zend_compile.c:1.689 --- ZendEngine2/zend_compile.c:1.688 Sun Feb 26 11:57:14 2006 +++ ZendEngine2/zend_compile.c Fri Mar 3 13:09:13 2006 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_compile.c,v 1.688 2006/02/26 11:57:14 dmitry Exp $ */ +/* $Id: zend_compile.c,v 1.689 2006/03/03 13:09:13 dmitry Exp $ */ #include <zend_language_parser.h> #include "zend.h" @@ -148,6 +148,9 @@ CG(start_lineno) = 0; init_compiler_declarables(TSRMLS_C); zend_hash_apply(CG(auto_globals), (apply_func_t) zend_auto_global_arm TSRMLS_CC); + zend_stack_init(&CG(labels_stack)); + CG(labels) = NULL; + CG(last_label) = NULL; } @@ -174,6 +177,7 @@ zend_hash_destroy(&CG(script_encodings_table)); zend_hash_destroy(&CG(filenames_table)); zend_llist_destroy(&CG(open_files)); + zend_stack_destroy(&CG(labels_stack)); } @@ -675,6 +679,10 @@ CG(active_op_array)->current_brk_cont = CG(active_op_array)->last_brk_cont; brk_cont_element = get_next_brk_cont_element(CG(active_op_array)); brk_cont_element->parent = parent; + if (CG(last_label)) { + CG(last_label)->loop = CG(active_op_array)->current_brk_cont; + CG(last_label) = NULL; + } } @@ -1241,6 +1249,10 @@ CG(doc_comment) = NULL; CG(doc_comment_len) = 0; } + + zend_stack_push(&CG(labels_stack), (void *) &CG(labels), sizeof(HashTable*)); + CG(labels) = NULL; + CG(last_label) = NULL; } void zend_do_handle_exception(TSRMLS_D) @@ -1250,6 +1262,21 @@ opline->opcode = ZEND_HANDLE_EXCEPTION; SET_UNUSED(opline->op1); SET_UNUSED(opline->op2); + + if (CG(labels)) { + zend_hash_destroy(CG(labels)); + FREE_HASHTABLE(CG(labels)); + } + if (!zend_stack_is_empty(&CG(labels_stack))) { + HashTable **pht; + + zend_stack_top(&CG(labels_stack), (void**)&pht); + CG(labels) = *pht; + zend_stack_del_top(&CG(labels_stack)); + } else { + CG(labels) = NULL; + } + CG(last_label) = NULL; } @@ -2665,8 +2692,37 @@ if (expr) { if (expr->op_type != IS_CONST) { zend_error(E_COMPILE_ERROR, "'%s' operator with non-constant operand is no longer supported", op == ZEND_BRK ? "break" : "continue"); - } else if (Z_TYPE(expr->u.constant) != IS_LONG || Z_LVAL(expr->u.constant) < 1) { - zend_error(E_COMPILE_ERROR, "'%s' operator accepts only positive numbers", op == ZEND_BRK ? "break" : "continue"); + } else { + if (Z_TYPE(expr->u.constant) == IS_STRING || + Z_TYPE(expr->u.constant) == IS_UNICODE) { + zend_label *label; + + if (CG(labels) == NULL || + zend_u_hash_find(CG(labels), Z_TYPE(expr->u.constant), Z_UNIVAL(expr->u.constant), Z_UNILEN(expr->u.constant)+1, (void**)&label) == FAILURE) { + zend_error(E_COMPILE_ERROR, "%s to undefined label '%R'", op == ZEND_BRK ? "break" : "continue", Z_TYPE(expr->u.constant), Z_UNIVAL(expr->u.constant)); + } + + if (label->loop != -1) { + long distance = 1; + long current = CG(active_op_array)->current_brk_cont; + + while (current != -1) { + if (label->loop == current) { + zval_dtor(&expr->u.constant); + Z_TYPE(expr->u.constant) = IS_LONG; + Z_LVAL(expr->u.constant) = distance; + break; + } + distance++; + current = CG(active_op_array)->brk_cont_array[current].parent; + } + } + if (Z_TYPE(expr->u.constant) != IS_LONG) { + zend_error(E_COMPILE_ERROR, "%s to label '%R', that doesn't mark outer loop", op == ZEND_BRK ? "break" : "continue", Z_TYPE(expr->u.constant), Z_UNIVAL(expr->u.constant)); + } + } else if (Z_TYPE(expr->u.constant) != IS_LONG || Z_LVAL(expr->u.constant) < 1) { + zend_error(E_COMPILE_ERROR, "'%s' operator accepts only positive numbers and labels", op == ZEND_BRK ? "break" : "continue"); + } } opline->op2 = *expr; } else { @@ -4315,6 +4371,30 @@ *result = opline->result; } +void zend_do_label(znode *label TSRMLS_DC) +{ + zend_op_array *oparray = CG(active_op_array); + zend_label dest; + + if (!CG(labels)) { + ALLOC_HASHTABLE(CG(labels)); + zend_hash_init(CG(labels), 4, NULL, NULL, 0); + } + + dest.brk_cont = oparray->current_brk_cont; + dest.loop = -1; + dest.opline_num = get_next_op_number(oparray); + + if (zend_u_hash_add(CG(labels), Z_TYPE(label->u.constant), Z_UNIVAL(label->u.constant), + Z_UNILEN(label->u.constant) + 1, (void**)&dest, sizeof(zend_label), (void**)&CG(last_label)) == FAILURE) { + CG(last_label) = NULL; + zend_error(E_COMPILE_ERROR, "Label '%R' already defined", Z_TYPE(label->u.constant), Z_UNIVAL(label->u.constant)); + } + + /* Done with label now */ + zval_dtor(&label->u.constant); +} + /* * Local variables: * tab-width: 4 http://cvs.php.net/viewcvs.cgi/ZendEngine2/zend_compile.h?r1=1.334&r2=1.335&diff_format=u Index: ZendEngine2/zend_compile.h diff -u ZendEngine2/zend_compile.h:1.334 ZendEngine2/zend_compile.h:1.335 --- ZendEngine2/zend_compile.h:1.334 Sun Feb 26 11:57:14 2006 +++ ZendEngine2/zend_compile.h Fri Mar 3 13:09:13 2006 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_compile.h,v 1.334 2006/02/26 11:57:14 dmitry Exp $ */ +/* $Id: zend_compile.h,v 1.335 2006/03/03 13:09:13 dmitry Exp $ */ #ifndef ZEND_COMPILE_H #define ZEND_COMPILE_H @@ -94,6 +94,11 @@ int parent; } zend_brk_cont_element; +typedef struct _zend_label { + int brk_cont; + int loop; + zend_uint opline_num; +} zend_label; typedef struct _zend_try_catch_element { zend_uint try_op; @@ -513,6 +518,8 @@ void zend_do_normalization(znode *result, znode *str TSRMLS_DC); +void zend_do_label(znode *label TSRMLS_DC); + #define INITIAL_OP_ARRAY_SIZE 64 http://cvs.php.net/viewcvs.cgi/ZendEngine2/zend_globals.h?r1=1.146&r2=1.147&diff_format=u Index: ZendEngine2/zend_globals.h diff -u ZendEngine2/zend_globals.h:1.146 ZendEngine2/zend_globals.h:1.147 --- ZendEngine2/zend_globals.h:1.146 Mon Feb 13 10:23:57 2006 +++ ZendEngine2/zend_globals.h Fri Mar 3 13:09:13 2006 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_globals.h,v 1.146 2006/02/13 10:23:57 dmitry Exp $ */ +/* $Id: zend_globals.h,v 1.147 2006/03/03 13:09:13 dmitry Exp $ */ #ifndef ZEND_GLOBALS_H #define ZEND_GLOBALS_H @@ -136,6 +136,10 @@ HashTable script_encodings_table; char *script_encoding; + HashTable *labels; + zend_label *last_label; + zend_stack labels_stack; + #ifdef ZTS HashTable **static_members; int last_static_member; http://cvs.php.net/viewcvs.cgi/ZendEngine2/zend_language_parser.y?r1=1.169&r2=1.170&diff_format=u Index: ZendEngine2/zend_language_parser.y diff -u ZendEngine2/zend_language_parser.y:1.169 ZendEngine2/zend_language_parser.y:1.170 --- ZendEngine2/zend_language_parser.y:1.169 Thu Feb 23 18:06:36 2006 +++ ZendEngine2/zend_language_parser.y Fri Mar 3 13:09:13 2006 @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_language_parser.y,v 1.169 2006/02/23 18:06:36 andrei Exp $ */ +/* $Id: zend_language_parser.y,v 1.170 2006/03/03 13:09:13 dmitry Exp $ */ /* * LALR shift/reduce conflicts and how they are resolved: @@ -183,11 +183,12 @@ statement: - unticked_statement { zend_do_ticks(TSRMLS_C); } + unticked_statement { CG(last_label) = NULL; zend_do_ticks(TSRMLS_C); } + | T_STRING ':' { zend_do_label(&$1 TSRMLS_CC); } ; unticked_statement: - '{' inner_statement_list '}' + '{' { CG(last_label) = NULL; } inner_statement_list '}' | T_IF '(' expr ')' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } statement { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); } | T_IF '(' expr ')' ':' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); } | T_WHILE '(' { $1.u.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' { zend_do_while_cond(&$4, &$5 TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$5 TSRMLS_CC); } @@ -203,9 +204,11 @@ for_statement { zend_do_for_end(&$7 TSRMLS_CC); } | T_SWITCH '(' expr ')' { zend_do_switch_cond(&$3 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$6 TSRMLS_CC); } | T_BREAK ';' { zend_do_brk_cont(ZEND_BRK, NULL TSRMLS_CC); } - | T_BREAK expr ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } + | T_BREAK T_LNUMBER ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } + | T_BREAK T_STRING ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } | T_CONTINUE ';' { zend_do_brk_cont(ZEND_CONT, NULL TSRMLS_CC); } - | T_CONTINUE expr ';' { zend_do_brk_cont(ZEND_CONT, &$2 TSRMLS_CC); } + | T_CONTINUE T_LNUMBER ';' { zend_do_brk_cont(ZEND_CONT, &$2 TSRMLS_CC); } + | T_CONTINUE T_STRING ';' { zend_do_brk_cont(ZEND_CONT, &$2 TSRMLS_CC); } | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label01.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label01.phpt +++ ZendEngine2/tests/break_label01.phpt --TEST-- labeled break 01: Old style "break <number>" --FILE-- <?php while (1) { echo "1: "; break 1; echo "bug\n"; } echo "ok\n"; while (1) { echo "2: "; while (1) { break 2; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; while (1) { echo "3: "; while (1) { break 1; echo "bug\n"; } break 1; echo "bug\n"; } echo "ok\n"; while (1) { echo "4: "; switch (1) { case 1: break 2; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; while (1) { while (1) { break 3; } } ?> --EXPECTF-- 1: ok 2: ok 3: ok 4: ok Fatal error: Cannot break/continue 3 levels in %sbreak_label01.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label02.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label02.phpt +++ ZendEngine2/tests/break_label02.phpt --TEST-- labeled break 02: New style "break <label>" --FILE-- <?php L1: while (1) { echo "1: "; break L1; echo "bug\n"; } echo "ok\n"; L2: while (1) { echo "2: "; while (1) { break L2; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; L3: while (1) { echo "3: "; L4: while (1) { break L4; echo "bug\n"; } break L3; echo "bug\n"; } echo "ok\n"; L5: while (1) { echo "4: "; switch (1) { case 1: break L5; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; L6: while (1) { echo "5: "; L7: switch (1) { case 1: break L7; echo "bug\n"; } break L6; echo "bug\n"; } echo "ok\n"; ?> --EXPECT-- 1: ok 2: ok 3: ok 4: ok 5: ok http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label03.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label03.phpt +++ ZendEngine2/tests/break_label03.phpt --TEST-- labeled break 03: Undefined label --FILE-- <?php echo "bug\n"; L1: while (1) { break L2; } ?> --EXPECTF-- Fatal error: break to undefined label 'L2' in %sbreak_label03.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label04.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label04.phpt +++ ZendEngine2/tests/break_label04.phpt --TEST-- labeled break 04: Invalid label (another loop) --FILE-- <?php echo "bug\n"; L1: while (1) { break; } L2: while (1) { break L1; } ?> --EXPECTF-- Fatal error: break to label 'L1', that doesn't mark outer loop in %sbreak_label04.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label05.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label05.phpt +++ ZendEngine2/tests/break_label05.phpt --TEST-- labeled break 05: Invalid label (non loop) --FILE-- <?php L1: echo "bug\n"; while (1) { break L1; } ?> --EXPECTF-- Fatal error: break to label 'L1', that doesn't mark outer loop in %sbreak_label05.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label06.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label06.phpt +++ ZendEngine2/tests/break_label06.phpt --TEST-- labeled break 06: Invalid label (non declared yet) --FILE-- <?php echo "bug\n"; while (1) { break L1; } L1: ?> --EXPECTF-- Fatal error: break to undefined label 'L1' in %sbreak_label06.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label07.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label07.phpt +++ ZendEngine2/tests/break_label07.phpt --TEST-- labeled break 07: Label redefinition --FILE-- <?php echo "bug\n"; L1: while (1) { L2: while (1) { break L1; } } L3: while (1) { L1: while (1) { break L3; } } --EXPECTF-- Fatal error: Label 'L1' already defined in %sbreak_label07.php on line %d http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label08.inc?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label08.inc +++ ZendEngine2/tests/break_label08.inc <?php L1: while (1) { echo "2: "; L2: while (1) { break L1; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; ?> http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label08.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label08.phpt +++ ZendEngine2/tests/break_label08.phpt --TEST-- labeled break 08: Use the same <label> in different files --FILE-- <?php L1: while (1) { echo "1: "; L2: while (1) { break L1; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; include(dirname(__FILE__)."/break_label08.inc"); ?> --EXPECT-- 1: ok 2: ok http://cvs.php.net/viewcvs.cgi/ZendEngine2/tests/break_label09.phpt?view=markup&rev=1.1 Index: ZendEngine2/tests/break_label09.phpt +++ ZendEngine2/tests/break_label09.phpt --TEST-- labeled break 09: Use the same <label> name in several copies of one file --FILE-- <?php if (isset($n)) { ++$n; } else { $n = 1; } L1: while (1) { echo "$n: "; L2: while (1) { break L1; echo "bug\n"; } echo "bug\n"; } echo "ok\n"; if ($n < 3) { include(__FILE__); } ?> --EXPECTF-- 1: ok 2: ok 3: ok