]> granicus.if.org Git - php/commitdiff
Squashed commit of the following:
authorDmitry Stogov <dmitry@zend.com>
Fri, 10 Jul 2015 00:31:52 +0000 (03:31 +0300)
committerDmitry Stogov <dmitry@zend.com>
Fri, 10 Jul 2015 00:31:52 +0000 (03:31 +0300)
commit 03cf871f1576f08b2348c141b209894a7bf17a86
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:45:31 2015 +0300

    Revert "Fixed bug #62210 (Exceptions can leak temporary variables. As a part of the fix serious refactoring was done. op_array->brk_cont_array was removed, and replaced with more general and speed efficient op_array->T_liveliness. ZEND_GOTO opcode is always replaced by ZEND_JMP at compile time). (Bob, Dmitry, Laruence)"

    This reverts commit 5ee841325901a4b040cfea56292a24702fe224d9.

commit 285a68227ce3d380e821a24fa389aa5239bd3fe1
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:45:26 2015 +0300

    Revert "Tuned off dubugging of live ranges"

    This reverts commit 404dc93d35f7061fc4b1b41ad6cb0721b9b52bcc.

commit 93d9d11157301ee2ec99afb6f5744b126d17f637
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:45:17 2015 +0300

    Revert "Remove loop_var_stack"

    This reverts commit b3a4c05071c3786e27e1326fa1b4d5acad62fccd.

commit ede68ebbc284aec79e3f719f2c8dbf9da6907752
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:45:12 2015 +0300

    Revert "ZEND_SEPARATE reuses temporaries"

    This reverts commit 1852f538b9f8d5e7d67fe5a4f6080396d8b10034.

commit 96d8995dc1f517fb01b481736273767509f76c47
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:45:10 2015 +0300

    Revert "Add assertion in liveliness computation"

    This reverts commit ed14019e8c0c852480eebc6fc552d8c3d939dce1.

commit 0649d7bfef152e6cc8e67b922534e9946c634d9c
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:45:07 2015 +0300

    Revert "Fixed invalid live-range detection"

    This reverts commit 54f367ee2a2e4cb7c952b17915c226fdc56038ab.

commit dfe8f3851f6b04595eb089323e3492115a59363e
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:45:04 2015 +0300

    Revert "Add test guaranteeing that loop vars are only freed after potential return type exceptions"

    This reverts commit f5db5a558d550bf441373febebbb02f3884209d1.

commit 52a94aad6f48a199358cc07f7e4f56bb73050504
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:45:01 2015 +0300

    Revert "Fixed exception habdling on "return" statement."

    This reverts commit 17c5315bdf8f8087979aeb55f6d3a512ba197cf5.

commit 6e90ad7331901711e89c2ceb2bcab5023e5cee60
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:44:58 2015 +0300

    Revert "Fix too early terminated temporary range with break/cont/goto"

    This reverts commit cc876c04b420589cb1f62b650d0c0e24975dd4af.

commit 7b766e44b1970e4031f75109c302c07ead2c05cb
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 10 02:44:55 2015 +0300

    Revert "Fixed exception catching on break/continue"

    This reverts commit 8c3f701eebfa92d761bb368cfa8c2d1ccf821b9d.

33 files changed:
NEWS
Zend/tests/jump15.phpt [deleted file]
Zend/tests/return_types/029.phpt [deleted file]
Zend/tests/return_types/030.phpt [deleted file]
Zend/tests/return_types/031.phpt [deleted file]
Zend/tests/temporary_cleaning_001.phpt [deleted file]
Zend/tests/temporary_cleaning_002.phpt [deleted file]
Zend/tests/temporary_cleaning_003.phpt [deleted file]
Zend/tests/temporary_cleaning_004.phpt [deleted file]
Zend/tests/temporary_cleaning_005.phpt [deleted file]
Zend/tests/temporary_cleaning_006.phpt [deleted file]
Zend/tests/temporary_cleaning_007.phpt [deleted file]
Zend/tests/temporary_cleaning_008.phpt [deleted file]
Zend/tests/temporary_cleaning_009.phpt [deleted file]
Zend/zend.c
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_globals.h
Zend/zend_opcode.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h
ext/opcache/Optimizer/block_pass.c
ext/opcache/Optimizer/nop_removal.c
ext/opcache/Optimizer/pass1_5.c
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/Optimizer/zend_optimizer_internal.h
ext/opcache/zend_file_cache.c
ext/opcache/zend_persist.c
ext/opcache/zend_persist_calc.c
sapi/phpdbg/phpdbg_opcode.c

diff --git a/NEWS b/NEWS
index 829fd3ca20ee276f2ce6a09e0e827d95e8363885..547ed4667ef4952431534f81ef5c76490a2ada36 100644 (file)
--- a/NEWS
+++ b/NEWS
     ArrayAccess object). (Laruence)
   . Fixed bug #69957 (Different ways of handling div/mod/intdiv). (Bob)
   . Fixed bug #69900 (Too long timeout on pipes). (Anatol)
-  . Fixed bug #62210 (Exceptions can leak temporary variables. As a part of
-    the fix serious refactoring was done. op_array->brk_cont_array was removed,
-    and replaced with more general and speed efficient op_array->T_liveliness.
-    ZEND_GOTO opcode is always replaced by ZEND_JMP at compile time).
-    (Bob, Dmitry, Laruence)
 
 - CLI server:
   . Fixed bug #69655 (php -S changes MKCALENDAR request method to MKCOL). (cmb)
