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;
}
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));
}
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;
+ }
}
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)
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;
}
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 {
*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
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); }
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); }