Permalink
Browse files

MFH: Implemented "jump label" operator (limited "goto")

[DOC]
  • Loading branch information...
1 parent 9d9468e commit f66f55edc5460c7105c43ede5b0d1d3d1bf2b8c8 @felipensp felipensp committed Mar 28, 2008
View
@@ -0,0 +1,14 @@
+--TEST--
+jump 01: goto backward
+--FILE--
+<?php
+$n = 1;
+L1:
+echo "$n: ok\n";
+$n++;
+if ($n <= 3) goto L1;
+?>
+--EXPECT--
+1: ok
+2: ok
+3: ok
View
@@ -0,0 +1,16 @@
+--TEST--
+jump 02: goto forward
+--FILE--
+<?php
+$n = 1;
+L1:
+if ($n > 3) goto L2;
+echo "$n: ok\n";
+$n++;
+goto L1;
+L2:
+?>
+--EXPECT--
+1: ok
+2: ok
+3: ok
View
@@ -0,0 +1,18 @@
+--TEST--
+jump 03: goto inside control structures
+--FILE--
+<?php
+do {
+ if (1) {
+ echo "1: ok\n";
+ goto L1;
+ } else {
+ echo "bug\n";
+L1:
+ echo "2: ok\n";
+ }
+} while (0);
+?>
+--EXPECT--
+1: ok
+2: ok
View
@@ -0,0 +1,24 @@
+--TEST--
+jump 04: goto from loop (backward)
+--FILE--
+<?php
+$s = "X";
+echo "1: ok\n";
+L1: if ($s != "X") {
+ echo "4: ok\n";
+} else {
+ echo "2: ok\n";
+ while ($s != "XXX") {
+ echo "3: ok\n";
+ $s .= "X";
+ goto L1;
+ echo "bug\n";
+ }
+ echo "bug\n";
+}
+?>
+--EXPECT--
+1: ok
+2: ok
+3: ok
+4: ok
View
@@ -0,0 +1,26 @@
+--TEST--
+jump 05: goto from loop (forward)
+--FILE--
+<?php
+$ar = array("1","2","3");
+foreach ($ar as $val) {
+ switch ($val) {
+ case "1":
+ echo "1: ok\n";
+ break;
+ case "2":
+ echo "2: ok\n";
+ goto L1;
+ case "3":
+ echo "bug\n";
+ break;
+ }
+}
+echo "bug\n";
+L1:
+echo "3: ok\n";
+?>
+--EXPECT--
+1: ok
+2: ok
+3: ok
View
@@ -0,0 +1,8 @@
+--TEST--
+jump 06: goto to undefined label
+--FILE--
+<?php
+goto L1;
+?>
+--EXPECTF--
+Fatal error: 'goto' to undefined label 'L1' in %sjump06.php on line 2
View
@@ -0,0 +1,11 @@
+--TEST--
+jump 07: goto into loop (backward)
+--FILE--
+<?php
+while (0) {
+ L1: echo "bug\n";
+}
+goto L1;
+?>
+--EXPECTF--
+Fatal error: 'goto' into loop or switch statement is disallowed in %sjump07.php on line 5
View
@@ -0,0 +1,11 @@
+--TEST--
+jump 08: goto into loop (forward)
+--FILE--
+<?php
+goto L1;
+while (0) {
+ L1: echo "bug\n";
+}
+?>
+--EXPECTF--
+Fatal error: 'goto' into loop or switch statement is disallowed in %sjump08.php on line 2
View
@@ -0,0 +1,13 @@
+--TEST--
+jump 09: goto into switch (backward)
+--FILE--
+<?php
+switch (0) {
+ case 1:
+ L1: echo "bug\n";
+ break;
+}
+goto L1;
+?>
+--EXPECTF--
+Fatal error: 'goto' into loop or switch statement is disallowed in %sjump09.php on line 7
View
@@ -0,0 +1,13 @@
+--TEST--
+jump 10: goto into switch (forward)
+--FILE--
+<?php
+goto L1;
+switch (0) {
+ case 1:
+ L1: echo "bug\n";
+ break;
+}
+?>
+--EXPECTF--
+Fatal error: 'goto' into loop or switch statement is disallowed in %sjump10.php on line 2
View
@@ -0,0 +1,26 @@
+--TEST--
+jump 08: goto inside switch in constructor
+--FILE--
+<?php
+class foobar {
+ public function __construct() {
+ switch (1) {
+ default:
+ goto b;
+ a:
+ print "ok!\n";
+ break;
+ b:
+ print "ok!\n";
+ goto a;
+ }
+ print "ok!\n";
+ }
+}
+
+new foobar;
+?>
+--EXPECT--
+ok!
+ok!
+ok!
View
@@ -0,0 +1,19 @@
+--TEST--
+jump 09: goto in declare statement
+--FILE--
+<?php
+a: print "ok!\n";
+goto c;
+declare (ticks=1) {
+ b:
+ print "ok!\n";
+ exit;
+}
+c:
+ print "ok!\n";
+ goto b;
+?>
+--EXPECT--
+ok!
+ok!
+ok!
View
@@ -0,0 +1,25 @@
+--TEST--
+jump 10: goto with try blocks
+--FILE--
+<?php
+goto a;
+e: return;
+try {
+ a: print 1;
+ goto b;
+ try {
+ b: print 2;
+ goto c;
+ }
+ catch(Exception $e) {
+ c: print 3;
+ goto d;
+ }
+}
+catch(Exception $e) {
+ d: print 4;
+ goto e;
+}
+?>
+--EXPECT--
+1234
View
@@ -130,6 +130,8 @@ void zend_init_compiler_data_structures(TSRMLS_D)
CG(current_import) = NULL;
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;
}
@@ -163,6 +165,7 @@ void shutdown_compiler(TSRMLS_D)
zend_stack_destroy(&CG(list_stack));
zend_hash_destroy(&CG(filenames_table));
zend_llist_destroy(&CG(open_files));
+ zend_stack_destroy(&CG(labels_stack));
}
@@ -1274,6 +1277,9 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n
CG(doc_comment) = NULL;
CG(doc_comment_len) = 0;
}
+
+ zend_stack_push(&CG(labels_stack), (void *) &CG(labels), sizeof(HashTable*));
+ CG(labels) = NULL;
}
void zend_do_handle_exception(TSRMLS_D)
@@ -1295,6 +1301,7 @@ void zend_do_end_function_declaration(znode *function_token TSRMLS_DC)
zend_do_return(NULL, 0 TSRMLS_CC);
pass_two(CG(active_op_array) TSRMLS_CC);
+ zend_release_labels(TSRMLS_C);
if (CG(active_class_entry)) {
zend_check_magic_method_implementation(CG(active_class_entry), (zend_function*)CG(active_op_array), E_COMPILE_ERROR TSRMLS_CC);
@@ -1668,6 +1675,110 @@ void zend_do_fetch_class(znode *result, znode *class_name TSRMLS_DC)
*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.opline_num = get_next_op_number(oparray);
+
+ if (zend_hash_add(CG(labels), Z_STRVAL(label->u.constant), Z_STRLEN(label->u.constant) + 1, (void**)&dest, sizeof(zend_label), NULL) == FAILURE) {
+ zend_error(E_COMPILE_ERROR, "Label '%s' already defined", Z_STRVAL(label->u.constant));
+ }
+
+ /* Done with label now */
+ zval_dtor(&label->u.constant);
+}
+/* }}} */
+
+void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2 TSRMLS_DC) /* {{{ */
+{
+ zend_label *dest;
+ long current, distance;
+
+ if (CG(labels) == NULL ||
+ zend_hash_find(CG(labels), Z_STRVAL(opline->op2.u.constant), Z_STRLEN(opline->op2.u.constant)+1, (void**)&dest) == FAILURE) {
+
+ if (pass2) {
+ CG(in_compilation) = 1;
+ CG(active_op_array) = op_array;
+ CG(zend_lineno) = opline->lineno;
+ zend_error(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL(opline->op2.u.constant));
+ } else {
+ /* Label is not defined. Delay to pass 2. */
+ INC_BPC(op_array);
+ return;
+ }
+ }
+
+ opline->op1.u.opline_num = dest->opline_num;
+ zval_dtor(&opline->op2.u.constant);
+
+ /* Check that we are not moving into loop or switch */
+ current = opline->extended_value;
+ for (distance = 0; current != dest->brk_cont; distance++) {
+ if (current == -1) {
+ if (pass2) {
+ CG(in_compilation) = 1;
+ CG(active_op_array) = op_array;
+ CG(zend_lineno) = opline->lineno;
+ }
+ zend_error(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
+ }
+ current = op_array->brk_cont_array[current].parent;
+ }
+
+ if (distance == 0) {
+ /* Nothing to break out of, optimize to ZEND_JMP */
+ opline->opcode = ZEND_JMP;
+ opline->extended_value = 0;
+ SET_UNUSED(opline->op2);
+ } else {
+ /* Set real break distance */
+ ZVAL_LONG(&opline->op2.u.constant, distance);
+ }
+
+ if (pass2) {
+ DEC_BPC(op_array);
+ }
+}
+/* }}} */
+
+void zend_do_goto(znode *label TSRMLS_DC) /* {{{ */
+{
+ zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+
+ opline->opcode = ZEND_GOTO;
+ opline->extended_value = CG(active_op_array)->current_brk_cont;
+ SET_UNUSED(opline->op1);
+ opline->op2 = *label;
+ zend_resolve_goto_label(CG(active_op_array), opline, 0 TSRMLS_CC);
+}
+/* }}} */
+
+void zend_release_labels(TSRMLS_D) /* {{{ */
+{
+ 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;
+ }
+}
+/* }}} */
void zend_do_build_full_name(znode *result, znode *prefix, znode *name TSRMLS_DC)
{
Oops, something went wrong.

0 comments on commit f66f55e

Please sign in to comment.