diff --git a/Zend/tests/jump15.phpt b/Zend/tests/jump15.phpt
deleted file mode 100644 (file)
index 456d277..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
---TEST--
-jump 15: 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:
-try {
-       echo "3: ok\n";
-} finally {
-}
-?>
---EXPECT--
-1: ok
-2: ok
-3: ok
diff --git a/Zend/tests/return_types/029.phpt b/Zend/tests/return_types/029.phpt
deleted file mode 100644 (file)
index c854456..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
---TEST--
-Return types must not double free loop variables
---FILE--
-<?php
-
-function foo(): string {
-       foreach ([new stdClass] as $class) {
-               try {
-                       return $class; // invalid return type
-               } catch (TypeError $e) {
-                       return "BAG!";
-               }
-       }
-}
-try {
-       print foo();
-} catch (TypeError $e) {
-       print "no leak or segfault";
-}
-?>
---EXPECT--
-no leak or segfault
diff --git a/Zend/tests/return_types/030.phpt b/Zend/tests/return_types/030.phpt
deleted file mode 100644 (file)
index 73b512e..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
---TEST--
-Return types must not double free loop variables
---FILE--
-<?php
-
-function foo(): string {
-       $a = [];
-       try {
-               return $a; // invalid return type
-       } catch (TypeError $e) {
-               echo "BAG!\n";
-               return "ops!\n";
-       }
-}
-try {
-       echo foo();
-} catch (TypeError $e) {
-       echo "OK\n";
-}
-
-?>
---EXPECT--
-OK
diff --git a/Zend/tests/return_types/031.phpt b/Zend/tests/return_types/031.phpt
deleted file mode 100644 (file)
index 7240274..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
---TEST--
-Excepton on return types mismutch shouldn't execute "catch" code but should execute "finally" code.
---FILE--
-<?php
-
-function foo(): string {
-       $a = "OK\n";
-       try {
-               return $a; // invalid return type
-       } catch (TypeError $e) {
-               echo "BAG!\n";
-               return "ops!\n";
-       } finally {
-               echo "finally1\n";
-       }
-}
-function bar(): string {
-       $a = [];
-       try {
-               return $a; // invalid return type
-       } catch (TypeError $e) {
-               echo "BAG!\n";
-               return "ops!\n";
-       } finally {
-               echo "finally2\n";
-       }
-}
-try {
-       echo foo();
-} catch (TypeError $e) {
-       echo "BAG\n";
-}
-try {
-       echo bar();
-} catch (TypeError $e) {
-       echo "OK\n";
-}
-?>
---EXPECT--
-finally1
-OK
-finally2
-OK
diff --git a/Zend/tests/temporary_cleaning_001.phpt b/Zend/tests/temporary_cleaning_001.phpt
deleted file mode 100644 (file)
index f2ccbb3..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
---TEST--
-Temporary leak on exception
---FILE--
-<?php
-
-function ops() {
-    throw new Exception();
-}
-
-try {
-       $x = 2;
-       $y = new stdClass;
-       while ($x-- && new stdClass) {
-               $r = [$x] + ($y ? ((array) $x) + [2] : ops());
-               $y = (array) $y;
-       }
-} catch (Exception $e) {
-}
-
-?>
-==DONE==
---EXPECT--
-==DONE==
diff --git a/Zend/tests/temporary_cleaning_002.phpt b/Zend/tests/temporary_cleaning_002.phpt
deleted file mode 100644 (file)
index bea54e7..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
---TEST--
-Temporary leak on rope (encapsed string)
---FILE--
-<?php
-class Obj {
-       function __get($x) {
-               throw new Exception();
-       }
-}
-
-$x = new Obj;
-$y = 0;
-
-try {
-       $r = "$y|$x->x|";
-} catch (Exception $e) {
-}
-
-try {
-       $r = "$x->x|$y|";
-} catch (Exception $e) {
-}
-
-try {
-       $r = "$y|$y|$x->x";
-} catch (Exception $e) {
-}
-
-?>
-==DONE==
---EXPECT--
-==DONE==
diff --git a/Zend/tests/temporary_cleaning_003.phpt b/Zend/tests/temporary_cleaning_003.phpt
deleted file mode 100644 (file)
index acff8c8..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
---TEST--
-Fundamental memory leak test on temporaries
---FILE--
-<?php
-
-function ops() {
-    throw new Exception();
-}
-
-try{
-    $x = 1;
-    $r = [$x] + ops();
-} catch (Exception $e) {
-}
-
-?>
-==DONE==
---EXPECT--
-==DONE==
diff --git a/Zend/tests/temporary_cleaning_004.phpt b/Zend/tests/temporary_cleaning_004.phpt
deleted file mode 100644 (file)
index b8a0251..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
---TEST--
-Temporary leak with switch
---FILE--
-<?php
-
-function ops() {
-    throw new Exception();
-}
-
-$a = [new stdClass, new stdClass];
-switch ($a[0]) {
-       case false:
-       break;
-       default:
-               try {
-                       $x = 2;
-                       $y = new stdClass;
-                       while ($x-- && new stdClass) {
-                               $r = [$x] + ($y ? ((array) $x) + [2] : ops());
-                               $y = (array) $y;
-                       }
-               } catch (Exception $e) {
-               }
-}
-
-try {
-       switch ($a[0]) {
-               case false:
-               break;
-               default:
-                       $x = 2;
-                       $y = new stdClass;
-                       while ($x-- && new stdClass) {
-                               $r = [$x] + ($y ? ((array) $x) + [2] : ops());
-                               $y = (array) $y;
-                       }
-       }
-} catch (Exception $e) {
-}
-
-?>
-==DONE==
---EXPECT--
-==DONE==
diff --git a/Zend/tests/temporary_cleaning_005.phpt b/Zend/tests/temporary_cleaning_005.phpt
deleted file mode 100644 (file)
index e8c7feb..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
---TEST--
-Temporary leak with foreach
---FILE--
-<?php
-
-function ops() {
-    throw new Exception();
-}
-
-$a = [new stdClass, new stdClass];
-foreach ([$a, [new stdClass]] as $b) {
-       switch ($b[0]) {
-               case false:
-               break;
-               default:
-                       try {
-                               $x = 2;
-                               $y = new stdClass;
-                               while ($x-- && new stdClass) {
-                                       $r = [$x] + ($y ? ((array) $x) + [2] : ops());
-                                       $y = (array) $y;
-                               }
-                       } catch (Exception $e) {
-                       }
-       }
-}
-
-foreach ([$a, [new stdClass]] as $b) {
-       try {
-               switch ($b[0]) {
-                       case false:
-                       break;
-                       default:
-                               $x = 2;
-                               $y = new stdClass;
-                               while ($x-- && new stdClass) {
-                                       $r = [$x] + ($y ? ((array) $x) + [2] : ops());
-                                       $y = (array) $y;
-                               }
-               }
-       } catch (Exception $e) {
-       }
-}
-
-?>
-==DONE==
---EXPECT--
-==DONE==
diff --git a/Zend/tests/temporary_cleaning_006.phpt b/Zend/tests/temporary_cleaning_006.phpt
deleted file mode 100644 (file)
index 758260d..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
---TEST--
-Exception after separation during indirect write to fcall result
---FILE--
-<?php
-
-function throwing() { throw new Exception; }
-
-function getArray() { return [0]; }
-
-try {
-    getArray()[throwing()] = 1;
-} catch (Exception $e) {
-    echo "Exception\n";
-}
-
-?>
---EXPECT--
-Exception
diff --git a/Zend/tests/temporary_cleaning_007.phpt b/Zend/tests/temporary_cleaning_007.phpt
deleted file mode 100644 (file)
index 0e29ed6..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
---TEST--
-Exception inside a foreach loop with return
---FILE--
-<?php
-class saboteurTestController {
-    public function isConsistent() { throw new \Exception(); }
-}
-
-$controllers = array(new saboteurTestController(),new saboteurTestController());
-foreach ($controllers as $controller) {        
-    try {
-        if ($controller->isConsistent()) {
-            return $controller;
-        }
-    } catch (\Exception $e) {
-        echo "Exception\n";
-    }
-}
-?>
---EXPECT--
-Exception
-Exception
diff --git a/Zend/tests/temporary_cleaning_008.phpt b/Zend/tests/temporary_cleaning_008.phpt
deleted file mode 100644 (file)
index cbbbc6c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
---TEST--
-Exception inside a foreach loop with on an object with destructor
---FILE--
-<?php
-class bar {
-       public $foo = 1;
-       public $bar = 2;
-       function __destruct() {
-               throw new Exception("test");
-       }
-}
-function foo(): string {
-       foreach (new bar() as $foo) {
-               try {
-                       $foo = new Exception;
-                       return;
-               } catch (Exception $e) {
-                       echo "Exception1: " . $e->getMessage() . "\n";
-               } catch (Error $e) {
-                       echo "Error1: " . $e->getMessage() . "\n";
-               }
-       }
-       echo "bag!\n";
-}
-try {
-       foo();
-} catch (Throwable $e) {
-       echo (($e instanceof Exception) ? "Exception2: " : "Error2: ") .
-               $e->getMessage() . "\n";
-       $e = $e->getPrevious();
-       while ($e instanceof Throwable) {
-               echo "\tPrev " . (($e instanceof Exception) ? "Exception2: " : "Error2: ") .
-                       $e->getMessage() . "\n";
-               $e = $e->getPrevious();         
-       }
-}
-echo "ok\n";
-?>
---EXPECTF--
-Exception2: test
-       Prev Error2: Return value of foo() must be of the type string, none returned in %stemporary_cleaning_008.php on line %d
-ok
diff --git a/Zend/tests/temporary_cleaning_009.phpt b/Zend/tests/temporary_cleaning_009.phpt
deleted file mode 100644 (file)
index c6f747e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
---TEST--
-Exception inside a foreach loop with on an object with destructor
---FILE--
-<?php
-class bar {
-       public $foo = 1;
-       function __destruct() {
-               throw new Exception;
-       }
-}
-
-function foo() {
-       foreach (new bar() as &$foo) {
-               try {
-                       $foo = new Exception;
-                       return;
-               } catch (Exception $e) {
-                       echo "Exception1\n";
-               }
-       }
-}
-try {
-       foo();
-} catch (Exception $e) {
-       echo "Exception2\n";
-}
-?>
---EXPECT--
-Exception2
index 200c6a9aa28345b5c4733e011e9717d38cb41afc..168658655c1a3135b5b1a9ee8440204c8e38fbda 100644 (file)
@@ -1045,6 +1045,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
        zval orig_user_error_handler;
        zend_bool in_compilation;
        zend_class_entry *saved_class_entry;
+       zend_stack loop_var_stack;
        zend_stack delayed_oplines_stack;
        zend_array *symbol_table;
 
@@ -1211,6 +1212,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
                        if (in_compilation) {
                                saved_class_entry = CG(active_class_entry);
                                CG(active_class_entry) = NULL;
+                               SAVE_STACK(loop_var_stack);
                                SAVE_STACK(delayed_oplines_stack);
                                CG(in_compilation) = 0;
                        }
@@ -1229,6 +1231,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
 
                        if (in_compilation) {
                                CG(active_class_entry) = saved_class_entry;
+                               RESTORE_STACK(loop_var_stack);
                                RESTORE_STACK(delayed_oplines_stack);
                                CG(in_compilation) = 1;
                        }
index d4b27679406c5a4c131c30b2446cd96d277b2c71..0927f0cd2323a34a48f1840b1d9778c6f7599fe9 100644 (file)
@@ -32,7 +32,6 @@
 #include "zend_multibyte.h"
 #include "zend_language_scanner.h"
 #include "zend_inheritance.h"
-#include "zend_vm.h"
 
 #define SET_NODE(target, src) do { \
                target ## _type = (src)->op_type; \
@@ -211,21 +210,16 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
        CG(context).opcodes_size = INITIAL_OP_ARRAY_SIZE;
        CG(context).vars_size = 0;
        CG(context).literals_size = 0;
+       CG(context).current_brk_cont = -1;
        CG(context).backpatch_count = 0;
        CG(context).in_finally = 0;
        CG(context).fast_call_var = -1;
-       CG(context).current_brk_cont = -1;
-       CG(context).last_brk_cont = 0;
-       CG(context).brk_cont_array = NULL;
        CG(context).labels = NULL;
 }
 /* }}} */
 
 void zend_oparray_context_end(zend_oparray_context *prev_context) /* {{{ */
 {
-       if (CG(context).brk_cont_array) {
-               efree(CG(context).brk_cont_array);
-       }
        if (CG(context).labels) {
                zend_hash_destroy(CG(context).labels);
                FREE_HASHTABLE(CG(context).labels);
@@ -289,6 +283,7 @@ void zend_file_context_end(zend_file_context *prev_context) /* {{{ */
 
 void zend_init_compiler_data_structures(void) /* {{{ */
 {
+       zend_stack_init(&CG(loop_var_stack), sizeof(znode));
        zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op));
        CG(active_class_entry) = NULL;
        CG(in_compilation) = 0;
@@ -321,6 +316,7 @@ void init_compiler(void) /* {{{ */
 
 void shutdown_compiler(void) /* {{{ */
 {
+       zend_stack_destroy(&CG(loop_var_stack));
        zend_stack_destroy(&CG(delayed_oplines_stack));
        zend_hash_destroy(&CG(filenames_table));
        zend_hash_destroy(&CG(const_filenames));
@@ -566,13 +562,17 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
        zend_brk_cont_element *brk_cont_element;
        int parent = CG(context).current_brk_cont;
 
-       CG(context).current_brk_cont = CG(context).last_brk_cont;
+       CG(context).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 (loop_var && (loop_var->op_type & (IS_TMP_VAR|IS_VAR))) {
-               brk_cont_element->loop_var = *loop_var;
+
+       if (loop_var) {
+               zend_stack_push(&CG(loop_var_stack), loop_var);
+               brk_cont_element->start = get_next_op_number(CG(active_op_array));
        } else {
-               brk_cont_element->loop_var.op_type = IS_UNUSED;
+               /* The start field is used to free temporary variables in case of exceptions.
+                * We won't try to free something of we don't have loop variable.  */
+               brk_cont_element->start = -1;
        }
 }
 /* }}} */
@@ -580,10 +580,14 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
 static inline void zend_end_loop(int cont_addr) /* {{{ */
 {
        zend_brk_cont_element *brk_cont_element
-               = &CG(context).brk_cont_array[CG(context).current_brk_cont];
+               = &CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont];
        brk_cont_element->cont = cont_addr;
        brk_cont_element->brk = get_next_op_number(CG(active_op_array));
        CG(context).current_brk_cont = brk_cont_element->parent;
+
+       if (brk_cont_element->start >= 0) {
+               zend_stack_del_top(&CG(loop_var_stack));
+       }
 }
 /* }}} */
 
@@ -870,25 +874,83 @@ static void str_dtor(zval *zv)  /* {{{ */ {
 }
 /* }}} */
 
-static zend_bool zend_is_call(zend_ast *ast);
-
-static void generate_free_loop_var_ex(znode *var, uint32_t flags) /* {{{ */
+void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2) /* {{{ */
 {
-       if (var->op_type != IS_UNUSED) {
-               zend_op *opline = get_next_op(CG(active_op_array));
+       zend_label *dest;
+       int current, distance;
+       zval *label;
+
+       if (pass2) {
+               label = RT_CONSTANT(op_array, opline->op2);
+       } else {
+               label = CT_CONSTANT_EX(op_array, opline->op2.constant);
+       }
+       if (CG(context).labels == NULL ||
+           (dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) {
 
-               opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
-               SET_NODE(opline->op1, var);
+               if (pass2) {
+                       CG(in_compilation) = 1;
+                       CG(active_op_array) = op_array;
+                       CG(zend_lineno) = opline->lineno;
+                       zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label));
+               } else {
+                       /* Label is not defined. Delay to pass 2. */
+                       return;
+               }
+       }
+
+       opline->op1.opline_num = dest->opline_num;
+       zval_dtor(label);
+       ZVAL_NULL(label);
+
+       /* 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_noreturn(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);
-               opline->extended_value = flags;
+       } else {
+               /* Set real break distance */
+               ZVAL_LONG(label, distance);
        }
 }
 /* }}} */
 
-static void generate_free_loop_var(znode *var) /* {{{ */
+static zend_bool zend_is_call(zend_ast *ast);
+
+static int generate_free_loop_var(znode *var) /* {{{ */
 {
-       generate_free_loop_var_ex(var, 0);
+       switch (var->op_type) {
+               case IS_UNUSED:
+                       /* Stack separator on function boundary, stop applying */
+                       return 1;
+               case IS_VAR:
+               case IS_TMP_VAR:
+               {
+                       zend_op *opline = get_next_op(CG(active_op_array));
+
+                       opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
+                       SET_NODE(opline->op1, var);
+                       SET_UNUSED(opline->op2);
+               }
+       }
+
+       return 0;
 }
+/* }}} */
 
 static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
 {
@@ -3452,14 +3514,9 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
 }
 /* }}} */
 
-static void zend_free_foreach_and_switch_variables(uint32_t flags) /* {{{ */
+static void zend_free_foreach_and_switch_variables(void) /* {{{ */
 {
-       int array_offset = CG(context).current_brk_cont;
-       while (array_offset != -1) {
-               zend_brk_cont_element *brk_cont = &CG(context).brk_cont_array[array_offset];
-               generate_free_loop_var_ex(&brk_cont->loop_var, flags);
-               array_offset = brk_cont->parent;
-       }
+       zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var);
 }
 /* }}} */
 
@@ -3481,12 +3538,7 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
                zend_compile_expr(&expr_node, expr_ast);
        }
 
-       /* Generator return types are handled separately */
-       if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
-               zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1);
-       }
-
-       zend_free_foreach_and_switch_variables(ZEND_FREE_ON_RETURN);
+       zend_free_foreach_and_switch_variables();
 
        if (CG(context).in_finally) {
                opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
@@ -3494,6 +3546,10 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
                opline->op1.var = CG(context).fast_call_var;
        }
 
+       /* Generator return types are handled separately */
+       if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+               zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1);
+       }
        opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN,
                &expr_node, NULL);
 
@@ -3562,6 +3618,7 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
        } else {
                int array_offset = CG(context).current_brk_cont;
                zend_long nest_level = depth;
+               znode *loop_var = zend_stack_top(&CG(loop_var_stack));
 
                do {
                        if (array_offset == -1) {
@@ -3570,11 +3627,12 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
                                        depth, depth == 1 ? "" : "s");
                        }
 
-                       if (nest_level > 1) {
-                               generate_free_loop_var_ex(&CG(context).brk_cont_array[array_offset].loop_var, ZEND_FREE_ON_BREAK);
+                       if (nest_level > 1 && CG(active_op_array)->brk_cont_array[array_offset].start >= 0) {
+                               generate_free_loop_var(loop_var);
+                               loop_var--;
                        }
 
-                       array_offset = CG(context).brk_cont_array[array_offset].parent;
+                       array_offset = CG(active_op_array)->brk_cont_array[array_offset].parent;
                } while (--nest_level > 0);
        }
        opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL);
@@ -3583,108 +3641,16 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
 }
 /* }}} */
 
-void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline) /* {{{ */
-{
-       zend_label *dest;
-       int current, distance, free_vars;
-       zval *label;
-
-       if (pass2_opline) {
-               label = RT_CONSTANT(op_array, pass2_opline->op2);
-       } else {
-               label = &label_node->u.constant;
-       }
-       if (CG(context).labels == NULL ||
-           (dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) {
-
-               if (pass2_opline) {
-                       CG(in_compilation) = 1;
-                       CG(active_op_array) = op_array;
-                       CG(zend_lineno) = pass2_opline->lineno;
-                       zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label));
-               } else {
-                       /* Label is not defined. Delay to pass 2. */
-                       zend_op *opline;
-
-                       current = CG(context).current_brk_cont;
-                       while (current != -1) {
-                               if (CG(context).brk_cont_array[current].loop_var.op_type != IS_UNUSED) {
-                                       zend_emit_op(NULL, ZEND_NOP, NULL, NULL);
-                               }
-                               current = CG(context).brk_cont_array[current].parent;
-                       }
-                       opline = zend_emit_op(NULL, ZEND_GOTO, NULL, label_node);
-                       opline->extended_value = CG(context).current_brk_cont;
-                       return;
-               }
-       }
-
-       zval_dtor(label);
-       ZVAL_NULL(label);
-
-       /* Check that we are not moving into loop or switch */
-       if (pass2_opline) {
-               current = pass2_opline->extended_value;
-       } else {
-               current = CG(context).current_brk_cont;
-       }
-       for (distance = 0, free_vars = 0; current != dest->brk_cont; distance++) {
-               if (current == -1) {
-                       if (pass2_opline) {
-                               CG(in_compilation) = 1;
-                               CG(active_op_array) = op_array;
-                               CG(zend_lineno) = pass2_opline->lineno;
-                       }
-                       zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
-               }
-               if (CG(context).brk_cont_array[current].loop_var.op_type != IS_UNUSED) {
-                       if (pass2_opline) {
-                               free_vars++;
-                       } else {
-                               generate_free_loop_var_ex(&CG(context).brk_cont_array[current].loop_var, ZEND_FREE_ON_BREAK);
-                       }
-               }
-               current = CG(context).brk_cont_array[current].parent;
-       }
-
-       if (pass2_opline) {
-               if (free_vars) {
-                       current = pass2_opline->extended_value;
-                       while (current != dest->brk_cont) {
-                               if (CG(context).brk_cont_array[current].loop_var.op_type != IS_UNUSED) {
-                                       zend_op *brk_opline = &op_array->opcodes[CG(context).brk_cont_array[current].brk];
-
-                                       (pass2_opline - free_vars)->opcode = brk_opline->opcode;
-                                       (pass2_opline - free_vars)->op1_type = brk_opline->op1_type;
-                                       if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
-                                               (pass2_opline - free_vars)->op1.var = brk_opline->op1.var;
-                                       } else {
-                                               (pass2_opline - free_vars)->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + brk_opline->op1.var);
-                                               ZEND_VM_SET_OPCODE_HANDLER(pass2_opline - free_vars);
-                                       }
-                                       free_vars--;
-                               }
-                               current = CG(context).brk_cont_array[current].parent;
-                       }
-               }
-               pass2_opline->opcode = ZEND_JMP;
-               pass2_opline->op1.opline_num = dest->opline_num;
-               SET_UNUSED(pass2_opline->op2);
-               pass2_opline->extended_value = 0;
-       } else {
-               zend_op *opline = zend_emit_op(NULL, ZEND_JMP, NULL, NULL);
-               opline->op1.opline_num = dest->opline_num;
-       }
-}
-/* }}} */
-
 void zend_compile_goto(zend_ast *ast) /* {{{ */
 {
        zend_ast *label_ast = ast->child[0];
        znode label_node;
+       zend_op *opline;
 
        zend_compile_expr(&label_node, label_ast);
-       zend_resolve_goto_label(CG(active_op_array), &label_node, NULL);
+       opline = zend_emit_op(NULL, ZEND_GOTO, NULL, &label_node);
+       opline->extended_value = CG(context).current_brk_cont;
+       zend_resolve_goto_label(CG(active_op_array), opline, 0);
 }
 /* }}} */
 
@@ -4780,6 +4746,14 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
                opline_ext->lineno = decl->start_lineno;
        }
 
+       {
+               /* Push a separator to the loop variable stack */
+               znode dummy_var;
+               dummy_var.op_type = IS_UNUSED;
+
+               zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var);
+       }
+
        zend_compile_params(params_ast, return_type_ast);
        if (uses_ast) {
                zend_compile_closure_uses(uses_ast);
@@ -4797,6 +4771,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
        pass_two(CG(active_op_array));
        zend_oparray_context_end(&orig_oparray_context);
 
+       /* Pop the loop variable stack separator */
+       zend_stack_del_top(&CG(loop_var_stack));
+
        CG(active_op_array) = orig_op_array;
 }
 /* }}} */
@@ -6313,7 +6290,10 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
 {
        zend_ast *expr_ast = ast->child[0];
        znode silence_node;
+       uint32_t begin_opline_num, end_opline_num;
+       zend_brk_cont_element *brk_cont_element;
 
+       begin_opline_num = get_next_op_number(CG(active_op_array));
        zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL);
 
        if (expr_ast->kind == ZEND_AST_VAR) {
@@ -6324,7 +6304,15 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
                zend_compile_expr(result, expr_ast);
        }
 
+       end_opline_num = get_next_op_number(CG(active_op_array));
        zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL);
+
+       /* Store BEGIN_SILENCE/END_SILENCE pair to restore previous
+        * EG(error_reporting) value on exception */
+       brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
+       brk_cont_element->start = begin_opline_num;
+       brk_cont_element->cont = brk_cont_element->brk = end_opline_num;
+       brk_cont_element->parent = -1;
 }
 /* }}} */
 
@@ -6652,6 +6640,10 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
                GET_NODE(result, opline->result);
        } else {
                uint32_t var;
+               zend_brk_cont_element *info = get_next_brk_cont_element(CG(active_op_array));
+               info->start = rope_init_lineno;
+               info->parent = CG(context).current_brk_cont;
+               info->cont = info->brk = opline - CG(active_op_array)->opcodes;
 
                init_opline->extended_value = j;
                opline->opcode = ZEND_ROPE_END;
index 52456a79e97b21c2e26155f94af9f51c5d8fbe47..a3bc17f4256dd828e58ef9941683c11213e090be 100644 (file)
@@ -109,36 +109,15 @@ typedef struct _zend_declarables {
        zend_long ticks;
 } zend_declarables;
 
-typedef struct _zend_brk_cont_element {
-       int cont;
-       int brk;
-       int parent;
-       znode loop_var;
-} zend_brk_cont_element;
-
-typedef struct _zend_label {
-       int brk_cont;
-       uint32_t opline_num;
-} zend_label;
-
-typedef struct _zend_try_catch_element {
-       uint32_t try_op;
-       uint32_t catch_op;  /* ketchup! */
-       uint32_t finally_op;
-       uint32_t finally_end;
-} zend_try_catch_element;
-
 /* Compilation context that is different for each op array. */
 typedef struct _zend_oparray_context {
        uint32_t   opcodes_size;
        int        vars_size;
        int        literals_size;
+       int        current_brk_cont;
        int        backpatch_count;
        int        in_finally;
        uint32_t   fast_call_var;
-       int        current_brk_cont;
-       int        last_brk_cont;
-       zend_brk_cont_element *brk_cont_array;
        HashTable *labels;
 } zend_oparray_context;
 
@@ -184,6 +163,26 @@ struct _zend_op {
        zend_uchar result_type;
 };
 
+
+typedef struct _zend_brk_cont_element {
+       int start;
+       int cont;
+       int brk;
+       int parent;
+} zend_brk_cont_element;
+
+typedef struct _zend_label {
+       int brk_cont;
+       uint32_t opline_num;
+} zend_label;
+
+typedef struct _zend_try_catch_element {
+       uint32_t try_op;
+       uint32_t catch_op;  /* ketchup! */
+       uint32_t finally_op;
+       uint32_t finally_end;
+} zend_try_catch_element;
+
 /* method flags (types) */
 #define ZEND_ACC_STATIC                        0x01
 #define ZEND_ACC_ABSTRACT              0x02
@@ -329,11 +328,6 @@ typedef struct _zend_internal_function_info {
        zend_bool _is_variadic;
 } zend_internal_function_info;
 
-#define ZEND_LIVE_ROPE    1
-#define ZEND_LIVE_SILENCE 2
-#define ZEND_LIVE_LOOP    3
-#define ZEND_LIVE_MASK    3
-
 struct _zend_op_array {
        /* Common elements */
        zend_uchar type;
@@ -357,12 +351,10 @@ struct _zend_op_array {
        int last_var;
        uint32_t T;
        zend_string **vars;
-       uint32_t *T_liveliness;
-
-       void **run_time_cache;
-       int  cache_size;
 
+       int last_brk_cont;
        int last_try_catch;
+       zend_brk_cont_element *brk_cont_array;
        zend_try_catch_element *try_catch_array;
 
        /* static variables support */
@@ -377,6 +369,9 @@ struct _zend_op_array {
        int last_literal;
        zval *literals;
 
+       int  cache_size;
+       void **run_time_cache;
+
        void *reserved[ZEND_MAX_RESERVED_RESOURCES];
 };
 
@@ -717,7 +712,7 @@ void zend_do_extended_fcall_end(void);
 
 void zend_verify_namespace(void);
 
-void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline);
+void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2);
 
 ZEND_API void function_add_ref(zend_function *function);
 
@@ -732,8 +727,6 @@ ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...);
 ZEND_API int open_file_for_scanning(zend_file_handle *file_handle);
 ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size);
 ZEND_API void destroy_op_array(zend_op_array *op_array);
-ZEND_API void zend_generate_var_liveliness_info(zend_op_array *op_array);
-ZEND_API void zend_regenerate_var_liveliness_info(zend_op_array *op_array);
 ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
 ZEND_API void zend_cleanup_user_class_data(zend_class_entry *ce);
 ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);
@@ -882,10 +875,7 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
 
 #define ZEND_FETCH_ARG_MASK         0x000fffff
 
-#define ZEND_FREE_ON_RETURN     (1<<0)
-#define ZEND_FREE_ON_BREAK      (1<<1)
-
-#define ZEND_MEMBER_FUNC_CALL  (1<<0)
+#define ZEND_MEMBER_FUNC_CALL  1<<0
 
 #define ZEND_ARG_SEND_BY_REF (1<<0)
 #define ZEND_ARG_COMPILE_TIME_BOUND (1<<1)
@@ -961,7 +951,6 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
 #define ZEND_ARRAY_SIZE_SHIFT          2
 
 /* Pseudo-opcodes that are used only temporarily during compilation */
-#define ZEND_GOTO 253
 #define ZEND_BRK 254
 #define ZEND_CONT 255
 
index 7aa34bb2df8a442251297fa86b0114a406a9ee85..257f627d22578d068a1205150f60220798968af9 100644 (file)
@@ -1939,6 +1939,31 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
        }
 }
 
+static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_offset, const zend_op_array *op_array, const zend_execute_data *execute_data)
+{
+       zend_brk_cont_element *jmp_to;
+
+       do {
+               ZEND_ASSERT(array_offset != -1);
+               jmp_to = &op_array->brk_cont_array[array_offset];
+               if (nest_levels > 1 && jmp_to->start >= 0) {
+                       zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
+
+                       if (brk_opline->opcode == ZEND_FREE) {
+                               zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
+                       } else if (brk_opline->opcode == ZEND_FE_FREE) {
+                               zval *var = EX_VAR(brk_opline->op1.var);
+                               if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
+                                       zend_hash_iterator_del(Z_FE_ITER_P(var));
+                               }
+                               zval_ptr_dtor_nogc(var);
+                       }
+               }
+               array_offset = jmp_to->parent;
+       } while (--nest_levels > 0);
+       return jmp_to;
+}
+
 #if ZEND_INTENSIVE_DEBUGGING
 
 #define CHECK_SYMBOL_TABLES()                                                                                                  \
@@ -2359,66 +2384,7 @@ static zend_always_inline zend_generator *zend_get_running_generator(zend_execut
 
 static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */
 {
-       if (EX(func)->op_array.T_liveliness
-        && op_num < EX(func)->op_array.last
-        && EX(func)->op_array.T_liveliness[op_num] != (uint32_t)-1) {
-               uint32_t *off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num];
-               uint32_t *catch_off = NULL;
-               uint32_t var = *off;
-
-               if (catch_op_num && EX(func)->op_array.T_liveliness[catch_op_num] != (uint32_t)-1) {
-                       catch_off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[catch_op_num];
-               }
-
-               do {
-                       /* we should be safe to assume that all temporaries at catch_op_num will be present at op_num too, in same order */
-                       if (catch_off && *catch_off == var) {
-                               catch_off++;
-                               var = *(++off);
-                               continue;
-                       }
-
-                       if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_ROPE) {
-                               /* free incomplete rope */
-                               zend_string **rope;
-                               zend_op *last;
-
-                               var = var & ~ZEND_LIVE_ROPE;
-                               rope = (zend_string **) EX_VAR(var);
-                               last = EX(func)->op_array.opcodes + op_num;
-                               while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
-                                               || last->result.var != var) {
-                                       ZEND_ASSERT(last >= EX(func)->op_array.opcodes);
-                                       last--;
-                               }
-                               if (last->opcode == ZEND_ROPE_INIT) {
-                                       zend_string_release(*rope);
-                               } else {
-                                       int j = last->extended_value;
-                                       do {
-                                               zend_string_release(rope[j]);
-                                       } while (j--);
-                               }
-                       } else if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_SILENCE) {
-                               /* restore previous error_reporting value */
-                               var = var & ~ZEND_LIVE_SILENCE;
-                               if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(var)) != 0) {
-                                       EG(error_reporting) = Z_LVAL_P(EX_VAR(var));
-                               }
-                       } else if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_LOOP) {
-                               /* free loop variables */
-                               var = var & ~ZEND_LIVE_LOOP;
-                               if (Z_TYPE_P(EX_VAR(var)) != IS_ARRAY && Z_FE_ITER_P(EX_VAR(var)) != (uint32_t) -1) {
-                                       zend_hash_iterator_del(Z_FE_ITER_P(EX_VAR(var)));
-                               }
-                               zval_ptr_dtor_nogc(EX_VAR(var));
-                       } else {
-                               zval_ptr_dtor_nogc(EX_VAR(var));
-                       }
-                       var = *(++off);
-               } while (var != (uint32_t)-1);
-       }
-
+       int i;
        if (UNEXPECTED(EX(call))) {
                zend_execute_data *call = EX(call);
                zend_op *opline = EX(func)->op_array.opcodes + op_num;
@@ -2532,6 +2498,51 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
                        call = EX(call);
                } while (call);
        }
+
+       for (i = 0; i < EX(func)->op_array.last_brk_cont; i++) {
+               const zend_brk_cont_element *brk_cont = &EX(func)->op_array.brk_cont_array[i];
+               if (brk_cont->start < 0) {
+                       continue;
+               } else if (brk_cont->start > op_num) {
+                       /* further blocks will not be relevant... */
+                       break;
+               } else if (op_num < brk_cont->brk) {
+                       if (!catch_op_num || catch_op_num >= brk_cont->brk) {
+                               zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk];
+
+                               if (brk_opline->opcode == ZEND_FREE) {
+                                       zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
+                               } else if (brk_opline->opcode == ZEND_FE_FREE) {
+                                       zval *var = EX_VAR(brk_opline->op1.var);
+                                       if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
+                                               zend_hash_iterator_del(Z_FE_ITER_P(var));
+                                       }
+                                       zval_ptr_dtor_nogc(var);
+                               } else if (brk_opline->opcode == ZEND_ROPE_END) {
+                                       zend_string **rope = (zend_string **) EX_VAR(brk_opline->op1.var);
+                                       zend_op *last = EX(func)->op_array.opcodes + op_num;
+                                       while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
+                                                       || last->result.var != brk_opline->op1.var) {
+                                               ZEND_ASSERT(last >= EX(func)->op_array.opcodes);
+                                               last--;
+                                       }
+                                       if (last->opcode == ZEND_ROPE_INIT) {
+                                               zend_string_release(*rope);
+                                       } else {
+                                               int j = last->extended_value;
+                                               do {
+                                                       zend_string_release(rope[j]);
+                                               } while (j--);
+                                       }
+                               } else if (brk_opline->opcode == ZEND_END_SILENCE) {
+                                       /* restore previous error_reporting value */
+                                       if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {
+                                               EG(error_reporting) = Z_LVAL_P(EX_VAR(brk_opline->op1.var));
+                                       }
+                               }
+                       }
+               }
+       }
 }
 /* }}} */
 
index 780007174eae9cc572c9d0f44cf7d80103efcfa4..28487a2a4a1859894569efc42f9fd9fa0b179650 100644 (file)
@@ -68,6 +68,8 @@ typedef struct _zend_ini_entry zend_ini_entry;
 
 
 struct _zend_compiler_globals {
+       zend_stack loop_var_stack;
+
        zend_class_entry *active_class_entry;
 
        zend_string *compiled_filename;
index 5fe5ae7f33041cb8395bf875c3e1480c1da221c6..a971a5e90047da8fff99973aff571e4cb6f78c5a 100644 (file)
@@ -65,7 +65,6 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
        op_array->vars = NULL;
 
        op_array->T = 0;
-       op_array->T_liveliness = NULL;
 
        op_array->function_name = NULL;
        op_array->filename = zend_get_compiled_filename();
@@ -78,7 +77,9 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
        op_array->scope = NULL;
        op_array->prototype = NULL;
 
+       op_array->brk_cont_array = NULL;
        op_array->try_catch_array = NULL;
+       op_array->last_brk_cont = 0;
 
        op_array->static_variables = NULL;
        op_array->last_try_catch = 0;
@@ -382,12 +383,12 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
        if (op_array->doc_comment) {
                zend_string_release(op_array->doc_comment);
        }
+       if (op_array->brk_cont_array) {
+               efree(op_array->brk_cont_array);
+       }
        if (op_array->try_catch_array) {
                efree(op_array->try_catch_array);
        }
-       if (op_array->T_liveliness) {
-               efree(op_array->T_liveliness);
-       }
        if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
                zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array);
        }
@@ -446,9 +447,9 @@ int get_next_op_number(zend_op_array *op_array)
 
 zend_brk_cont_element *get_next_brk_cont_element(zend_op_array *op_array)
 {
-       CG(context).last_brk_cont++;
-       CG(context).brk_cont_array = erealloc(CG(context).brk_cont_array, sizeof(zend_brk_cont_element)*CG(context).last_brk_cont);
-       return &CG(context).brk_cont_array[CG(context).last_brk_cont-1];
+       op_array->last_brk_cont++;
+       op_array->brk_cont_array = erealloc(op_array->brk_cont_array, sizeof(zend_brk_cont_element)*op_array->last_brk_cont);
+       return &op_array->brk_cont_array[op_array->last_brk_cont-1];
 }
 
 static void zend_update_extended_info(zend_op_array *op_array)
@@ -575,7 +576,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, uint32_t op_num,
                        fast_call_var = op_array->opcodes[op_array->try_catch_array[i].finally_end].op1.var;
 
                        /* generate a FAST_CALL to finally block */
-                       start_op = get_next_op_number(op_array);
+                   start_op = get_next_op_number(op_array);
 
                        opline = get_next_op(op_array);
                        opline->opcode = ZEND_FAST_CALL;
@@ -671,7 +672,7 @@ static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const ze
        int array_offset = opline->op1.num;
        zend_brk_cont_element *jmp_to;
        do {
-               jmp_to = &CG(context).brk_cont_array[array_offset];
+               jmp_to = &op_array->brk_cont_array[array_offset];
                if (nest_levels > 1) {
                        array_offset = jmp_to->parent;
                }
@@ -699,8 +700,11 @@ static void zend_resolve_finally_calls(zend_op_array *op_array)
                                break;
                        case ZEND_GOTO:
                                if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) != IS_LONG) {
+                                       uint32_t num = opline->op2.constant;
+
                                        ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
-                                       zend_resolve_goto_label(op_array, NULL, opline);
+                                       zend_resolve_goto_label(op_array, opline, 1);
+                                       opline->op2.constant = num;
                                }
                                /* break omitted intentionally */
                        case ZEND_JMP:
@@ -747,9 +751,6 @@ ZEND_API int pass_two(zend_op_array *op_array)
                op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal);
                CG(context).literals_size = op_array->last_literal;
        }
-
-       zend_generate_var_liveliness_info(op_array);
-
        opline = op_array->opcodes;
        end = opline + op_array->last;
        while (opline < end) {
@@ -786,7 +787,7 @@ ZEND_API int pass_two(zend_op_array *op_array)
                                break;
                        case ZEND_GOTO:
                                if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op2)) != IS_LONG) {
-                                       zend_resolve_goto_label(op_array, NULL, opline);
+                                       zend_resolve_goto_label(op_array, opline, 1);
                                }
                                /* break omitted intentionally */
                        case ZEND_JMP:
@@ -839,227 +840,6 @@ int pass_two_wrapper(zval *el)
        return pass_two((zend_op_array *) Z_PTR_P(el));
 }
 
-/* The following liveliness analyzing algorithm assumes that
- * 1) temporary variables are defined before use
- * 2) they have linear live-ranges without "holes"
- * 3) Opcodes never use and define the same temorary variables
- */
-typedef struct _op_var_info {
-       struct _op_var_info *next;
-       uint32_t var;
-} op_var_info;
-
-static zend_always_inline uint32_t liveliness_kill_var(zend_op_array *op_array, zend_op *cur_op, uint32_t var, uint32_t *Tstart, op_var_info **opTs)
-{
-       uint32_t start = Tstart[var];
-       uint32_t end = cur_op - op_array->opcodes;
-       uint32_t count = 0;
-       uint32_t var_offset, j;
-
-       Tstart[var] = -1;
-       if (cur_op->opcode == ZEND_OP_DATA) {
-               end--;
-       }
-       start++;
-       if (op_array->opcodes[start].opcode == ZEND_OP_DATA
-        || op_array->opcodes[start].opcode == ZEND_FE_FETCH_R
-        || op_array->opcodes[start].opcode == ZEND_FE_FETCH_RW) {
-               start++;
-       }
-       if (start < end) {
-               op_var_info *new_opTs;
-
-               var_offset = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + var);
-               if (op_array->opcodes[end].opcode == ZEND_ROPE_END) {
-                       var_offset |= ZEND_LIVE_ROPE;
-               } else if (op_array->opcodes[end].opcode == ZEND_END_SILENCE) {
-                       var_offset |= ZEND_LIVE_SILENCE;
-               } else if (op_array->opcodes[end].opcode == ZEND_FE_FREE) {
-                       var_offset |= ZEND_LIVE_LOOP;
-               }
-
-               if (opTs[start]) {
-                       if (start > 0 && opTs[start-1] == opTs[start]) {
-                               op_var_info *opT = opTs[start];
-                               do {
-                                       count++;
-                                       opT = opT->next;
-                               } while (opT);
-                               count += 2;
-                       } else {
-                               count++;
-                       }
-               } else {
-                       count += 2;
-               }
-
-               new_opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
-               new_opTs->next = opTs[start];
-               new_opTs->var = var_offset;
-               opTs[start] = new_opTs;
-
-               for (j = start + 1; j < end; j++) {
-                       if (opTs[j-1]->next == opTs[j]) {
-                               opTs[j] = opTs[j-1];
-                       } else {
-                           if (opTs[j]) {
-                                       count++;
-                           } else {
-                                       count += 2;
-                               }
-                               new_opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
-                               new_opTs->next = opTs[j];
-                               new_opTs->var = var_offset;
-                               opTs[j] = new_opTs;
-                       }
-               }
-       }
-
-       return count;
-}
-
-static zend_always_inline uint32_t *generate_var_liveliness_info_ex(zend_op_array *op_array, zend_bool done_pass_two)
-{
-       zend_op      *opline, *end;
-       uint32_t      var, i, op_live_total = 0;
-       uint32_t     *info, info_off = op_array->last + 1;
-       void         *checkpoint = zend_arena_checkpoint(CG(arena));
-       uint32_t     *Tstart = zend_arena_alloc(&CG(arena), sizeof(uint32_t) * op_array->T);
-       op_var_info **opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info *) * op_array->last);
-
-       memset(Tstart, -1, sizeof(uint32_t) * op_array->T);
-       memset(opTs, 0, sizeof(op_var_info *) * op_array->last);
-
-       opline = op_array->opcodes;
-       end = opline + op_array->last;
-       do {
-               if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
-                       && !((opline)->result_type & EXT_TYPE_UNUSED)
-                       /* the following opcodes are used in inline branching
-                        * (and anyway always bool, so no need to free) and may
-                        * not be defined depending on the taken branch */
-                       && opline->opcode != ZEND_BOOL
-                       && opline->opcode != ZEND_JMPZ_EX
-                       && opline->opcode != ZEND_JMPNZ_EX
-                       /* These opcodes write the result of the true branch of a ternary, short
-                        * ternary or coalesce and are immediately followed by the instructions
-                        * for the false branch (where this result is not live) */
-                       && (opline->opcode != ZEND_QM_ASSIGN || (opline + 1)->opcode != ZEND_JMP)
-                       && opline->opcode != ZEND_JMP_SET
-                       && opline->opcode != ZEND_COALESCE
-                       /* exception for opcache, it might nowhere use the temporary
-                        * (anyway bool, so no need to free) */
-                       && opline->opcode != ZEND_CASE
-                       /* the following opcodes reuse TMP created before */
-                       && opline->opcode != ZEND_ROPE_ADD
-                       && opline->opcode != ZEND_ADD_ARRAY_ELEMENT
-                       && opline->opcode != ZEND_SEPARATE
-                       /* passes fast_call */
-                       && opline->opcode != ZEND_FAST_CALL
-                       /* the following opcodes pass class_entry */
-                       && opline->opcode != ZEND_FETCH_CLASS
-                       && opline->opcode != ZEND_DECLARE_CLASS
-                       && opline->opcode != ZEND_DECLARE_INHERITED_CLASS
-                       && opline->opcode != ZEND_DECLARE_INHERITED_CLASS_DELAYED
-                       && opline->opcode != ZEND_DECLARE_ANON_CLASS
-                       && opline->opcode != ZEND_DECLARE_ANON_INHERITED_CLASS
-               ) {
-                       if (done_pass_two) {
-                               var = EX_VAR_TO_NUM(opline->result.var) - op_array->last_var;
-                       } else {
-                               var = opline->result.var;
-                       }
-                       ZEND_ASSERT(Tstart[var] == (unsigned) -1);
-                       if (opline->opcode == ZEND_NEW) {
-                               /* Objects created via ZEND_NEW are only fully initialized
-                                * after the DO_FCALL (constructor call) */
-                               Tstart[var] = opline->op2.opline_num - 1;
-                       } else {
-                               Tstart[var] = opline - op_array->opcodes;
-                       }
-               }
-               if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
-                       if (done_pass_two) {
-                               var = EX_VAR_TO_NUM(opline->op1.var) - op_array->last_var;
-                       } else {
-                               var = opline->op1.var;
-                       }
-                       if (Tstart[var] != (uint32_t)-1
-                               /* the following opcodes don't free TMP */
-                               && opline->opcode != ZEND_ROPE_ADD
-                               && opline->opcode != ZEND_SEPARATE
-                               && opline->opcode != ZEND_FETCH_LIST
-                               && opline->opcode != ZEND_CASE
-                               && opline->opcode != ZEND_FE_FETCH_R
-                               && opline->opcode != ZEND_FE_FETCH_RW
-                               /* the following opcodes are parts of "return" statement */
-                               && opline->opcode != ZEND_VERIFY_RETURN_TYPE
-                               && (opline->opcode != ZEND_FREE || !(opline->extended_value & (ZEND_FREE_ON_RETURN|ZEND_FREE_ON_BREAK)))
-                               && (opline->opcode != ZEND_FE_FREE || !(opline->extended_value & (ZEND_FREE_ON_RETURN|ZEND_FREE_ON_BREAK)))
-                       ) {
-                               op_live_total += liveliness_kill_var(op_array, opline, var, Tstart, opTs);
-                       }
-               }
-               if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
-                       if (done_pass_two) {
-                               var = EX_VAR_TO_NUM(opline->op2.var) - op_array->last_var;
-                       } else {
-                               var = opline->op2.var;
-                       }
-                       if (Tstart[var] != (uint32_t)-1) {
-                               op_live_total += liveliness_kill_var(op_array, opline, var, Tstart, opTs);
-                       }
-               }
-       } while (++opline != end);
-
-#if 0
-       /* Check that all TMP variable live-ranges are closed */
-       for (i = 0; i < op_array->T; i++) {
-               ZEND_ASSERT(Tstart[i] == (uint32_t)-1);
-       }
-#endif
-
-       if (!op_live_total) {
-               info = NULL;
-       } else {
-               info = emalloc((op_array->last + 1 + op_live_total) * sizeof(uint32_t));
-
-               for (i = 0; i < op_array->last; i++) {
-                       if (!opTs[i]) {
-                               info[i] = (uint32_t)-1;
-                       } else if (i > 0 && opTs[i-1] == opTs[i]) {
-                               info[i] = info[i-1];
-                       } else {
-                               op_var_info *opT = opTs[i];
-                               info[i] = info_off;
-                               while (opT) {
-                                       info[info_off++] = opT->var;
-                                       opT = opT->next;
-                               }
-                               info[info_off++] = (uint32_t)-1;
-                       }
-               }
-               info[op_array->last] = info_off;
-               ZEND_ASSERT(info_off == op_array->last + 1 + op_live_total);
-       }
-
-       zend_arena_release(&CG(arena), checkpoint);
-       return info;
-}
-
-ZEND_API void zend_generate_var_liveliness_info(zend_op_array *op_array)
-{
-       op_array->T_liveliness = generate_var_liveliness_info_ex(op_array, 0);
-}
-
-ZEND_API void zend_regenerate_var_liveliness_info(zend_op_array *op_array)
-{
-       if (op_array->T_liveliness) {
-               efree(op_array->T_liveliness);
-       }
-       op_array->T_liveliness = generate_var_liveliness_info_ex(op_array, 1);
-}
-
 int print_class(zend_class_entry *class_entry)
 {
        printf("Class %s:\n", ZSTR_VAL(class_entry->name));
index 98f6be671aee8438e0a4a455f0a33811060a15fd..b94614e6598fc94128fffb626aba4df749b1d300 100644 (file)
@@ -2610,13 +2610,10 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, ANY)
 
 ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
 {
-       zval *var;
        USE_OPLINE
 
        SAVE_OPLINE();
-       var = EX_VAR(opline->op1.var);
-       zval_ptr_dtor_nogc(var);
-       ZVAL_NULL(var);
+       zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
@@ -2629,10 +2626,8 @@ ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
        var = EX_VAR(opline->op1.var);
        if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
                zend_hash_iterator_del(Z_FE_ITER_P(var));
-               Z_FE_ITER_P(var) = (uint32_t)-1;
        }
        zval_ptr_dtor_nogc(var);
-       ZVAL_NULL(var);
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
@@ -3888,9 +3883,6 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
 
                if (UNEXPECTED(EG(exception) != NULL)) {
                        FREE_OP1();
-                       if (OP1_TYPE == IS_TMP_VAR || OP1_TYPE == IS_VAR) {
-                               ZVAL_NULL(retval_ref);
-                       }
                }
 #endif
        }
@@ -4795,6 +4787,31 @@ ZEND_VM_HANDLER(52, ZEND_BOOL, CONST|TMPVAR|CV, ANY)
        ZEND_VM_NEXT_OPCODE();
 }
 
+ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST)
+{
+       USE_OPLINE
+       zend_brk_cont_element *el;
+
+       SAVE_OPLINE();
+       el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value,
+                          &EX(func)->op_array, execute_data);
+
+       if (el->start >= 0) {
+               zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk;
+
+               if (brk_opline->opcode == ZEND_FREE) {
+                       zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
+               } else if (brk_opline->opcode == ZEND_FE_FREE) {
+                       zval *var = EX_VAR(brk_opline->op1.var);
+                       if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
+                               zend_hash_iterator_del(Z_FE_ITER_P(var));
+                       }
+                       zval_ptr_dtor_nogc(var);
+               }
+       }
+       ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+}
+
 ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
 {
        USE_OPLINE
@@ -7082,15 +7099,6 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
                }
        }
 
-       if (catch_op_num) {
-               if (EX(func)->op_array.opcodes[op_num].opcode == ZEND_VERIFY_RETURN_TYPE
-                || (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
-                || (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FE_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
-               ) {
-                       catch_op_num = 0;
-               }
-       }
-
        i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
 
        if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
index 95c1b5dfaa1905602987b32875eba5b5ed2fa485..828babb98d7bda237d665bfd422e8ac89bf40a22 100644 (file)
@@ -1500,15 +1500,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
                }
        }
 
-       if (catch_op_num) {
-               if (EX(func)->op_array.opcodes[op_num].opcode == ZEND_VERIFY_RETURN_TYPE
-                || (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
-                || (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FE_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
-               ) {
-                       catch_op_num = 0;
-               }
-       }
-
        i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
 
        if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
@@ -2228,6 +2219,31 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(Z
        ZEND_VM_NEXT_OPCODE();
 }
 
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+       zend_brk_cont_element *el;
+
+       SAVE_OPLINE();
+       el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value,
+                          &EX(func)->op_array, execute_data);
+
+       if (el->start >= 0) {
+               zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk;
+
+               if (brk_opline->opcode == ZEND_FREE) {
+                       zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
+               } else if (brk_opline->opcode == ZEND_FE_FREE) {
+                       zval *var = EX_VAR(brk_opline->op1.var);
+                       if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
+                               zend_hash_iterator_del(Z_FE_ITER_P(var));
+                       }
+                       zval_ptr_dtor_nogc(var);
+               }
+       }
+       ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+}
+
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
@@ -7749,9 +7765,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_
 
                if (UNEXPECTED(EG(exception) != NULL)) {
 
-                       if (IS_CONST == IS_TMP_VAR || IS_CONST == IS_VAR) {
-                               ZVAL_NULL(retval_ref);
-                       }
                }
 #endif
        }
@@ -13548,9 +13561,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
 
                if (UNEXPECTED(EG(exception) != NULL)) {
                        zval_ptr_dtor_nogc(free_op1);
-                       if (IS_TMP_VAR == IS_TMP_VAR || IS_TMP_VAR == IS_VAR) {
-                               ZVAL_NULL(retval_ref);
-                       }
                }
 #endif
        }
@@ -19240,9 +19250,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
 
                if (UNEXPECTED(EG(exception) != NULL)) {
                        zval_ptr_dtor_nogc(free_op1);
-                       if (IS_VAR == IS_TMP_VAR || IS_VAR == IS_VAR) {
-                               ZVAL_NULL(retval_ref);
-                       }
                }
 #endif
        }
@@ -24928,9 +24935,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
 
                if (UNEXPECTED(EG(exception) != NULL)) {
 
-                       if (IS_UNUSED == IS_TMP_VAR || IS_UNUSED == IS_VAR) {
-                               ZVAL_NULL(retval_ref);
-                       }
                }
 #endif
        }
@@ -34367,9 +34371,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
 
                if (UNEXPECTED(EG(exception) != NULL)) {
 
-                       if (IS_CV == IS_TMP_VAR || IS_CV == IS_VAR) {
-                               ZVAL_NULL(retval_ref);
-                       }
                }
 #endif
        }
@@ -40188,13 +40189,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER(Z
 
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
-       zval *var;
        USE_OPLINE
 
        SAVE_OPLINE();
-       var = EX_VAR(opline->op1.var);
-       zval_ptr_dtor_nogc(var);
-       ZVAL_NULL(var);
+       zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
@@ -40207,10 +40205,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZE
        var = EX_VAR(opline->op1.var);
        if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
                zend_hash_iterator_del(Z_FE_ITER_P(var));
-               Z_FE_ITER_P(var) = (uint32_t)-1;
        }
        zval_ptr_dtor_nogc(var);
-       ZVAL_NULL(var);
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
@@ -47691,27 +47687,27 @@ void zend_init_opcodes_handlers(void)
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
+       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
+       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
+       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
+       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
-       ZEND_NULL_HANDLER,
+       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
index 1f26f0439e42d2a90cb83919b9663b96acfc889b..705ab9cd29c8fc8527b0f7690321beae0e17e662 100644 (file)
@@ -122,7 +122,7 @@ const char *zend_vm_opcodes_map[173] = {
        "ZEND_FETCH_OBJ_UNSET",
        "ZEND_FETCH_LIST",
        "ZEND_FETCH_CONSTANT",
-       NULL,
+       "ZEND_GOTO",
        "ZEND_EXT_STMT",
        "ZEND_EXT_FCALL_BEGIN",
        "ZEND_EXT_FCALL_END",
index bb2d7717bdc179b1195bf5ec099713f290c679c0..f6de5b1b577ea293d0ff52d688e4d9fc6e018d1f 100644 (file)
@@ -130,6 +130,7 @@ END_EXTERN_C()
 #define ZEND_FETCH_OBJ_UNSET                  97
 #define ZEND_FETCH_LIST                       98
 #define ZEND_FETCH_CONSTANT                   99
+#define ZEND_GOTO                            100
 #define ZEND_EXT_STMT                        101
 #define ZEND_EXT_FCALL_BEGIN                 102
 #define ZEND_EXT_FCALL_END                   103
index dfe0e4baef9d5e6cef205be1849c7bf9750d7df5..e1e07ea01b69a4d4908e4294caa643c048f738a6 100644 (file)
@@ -123,6 +123,10 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
        blocks[0].start_opline_no = 0;
        while (opline < end) {
                switch((unsigned)opline->opcode) {
+                       case ZEND_GOTO:
+                               /* would not optimize GOTOs - we cannot really know where it jumps,
+                                * so these optimizations are too dangerous */
+                               return 0;
                        case ZEND_FAST_CALL:
                                START_BLOCK_OP(ZEND_OP1(opline).opline_num);
                                if (opline->extended_value) {
@@ -193,6 +197,65 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
                        blocks[op_array->try_catch_array[i].try_op].protected = 1;
                }
        }
+       /* Currently, we don't optimize op_arrays with BRK/CONT/GOTO opcodes,
+        * but, we have to keep brk_cont_array to avoid memory leaks during
+        * exception handling */
+       if (op_array->last_brk_cont) {
+               int i, j;
+
+               j = 0;
+               for (i = 0; i< op_array->last_brk_cont; i++) {
+                       if (op_array->brk_cont_array[i].start >= 0 &&
+                           (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
+                            op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
+                            op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
+                            op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
+                               int parent = op_array->brk_cont_array[i].parent;
+
+                               while (parent >= 0 &&
+                                      op_array->brk_cont_array[parent].start < 0 &&
+                                      (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE ||
+                                       op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE ||
+                                            op_array->opcodes[op_array->brk_cont_array[i].brk].opcode != ZEND_ROPE_END ||
+                                       op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) {
+                                       parent = op_array->brk_cont_array[parent].parent;
+                               }
+                               op_array->brk_cont_array[i].parent = parent;
+                               j++;
+                       }
+               }
+               if (j) {
+                       cfg->loop_start = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
+                       cfg->loop_cont  = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
+                       cfg->loop_brk   = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
+                       j = 0;
+                       for (i = 0; i< op_array->last_brk_cont; i++) {
+                               if (op_array->brk_cont_array[i].start >= 0 &&
+                                   (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
+                                    op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
+                                    op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
+                                    op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
+                                       if (i != j) {
+                                               op_array->brk_cont_array[j] = op_array->brk_cont_array[i];
+                                       }
+                                       cfg->loop_start[j] = &blocks[op_array->brk_cont_array[j].start];
+                                       cfg->loop_cont[j]  = &blocks[op_array->brk_cont_array[j].cont];
+                                       cfg->loop_brk[j]   = &blocks[op_array->brk_cont_array[j].brk];
+                                       START_BLOCK_OP(op_array->brk_cont_array[j].start);
+                                       START_BLOCK_OP(op_array->brk_cont_array[j].cont);
+                                       START_BLOCK_OP(op_array->brk_cont_array[j].brk);
+                                       blocks[op_array->brk_cont_array[j].start].protected = 1;
+                                       blocks[op_array->brk_cont_array[j].brk].protected = 1;
+                                       j++;
+                               }
+                       }
+                       op_array->last_brk_cont = j;
+               } else {
+                       efree(op_array->brk_cont_array);
+                       op_array->brk_cont_array = NULL;
+                       op_array->last_brk_cont = 0;
+               }
+       }
 
        /* Build CFG (Control Flow Graph) */
        cur_block = blocks;
@@ -460,6 +523,16 @@ static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int
        /* Walk thorough all paths */
        zend_access_path(start, ctx);
 
+       /* Add brk/cont paths */
+       if (op_array->last_brk_cont) {
+               int i;
+               for (i=0; i< op_array->last_brk_cont; i++) {
+                       zend_access_path(cfg->loop_start[i], ctx);
+                       zend_access_path(cfg->loop_cont[i], ctx);
+                       zend_access_path(cfg->loop_brk[i], ctx);
+               }
+       }
+
        /* Add exception paths */
        if (op_array->last_try_catch) {
                int i;
@@ -1123,6 +1196,16 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
                op_array->last_try_catch = j;
        }
 
+       /* adjust loop jump targets */
+       if (op_array->last_brk_cont) {
+               int i;
+               for (i = 0; i< op_array->last_brk_cont; i++) {
+                       op_array->brk_cont_array[i].start = cfg->loop_start[i]->start_opline - new_opcodes;
+                       op_array->brk_cont_array[i].cont = cfg->loop_cont[i]->start_opline - new_opcodes;
+                       op_array->brk_cont_array[i].brk = cfg->loop_brk[i]->start_opline - new_opcodes;
+               }
+       }
+
     /* adjust jump targets */
        for (cur_block = blocks; cur_block; cur_block = cur_block->next) {
                if (!cur_block->access) {
index 9983ff4f6016487ba079061a880ca63dec68d96b..20510b416352da3a46479a1beac362348934a156 100644 (file)
@@ -44,6 +44,14 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
        end = op_array->opcodes + op_array->last;
        for (opline = op_array->opcodes; opline < end; opline++) {
 
+               /* GOTO target is unresolved yet. We can't optimize. */
+               if (opline->opcode == ZEND_GOTO &&
+                       Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
+                       /* TODO: in general we can avoid this restriction */
+                       FREE_ALLOCA(shiftlist);
+                       return;
+               }
+
                /* Kill JMP-over-NOP-s */
                if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) {
                        /* check if there are only NOPs under the branch */
@@ -77,6 +85,7 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
                for (opline = op_array->opcodes; opline<end; opline++) {
                        switch (opline->opcode) {
                                case ZEND_JMP:
+                               case ZEND_GOTO:
                                case ZEND_FAST_CALL:
                                case ZEND_DECLARE_ANON_CLASS:
                                case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -108,6 +117,13 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
                        }
                }
 
+               /* update brk/cont array */
+               for (j = 0; j < op_array->last_brk_cont; j++) {
+                       op_array->brk_cont_array[j].brk -= shiftlist[op_array->brk_cont_array[j].brk];
+                       op_array->brk_cont_array[j].cont -= shiftlist[op_array->brk_cont_array[j].cont];
+                       op_array->brk_cont_array[j].start -= shiftlist[op_array->brk_cont_array[j].start];
+               }
+
                /* update try/catch array */
                for (j = 0; j < op_array->last_try_catch; j++) {
                        op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
index 766eb2c2d4bc7f973d76a8ae2e89e3946627c6ec..6fcdc3e47a6aad21348bd06430d63d0df5467370 100644 (file)
@@ -624,6 +624,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
                case ZEND_EXIT:
                case ZEND_THROW:
                case ZEND_CATCH:
+               case ZEND_GOTO:
                case ZEND_FAST_CALL:
                case ZEND_FAST_RET:
                case ZEND_JMP:
index 86b0837137146b5544e45fa8e684bbe1e0c9ff68..dc69d2511ec2e4a0cd5667350721e7bec52cf06f 100644 (file)
@@ -340,11 +340,29 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
                                 */
                                case ZEND_FREE:
                                case ZEND_CASE: {
-                                       zend_op *m = opline;
-                                       zend_op *end = op_array->opcodes + op_array->last;
+                                       zend_op *m, *n;
+                                       int brk = op_array->last_brk_cont;
+                                       zend_bool in_switch = 0;
+                                       while (brk--) {
+                                               if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) &&
+                                                               op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) {
+                                                       in_switch = 1;
+                                                       break;
+                                               }
+                                       }
 
-                                       while (m < end) {
-                                               if (ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var) {
+                                       if (!in_switch) {
+                                               ZEND_ASSERT(opline->opcode == ZEND_FREE);
+                                               MAKE_NOP(opline);
+                                               zval_dtor(val);
+                                               return 1;
+                                       }
+
+                                       m = opline;
+                                       n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1;
+                                       while (m < n) {
+                                               if (ZEND_OP1_TYPE(m) == type &&
+                                                               ZEND_OP1(m).var == var) {
                                                        if (m->opcode == ZEND_CASE) {
                                                                zval old_val;
                                                                ZVAL_COPY_VALUE(&old_val, val);
@@ -353,7 +371,6 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
                                                                ZVAL_COPY_VALUE(val, &old_val);
                                                        } else if (m->opcode == ZEND_FREE) {
                                                                MAKE_NOP(m);
-                                                               break;
                                                        } else {
                                                                ZEND_ASSERT(0);
                                                        }
@@ -458,17 +475,6 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_11 & OPTIMIZATION_LEVEL) {
                zend_optimizer_compact_literals(op_array, ctx);
        }
-
-       if ((ZEND_OPTIMIZER_PASS_1
-               |ZEND_OPTIMIZER_PASS_2
-               |ZEND_OPTIMIZER_PASS_3
-               |ZEND_OPTIMIZER_PASS_4
-               |ZEND_OPTIMIZER_PASS_5
-               |ZEND_OPTIMIZER_PASS_9
-               |ZEND_OPTIMIZER_PASS_10
-               |ZEND_OPTIMIZER_PASS_11) & OPTIMIZATION_LEVEL) {
-               zend_regenerate_var_liveliness_info(op_array);
-       }
 }
 
 static void zend_accel_optimize(zend_op_array      *op_array,
@@ -488,6 +494,7 @@ static void zend_accel_optimize(zend_op_array      *op_array,
                }
                switch (opline->opcode) {
                        case ZEND_JMP:
+                       case ZEND_GOTO:
                        case ZEND_FAST_CALL:
                        case ZEND_DECLARE_ANON_CLASS:
                        case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -532,6 +539,7 @@ static void zend_accel_optimize(zend_op_array      *op_array,
                }
                switch (opline->opcode) {
                        case ZEND_JMP:
+                       case ZEND_GOTO:
                        case ZEND_FAST_CALL:
                        case ZEND_DECLARE_ANON_CLASS:
                        case ZEND_DECLARE_ANON_INHERITED_CLASS:
index 69288c72283bba46c0aa013ba247a6b561c9648a..c2f97ff715a332c6ff30981a1ae684614342da4d 100644 (file)
@@ -73,6 +73,9 @@ typedef struct _zend_cfg {
        zend_code_block    *blocks;
        zend_code_block   **try;
        zend_code_block   **catch;
+       zend_code_block   **loop_start;
+       zend_code_block   **loop_cont;
+       zend_code_block   **loop_brk;
        zend_op           **Tsource;
        char               *same_t;
 } zend_cfg;
index 54e2ab639ad1e5ba77a4d6fbbfbb4c7991544e02..0781b91c9d3d8d1cf9294d732e0c56bd65d1b9e8 100644 (file)
@@ -386,6 +386,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array            *op_arra
 # if ZEND_USE_ABS_JMP_ADDR
                        switch (opline->opcode) {
                                case ZEND_JMP:
+                               case ZEND_GOTO:
                                case ZEND_FAST_CALL:
                                case ZEND_DECLARE_ANON_CLASS:
                                case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -458,11 +459,11 @@ static void zend_file_cache_serialize_op_array(zend_op_array            *op_arra
 
                SERIALIZE_STR(op_array->function_name);
                SERIALIZE_STR(op_array->filename);
+               SERIALIZE_PTR(op_array->brk_cont_array);
                SERIALIZE_PTR(op_array->scope);
                SERIALIZE_STR(op_array->doc_comment);
                SERIALIZE_PTR(op_array->try_catch_array);
                SERIALIZE_PTR(op_array->prototype);
-               SERIALIZE_PTR(op_array->T_liveliness);
        }
 }
 
@@ -912,6 +913,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array           *op_arr
 # if ZEND_USE_ABS_JMP_ADDR
                        switch (opline->opcode) {
                                case ZEND_JMP:
+                               case ZEND_GOTO:
                                case ZEND_FAST_CALL:
                                case ZEND_DECLARE_ANON_CLASS:
                                case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -980,11 +982,11 @@ static void zend_file_cache_unserialize_op_array(zend_op_array           *op_arr
 
                UNSERIALIZE_STR(op_array->function_name);
                UNSERIALIZE_STR(op_array->filename);
+               UNSERIALIZE_PTR(op_array->brk_cont_array);
                UNSERIALIZE_PTR(op_array->scope);
                UNSERIALIZE_STR(op_array->doc_comment);
                UNSERIALIZE_PTR(op_array->try_catch_array);
                UNSERIALIZE_PTR(op_array->prototype);
-               UNSERIALIZE_PTR(op_array->T_liveliness);
        }
 }
 
index 0f789090cdc74272cc122deff3b077c3aa70c416..bbcb20713b0f18e1bf2eafcef23e724af2b900b5 100644 (file)
@@ -501,6 +501,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
                                /* fix jumps to point to new array */
                                switch (opline->opcode) {
                                        case ZEND_JMP:
+                                       case ZEND_GOTO:
                                        case ZEND_FAST_CALL:
                                        case ZEND_DECLARE_ANON_CLASS:
                                        case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -589,6 +590,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
                op_array->arg_info = arg_info;
        }
 
+       if (op_array->brk_cont_array) {
+               zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
+       }
+
        if (op_array->scope) {
                op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope);
        }
@@ -613,10 +618,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
                zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
        }
 
-       if (op_array->T_liveliness) {
-               zend_accel_store(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]);
-       }
-
        if (op_array->vars) {
                if (already_stored) {
                        persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars);
index 9265ba5ce9c237a91938c9f0776d9a825612d6fe..d78cc592591f10cf5b5491011a1ca8fe3aa34e1d 100644 (file)
@@ -229,6 +229,10 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
                }
        }
 
+       if (op_array->brk_cont_array) {
+               ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
+       }
+
        if (ZCG(accel_directives).save_comments && op_array->doc_comment) {
                ADD_STRING(op_array->doc_comment);
        }
@@ -237,10 +241,6 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
                ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
        }
 
-       if (op_array->T_liveliness) {
-               ADD_DUP_SIZE(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]);
-       }
-
        if (op_array->vars) {
                int i;
 
index 9b19d37de3c5b015b948652cba874040f38e34cc..3af52f17cdaccf94dad11f84db53c6bdab19bfbe 100644 (file)
@@ -24,7 +24,6 @@
 #include "phpdbg_opcode.h"
 #include "phpdbg_utils.h"
 #include "ext/standard/php_string.h"
-#include "zend_smart_str.h"
 
 ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
 
@@ -57,6 +56,7 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
        /* OP1 */
        switch (op->opcode) {
        case ZEND_JMP:
+       case ZEND_GOTO:
        case ZEND_FAST_CALL:
                asprintf(&decode[1], "J%ld", OP_JMP_ADDR(op, op->op1) - ops->opcodes);
                break;
@@ -114,44 +114,6 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
                break;
        }
 
-#if 0
-       if (ops->T_liveliness) {
-               uint32_t *var = ops->T_liveliness + (op - ops->opcodes);
-
-               if (*var != (uint32_t)-1) {
-                       smart_str str = {0};
-
-                       var = ops->T_liveliness + (*var);
-                       smart_str_appends(&str, "; [@");
-                       smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var);
-                       while (*(++var) != (uint32_t)-1) {
-                               smart_str_appends(&str, ", @");
-                               smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var);
-                       }
-                       smart_str_appendc(&str, ']');
-                       smart_str_0(&str);
-
-                       asprintf(&decode[0],
-                               "%-20s %-20s %-20s%-20s",
-                               decode[1] ? decode[1] : "",
-                               decode[2] ? decode[2] : "",
-                               decode[3] ? decode[3] : "",
-                               ZSTR_VAL(str.s));
-
-                       smart_str_free(&str);
-
-                       if (decode[1])
-                               free(decode[1]);
-                       if (decode[2])
-                               free(decode[2]);
-                       if (decode[3])
-                               free(decode[3]);
-
-                       return decode[0];
-               }
-       }
-#endif
-
        asprintf(&decode[0],
                "%-20s %-20s %-20s",
                decode[1] ? decode[1] : "",