source: trunk/lib/Decompiler.class.php @ 1488

Last change on this file since 1488 was 1488, checked in by moo, 6 months ago

Decompiler PHP_5_6: updated support for zend_ast

  • Property svn:eol-style set to native
File size: 79.3 KB
Line 
1<?php
2
3define('INDENT', "\t");
4ini_set('error_reporting', E_ALL);
5
6function color($str, $color = 33)
7{
8    return "\x1B[{$color}m$str\x1B[0m";
9}
10
11function printBacktrace() // {{{
12{
13    $backtrace = debug_backtrace();
14    foreach ($backtrace as $stack) {
15        $args = array();
16        foreach ($stack['args'] as $arg) {
17            if (is_scalar($arg)) {
18                $args[] = var_export($arg, true);
19            }
20            else if (is_array($arg)) {
21                $array = array();
22                foreach ($arg as $key => $value) {
23                    $array[] = var_export($key, true) . " => " . (is_scalar($value) ? var_export($value, true) : gettype($value));
24                    if (count($array) >= 5) {
25                        $array[] = '...';
26                        break;
27                    }
28                }
29                $args[] = 'array(' . implode(', ', $array) . ')';
30            }
31            else {
32                $args[] = gettype($arg);
33            }
34        }
35        printf("%d: %s::%s(%s)" . PHP_EOL
36                , $stack['line']
37                , isset($stack['class']) ? $stack['class'] : ''
38                , $stack['function']
39                , implode(', ', $args)
40                );
41    }
42}
43// }}}
44
45function str($code, $indent = '') // {{{
46{
47    if (is_array($code)) {
48        $array = array();
49        foreach ($code as $key => $value) {
50            $array[$key] = str($value, $indent);
51        }
52        return $array;
53    }
54    if (is_object($code)) {
55        $code = foldToCode($code, $indent);
56        return $code->toCode($indent);
57    }
58
59    return (string) $code;
60}
61// }}}
62function unsetArray(&$array, $name) // {{{
63{
64    unset($array[$name]);
65}
66// }}}
67
68function foldToCode($src, $indent = '') // {{{ wrap or rewrap anything to Decompiler_Code
69{
70    if (is_array($indent)) {
71        $indent = $indent['indent'];
72    }
73
74    if (!is_object($src)) {
75        return new Decompiler_Code($src);
76    }
77
78    if (!method_exists($src, 'toCode')) {
79        var_dump($src);
80        exit('no toCode');
81    }
82    if (get_class($src) != 'Decompiler_Code') {
83        // rewrap it
84        $src = new Decompiler_Code($src->toCode($indent));
85    }
86
87    return $src;
88}
89// }}}
90function decompileAst($ast, $EX) // {{{
91{
92    $kind = $ast['kind'];
93    $children = $ast['children'];
94    unset($ast['kind']);
95    unset($ast['children']);
96    switch ($kind) {
97    case ZEND_CONST:
98        return value($ast[0], $EX);
99
100    case XC_INIT_ARRAY:
101        $array = new Decompiler_Array();
102        for ($i = 0; $i < $children; $i += 2) {
103            if (isset($ast[$i + 1])) {
104                $key = decompileAst($ast[$i], $EX);
105                $value = decompileAst($ast[$i + 1], $EX);
106                $array->value[] = array($key, $value);
107            }
108            else {
109                $array->value[] = array(null, decompileAst($ast[$i], $EX));
110            }
111        }
112        return $array;
113
114    // ZEND_BOOL_AND: handled in binop
115    // ZEND_BOOL_OR:  handled in binop
116
117    case ZEND_SELECT:
118        return new Decompiler_TriOp(
119                decompileAst($ast[0], $EX)
120                , decompileAst($ast[1], $EX)
121                , decompileAst($ast[2], $EX)
122                );
123
124    case ZEND_UNARY_PLUS:
125        return new Decompiler_Code('+' . str(decompileAst($ast[0], $EX)));
126
127    case ZEND_UNARY_MINUS:
128        return new Decompiler_Code('-' . str(decompileAst($ast[0], $EX)));
129
130    default:
131        $decompiler = $GLOBALS['__xcache_decompiler'];
132        if (isset($decompiler->binops[$kind])) {
133            return new Decompiler_Binop($decompiler
134                    , decompileAst($ast[0], $EX)
135                    , $kind
136                    , decompileAst($ast[1], $EX)
137                    );
138        }
139
140        return "un-handled kind $kind in zend_ast";
141    }
142}
143// }}}
144function value($value, &$EX) // {{{
145{
146    if (ZEND_ENGINE_2_6 && (xcache_get_type($value) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT_AST) {
147        return decompileAst(xcache_dasm_ast($value), $EX);
148    }
149
150    $originalValue = xcache_get_special_value($value);
151    if (isset($originalValue)) {
152        if ((xcache_get_type($value) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT) {
153            // constant
154            return $GLOBALS['__xcache_decompiler']->stripNamespace($originalValue);
155        }
156
157        $value = $originalValue;
158    }
159
160    if (is_a($value, 'Decompiler_Object')) {
161        // use as is
162    }
163    else if (is_array($value)) {
164        $value = new Decompiler_ConstArray($value, $EX);
165    }
166    else {
167        if (isset($EX['value2constant'][$value])) {
168            $value = new Decompiler_Code($EX['value2constant'][$value]);
169        }
170        else {
171            $value = new Decompiler_Value($value);
172        }
173    }
174    return $value;
175}
176// }}}
177function unquoteName_($str, $asVariableName, $indent = '') // {{{
178{
179    $str = str($str, $indent);
180    if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
181        return str_replace('\\\\', '\\', substr($str, 1, -1));
182    }
183    else if ($asVariableName) {
184        return "{" . $str . "}";
185    }
186    else {
187        return $str;
188    }
189}
190// }}}
191function unquoteVariableName($str, $indent = '') // {{{
192{
193    return unquoteName_($str, true, $indent);
194}
195// }}}
196function unquoteName($str, $indent = '') // {{{
197{
198    return unquoteName_($str, false, $indent);
199}
200// }}}
201class Decompiler_Object // {{{
202{
203}
204// }}}
205class Decompiler_Value extends Decompiler_Object // {{{
206{
207    var $value;
208
209    function Decompiler_Value($value = null)
210    {
211        $this->value = $value;
212    }
213
214    function toCode($indent)
215    {
216        $code = var_export($this->value, true);
217        if (gettype($this->value) == 'string') {
218            switch ($this->value) {
219            case "\r":
220                return '"\\r"';
221            case "\n":
222                return '"\\n"';
223            case "\r\n":
224                return '"\\r\\n"';
225            }
226            $code = str_replace("\r\n", '\' . "\\r\\n" . \'', $code);
227            $code = str_replace("\r", '\' . "\\r" . \'', $code);
228            $code = str_replace("\n", '\' . "\\n" . \'', $code);
229        }
230        return $code;
231    }
232}
233// }}}
234class Decompiler_Code extends Decompiler_Object // {{{
235{
236    var $src;
237
238    function Decompiler_Code($src)
239    {
240        if (!assert('isset($src)')) {
241            printBacktrace();
242        }
243        $this->src = $src;
244    }
245
246    function toCode($indent)
247    {
248        return $this->src;
249    }
250}
251// }}}
252class Decompiler_Binop extends Decompiler_Code // {{{
253{
254    var $opc;
255    var $op1;
256    var $op2;
257    var $parent;
258
259    function Decompiler_Binop($parent, $op1, $opc, $op2)
260    {
261        $this->parent = &$parent;
262        $this->opc = $opc;
263        $this->op1 = $op1;
264        $this->op2 = $op2;
265    }
266
267    function toCode($indent)
268    {
269        $opstr = $this->parent->binops[$this->opc];
270
271        if (is_a($this->op1, 'Decompiler_TriOp') || is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) {
272            $op1 = "(" . str($this->op1, $indent) . ")";
273        }
274        else {
275            $op1 = $this->op1;
276        }
277
278        if (is_a($this->op2, 'Decompiler_TriOp') || is_a($this->op2, 'Decompiler_Binop') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') {
279            $op2 = "(" . str($this->op2, $indent) . ")";
280        }
281        else {
282            $op2 = $this->op2;
283        }
284
285        if (str($op1) == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
286            return $opstr . str($op2, $indent);
287        }
288
289        return str($op1, $indent) . ' ' . $opstr . ($this->opc == XC_ASSIGN_REF ? '' : ' ') . str($op2, $indent);
290    }
291}
292// }}}
293class Decompiler_TriOp extends Decompiler_Code // {{{
294{
295    var $condition;
296    var $trueValue;
297    var $falseValue;
298
299    function Decompiler_TriOp($condition, $trueValue, $falseValue)
300    {
301        $this->condition = $condition;
302        $this->trueValue = $trueValue;
303        $this->falseValue = $falseValue;
304    }
305
306    function toCode($indent)
307    {
308        $trueValue = $this->trueValue;
309        if (is_a($this->trueValue, 'Decompiler_TriOp')) {
310            $trueValue = "(" . str($trueValue, $indent) . ")";
311        }
312        $falseValue = $this->falseValue;
313        if (is_a($this->falseValue, 'Decompiler_TriOp')) {
314            $falseValue = "(" . str($falseValue, $indent) . ")";
315        }
316
317        return str($this->condition) . ' ? ' . str($trueValue) . ' : ' . str($falseValue);
318    }
319}
320// }}}
321class Decompiler_Fetch extends Decompiler_Code // {{{
322{
323    var $src;
324    var $fetchType;
325
326    function Decompiler_Fetch($src, $type, $globalSrc)
327    {
328        $this->src = $src;
329        $this->fetchType = $type;
330        $this->globalSrc = $globalSrc;
331    }
332
333    function toCode($indent)
334    {
335        switch ($this->fetchType) {
336        case ZEND_FETCH_LOCAL:
337            return '$' . $this->src;
338        case ZEND_FETCH_STATIC:
339            if (ZEND_ENGINE_2_3) {
340                // closure local variable?
341                return 'STR' . str($this->src);
342            }
343            else {
344                $EX = array();
345                return str(value($this->src, $EX));
346            }
347            die('static fetch cant to string');
348        case ZEND_FETCH_GLOBAL:
349        case ZEND_FETCH_GLOBAL_LOCK:
350            return $this->globalSrc;
351        default:
352            var_dump($this->fetchType);
353            assert(0);
354        }
355    }
356}
357// }}}
358class Decompiler_Box // {{{
359{
360    var $obj;
361
362    function Decompiler_Box(&$obj)
363    {
364        $this->obj = &$obj;
365    }
366
367    function toCode($indent)
368    {
369        return $this->obj->toCode($indent);
370    }
371}
372// }}}
373class Decompiler_Dim extends Decompiler_Value // {{{
374{
375    var $offsets = array();
376    var $isLast = false;
377    var $isObject = false;
378    var $assign = null;
379
380    function toCode($indent)
381    {
382        if (is_a($this->value, 'Decompiler_ListBox')) {
383            $exp = str($this->value->obj->src, $indent);
384        }
385        else {
386            $exp = str($this->value, $indent);
387        }
388        $last = count($this->offsets) - 1;
389        foreach ($this->offsets as $i => $dim) {
390            if ($this->isObject && $i == $last) {
391                $exp .= '->' . unquoteVariableName($dim, $indent);
392            }
393            else {
394                $exp .= '[' . str($dim, $indent) . ']';
395            }
396        }
397        return $exp;
398    }
399}
400// }}}
401class Decompiler_DimBox extends Decompiler_Box // {{{
402{
403}
404// }}}
405class Decompiler_List extends Decompiler_Code // {{{
406{
407    var $src;
408    var $dims = array();
409    var $everLocked = false;
410
411    function toCode($indent)
412    {
413        if (count($this->dims) == 1 && !$this->everLocked) {
414            $dim = $this->dims[0];
415            unset($dim->value);
416            $dim->value = $this->src;
417            if (!isset($dim->assign)) {
418                return str($dim, $indent);
419            }
420            return str($this->dims[0]->assign, $indent) . ' = ' . str($dim, $indent);
421        }
422        /* flatten dims */
423        $assigns = array();
424        foreach ($this->dims as $dim) {
425            $assign = &$assigns;
426            foreach ($dim->offsets as $offset) {
427                $assign = &$assign[$offset];
428            }
429            $assign = foldToCode($dim->assign, $indent);
430        }
431        return str($this->toList($assigns)) . ' = ' . str($this->src, $indent);
432    }
433
434    function toList($assigns)
435    {
436        $keys = array_keys($assigns);
437        if (count($keys) < 2) {
438            $keys[] = 0;
439        }
440        $max = call_user_func_array('max', $keys);
441        $list = 'list(';
442        for ($i = 0; $i <= $max; $i ++) {
443            if ($i) {
444                $list .= ', ';
445            }
446            if (!isset($assigns[$i])) {
447                continue;
448            }
449            if (is_array($assigns[$i])) {
450                $list .= $this->toList($assigns[$i]);
451            }
452            else {
453                $list .= $assigns[$i];
454            }
455        }
456        return $list . ')';
457    }
458}
459// }}}
460class Decompiler_ListBox extends Decompiler_Box // {{{
461{
462}
463// }}}
464class Decompiler_Array extends Decompiler_Value // {{{
465{
466    // emenets
467    function Decompiler_Array()
468    {
469        $this->value = array();
470    }
471
472    function toCode($indent)
473    {
474        $subindent = $indent . INDENT;
475
476        $elementsCode = array();
477        $index = 0;
478        foreach ($this->value as $element) {
479            list($key, $value) = $element;
480            if (!isset($key)) {
481                $key = $index++;
482            }
483            $elementsCode[] = array(str($key, $subindent), str($value, $subindent), $key, $value);
484        }
485
486        $exp = "array(";
487        $indent = $indent . INDENT;
488        $assocWidth = 0;
489        $multiline = 0;
490        $i = 0;
491        foreach ($elementsCode as $element) {
492            list($keyCode, $valueCode) = $element;
493            if ((string) $i !== $keyCode) {
494                $assocWidth = 1;
495                break;
496            }
497            ++$i;
498        }
499        foreach ($elementsCode as $element) {
500            list($keyCode, $valueCode, $key, $value) = $element;
501            if ($assocWidth) {
502                $len = strlen($keyCode);
503                if ($assocWidth < $len) {
504                    $assocWidth = $len;
505                }
506            }
507            if (is_array($value) || is_a($value, 'Decompiler_Array')) {
508                $multiline ++;
509            }
510        }
511
512        $i = 0;
513        foreach ($elementsCode as $element) {
514            list($keyCode, $value) = $element;
515            if ($multiline) {
516                if ($i) {
517                    $exp .= ",";
518                }
519                $exp .= "\n";
520                $exp .= $indent;
521            }
522            else {
523                if ($i) {
524                    $exp .= ", ";
525                }
526            }
527
528            if ($assocWidth) {
529                if ($multiline) {
530                    $exp .= sprintf("%-{$assocWidth}s => ", $keyCode);
531                }
532                else {
533                    $exp .= $keyCode . ' => ';
534                }
535            }
536
537            $exp .= $value;
538
539            $i ++;
540        }
541        if ($multiline) {
542            $exp .= "\n$indent)";
543        }
544        else {
545            $exp .= ")";
546        }
547        return $exp;
548    }
549}
550// }}}
551class Decompiler_ConstArray extends Decompiler_Array // {{{
552{
553    function Decompiler_ConstArray($array, &$EX)
554    {
555        $elements = array();
556        foreach ($array as $key => $value) {
557            if ((xcache_get_type($value) & IS_CONSTANT_INDEX)) {
558                $keyCode = $GLOBALS['__xcache_decompiler']->stripNamespace(
559                        ZEND_ENGINE_2_3
560                        ? substr($key, 0, -2)
561                        : $key
562                        );
563            }
564            else {
565                $keyCode = value($key, $EX);
566            }
567            $elements[] = array($keyCode, value($value, $EX));
568        }
569        $this->value = $elements;
570    }
571}
572// }}}
573class Decompiler_ForeachBox extends Decompiler_Box // {{{
574{
575    var $iskey;
576
577    function toCode($indent)
578    {
579        return '#foreachBox#';
580    }
581}
582// }}}
583
584class Decompiler
585{
586    var $namespace;
587    var $namespaceDecided;
588    var $activeFile;
589    var $activeClass;
590    var $activeMethod;
591    var $activeFunction;
592
593    function Decompiler()
594    {
595        $GLOBALS['__xcache_decompiler'] = $this;
596        // {{{ testing
597        // XC_UNDEF XC_OP_DATA
598        $this->test = !empty($_ENV['XCACHE_DECOMPILER_TEST']);
599        $this->usedOps = array();
600
601        if ($this->test) {
602            $content = file_get_contents(__FILE__);
603            for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
604                if (!preg_match("/\\bXC_" . $opname . "\\b(?!')/", $content)) {
605                    echo "not recognized opcode ", $opname, "\n";
606                }
607            }
608        }
609        // }}}
610        // {{{ opinfo
611        $this->unaryops = array(
612                XC_BW_NOT   => '~',
613                XC_BOOL_NOT => '!',
614                );
615        $this->binops = array(
616                XC_ADD                 => "+",
617                XC_ASSIGN_ADD          => "+=",
618                XC_SUB                 => "-",
619                XC_ASSIGN_SUB          => "-=",
620                XC_MUL                 => "*",
621                XC_ASSIGN_MUL          => "*=",
622                XC_DIV                 => "/",
623                XC_ASSIGN_DIV          => "/=",
624                XC_MOD                 => "%",
625                XC_ASSIGN_MOD          => "%=",
626                XC_SL                  => "<<",
627                XC_ASSIGN_SL           => "<<=",
628                XC_SR                  => ">>",
629                XC_ASSIGN_SR           => ">>=",
630                XC_CONCAT              => ".",
631                XC_ASSIGN_CONCAT       => ".=",
632                XC_IS_IDENTICAL        => "===",
633                XC_IS_NOT_IDENTICAL    => "!==",
634                XC_IS_EQUAL            => "==",
635                XC_IS_NOT_EQUAL        => "!=",
636                XC_IS_SMALLER          => "<",
637                XC_IS_SMALLER_OR_EQUAL => "<=",
638                XC_BW_OR               => "|",
639                XC_ASSIGN_BW_OR        => "|=",
640                XC_BW_AND              => "&",
641                XC_ASSIGN_BW_AND       => "&=",
642                XC_BW_XOR              => "^",
643                XC_ASSIGN_BW_XOR       => "^=",
644                XC_BOOL_XOR            => "xor",
645                XC_ASSIGN              => "=",
646                XC_ASSIGN_REF          => "= &",
647                XC_JMP_SET             => "?:",
648                XC_JMP_SET_VAR         => "?:",
649                XC_JMPZ_EX             => "&&",
650                XC_JMPNZ_EX            => "||",
651
652                // zend_ast
653                ZEND_BOOL_AND          => '&&',
654                ZEND_BOOL_OR           => '||',
655                );
656        // }}}
657        $this->includeTypes = array( // {{{
658                ZEND_EVAL         => 'eval',
659                ZEND_INCLUDE      => 'include',
660                ZEND_INCLUDE_ONCE => 'include_once',
661                ZEND_REQUIRE      => 'require',
662                ZEND_REQUIRE_ONCE => 'require_once',
663                );
664                // }}}
665    }
666    function detectNamespace($name) // {{{
667    {
668        if ($this->namespaceDecided) {
669            return;
670        }
671
672        if (strpos($name, '\\') !== false) {
673            $this->namespace = strtok($name, '\\');
674            echo 'namespace ', $this->namespace, ";\n\n";
675        }
676
677        $this->namespaceDecided = true;
678    }
679    // }}}
680    function stripNamespace($name) // {{{
681    {
682        if (!isset($name)) {
683            return $name;
684        }
685
686        $name = str($name);
687        $len = strlen($this->namespace) + 1;
688        if (substr($name, 0, $len) == $this->namespace . '\\') {
689            return substr($name, $len);
690        }
691        else {
692            return $name;
693        }
694    }
695    // }}}
696    function outputPhp(&$EX, $range) // {{{
697    {
698        $needBlankline = isset($EX['lastBlock']);
699        $indent = $EX['indent'];
700        $curticks = 0;
701        for ($i = $range[0]; $i <= $range[1]; $i ++) {
702            $op = $EX['opcodes'][$i];
703            if (isset($op['gofrom'])) {
704                if ($needBlankline) {
705                    $needBlankline = false;
706                    echo PHP_EOL;
707                }
708                echo 'label' . $i, ":\n";
709            }
710            if (isset($op['php'])) {
711                $toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0;
712                if ($curticks != $toticks) {
713                    $oldticks = $curticks;
714                    $curticks = $toticks;
715                    if (!$curticks) {
716                        echo $EX['indent'], "}\n\n";
717                        $indent = $EX['indent'];
718                    }
719                    else {
720                        if ($oldticks) {
721                            echo $EX['indent'], "}\n\n";
722                        }
723                        else if (!$oldticks) {
724                            $indent .= INDENT;
725                        }
726                        if ($needBlankline) {
727                            $needBlankline = false;
728                            echo PHP_EOL;
729                        }
730                        echo $EX['indent'], "declare (ticks=$curticks) {\n";
731                    }
732                }
733                if ($needBlankline) {
734                    $needBlankline = false;
735                    echo PHP_EOL;
736                }
737                echo $indent, str($op['php'], $indent), ";\n";
738                $EX['lastBlock'] = 'basic';
739            }
740        }
741        if ($curticks) {
742            echo $EX['indent'], "}\n";
743        }
744    }
745    // }}}
746    function getOpVal($op, &$EX, $free = false) // {{{
747    {
748        switch ($op['op_type']) {
749        case XC_IS_CONST:
750            return value($op['constant'], $EX);
751
752        case XC_IS_VAR:
753        case XC_IS_TMP_VAR:
754            $T = &$EX['Ts'];
755            if (!isset($T[$op['var']])) {
756                printBacktrace();
757            }
758            $ret = $T[$op['var']];
759            if ($free && empty($this->keepTs)) {
760                unset($T[$op['var']]);
761            }
762            return $ret;
763
764        case XC_IS_CV:
765            $var = $op['var'];
766            $var = $EX['op_array']['vars'][$var];
767            return '$' . $var['name'];
768
769        case XC_IS_UNUSED:
770            return null;
771        }
772    }
773    // }}}
774    function removeKeyPrefix($array, $prefix) // {{{
775    {
776        $prefixLen = strlen($prefix);
777        $ret = array();
778        foreach ($array as $key => $value) {
779            if (substr($key, 0, $prefixLen) == $prefix) {
780                $key = substr($key, $prefixLen);
781            }
782            $ret[$key] = $value;
783        }
784        return $ret;
785    }
786    // }}}
787    function fixOpCode(&$opcodes, $removeTailing = false, $defaultReturnValue = null) // {{{
788    {
789        $last = count($opcodes) - 1;
790        for ($i = 0; $i <= $last; $i ++) {
791            if (function_exists('xcache_get_fixed_opcode')) {
792                $opcodes[$i]['opcode'] = xcache_get_fixed_opcode($opcodes[$i]['opcode'], $i);
793            }
794            if (isset($opcodes[$i]['op1'])) {
795                $opcodes[$i]['op1'] = $this->removeKeyPrefix($opcodes[$i]['op1'], 'u.');
796                $opcodes[$i]['op2'] = $this->removeKeyPrefix($opcodes[$i]['op2'], 'u.');
797                $opcodes[$i]['result'] = $this->removeKeyPrefix($opcodes[$i]['result'], 'u.');
798            }
799            else {
800                $op = array(
801                    'op1' => array(),
802                    'op2' => array(),
803                    'result' => array(),
804                );
805                foreach ($opcodes[$i] as $name => $value) {
806                    if (preg_match('!^(op1|op2|result)\\.(.*)!', $name, $m)) {
807                        list(, $which, $field) = $m;
808                        $op[$which][$field] = $value;
809                    }
810                    else if (preg_match('!^(op1|op2|result)_type$!', $name, $m)) {
811                        list(, $which) = $m;
812                        $op[$which]['op_type'] = $value;
813                    }
814                    else {
815                        $op[$name] = $value;
816                    }
817                }
818                $opcodes[$i] = $op;
819            }
820        }
821
822        if ($removeTailing) {
823            $last = count($opcodes) - 1;
824            if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
825                $this->usedOps[XC_HANDLE_EXCEPTION] = true;
826                $opcodes[$last]['opcode'] = XC_NOP;
827                --$last;
828            }
829            if ($opcodes[$last]['opcode'] == XC_RETURN
830             || $opcodes[$last]['opcode'] == XC_GENERATOR_RETURN) {
831                $op1 = $opcodes[$last]['op1'];
832                if ($op1['op_type'] == XC_IS_CONST && array_key_exists('constant', $op1) && $op1['constant'] === $defaultReturnValue) {
833                    $opcodes[$last]['opcode'] = XC_NOP;
834                    --$last;
835                }
836            }
837        }
838    }
839    // }}}
840    function decompileBasicBlock(&$EX, $range, $unhandled = false) // {{{
841    {
842        $this->dasmBasicBlock($EX, $range);
843        if ($unhandled) {
844            $this->dumpRange($EX, $range);
845        }
846        $this->outputPhp($EX, $range);
847    }
848    // }}}
849    function isIfCondition(&$EX, $range) // {{{
850    {
851        $opcodes = &$EX['opcodes'];
852        $firstOp = &$opcodes[$range[0]];
853        return $firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts']) && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_JMP
854         && !empty($opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'])
855         && $opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'][0] == $range[1] + 1;
856    }
857    // }}}
858    function removeJmpInfo(&$EX, $line) // {{{
859    {
860        $opcodes = &$EX['opcodes'];
861        if (!isset($opcodes[$line]['jmpouts'])) {
862            printBacktrace();
863        }
864        foreach ($opcodes[$line]['jmpouts'] as $jmpTo) {
865            $jmpins = &$opcodes[$jmpTo]['jmpins'];
866            $jmpins = array_flip($jmpins);
867            unset($jmpins[$line]);
868            $jmpins = array_keys($jmpins);
869        }
870        // $opcodes[$line]['opcode'] = XC_NOP;
871        unset($opcodes[$line]['jmpouts']);
872    }
873    // }}}
874    function beginScope(&$EX, $doIndent = true) // {{{
875    {
876        array_push($EX['scopeStack'], array($EX['lastBlock'], $EX['indent']));
877        if ($doIndent) {
878            $EX['indent'] .= INDENT;
879        }
880        $EX['lastBlock'] = null;
881    }
882    // }}}
883    function endScope(&$EX) // {{{
884    {
885        list($EX['lastBlock'], $EX['indent']) = array_pop($EX['scopeStack']);
886    }
887    // }}}
888    function beginComplexBlock(&$EX) // {{{
889    {
890        if (isset($EX['lastBlock'])) {
891            echo PHP_EOL;
892            $EX['lastBlock'] = null;
893        }
894    }
895    // }}}
896    function endComplexBlock(&$EX) // {{{
897    {
898        $EX['lastBlock'] = 'complex';
899    }
900    // }}}
901    function decompileComplexBlock(&$EX, $range) // {{{
902    {
903        $T = &$EX['Ts'];
904        $opcodes = &$EX['opcodes'];
905        $indent = $EX['indent'];
906
907        $firstOp = &$opcodes[$range[0]];
908        $lastOp = &$opcodes[$range[1]];
909
910        // {{{ && || and or
911        if (($firstOp['opcode'] == XC_JMPZ_EX || $firstOp['opcode'] == XC_JMPNZ_EX) && !empty($firstOp['jmpouts'])
912         && $firstOp['jmpouts'][0] == $range[1] + 1
913         && $lastOp['opcode'] == XC_BOOL
914         && $firstOp['opcode']['result']['var'] == $lastOp['opcode']['result']['var']
915        ) {
916            $this->removeJmpInfo($EX, $range[0]);
917
918            $this->recognizeAndDecompileClosedBlocks($EX, array($range[0], $range[0]));
919            $op1 = $this->getOpVal($firstOp['result'], $EX, true);
920
921            $this->recognizeAndDecompileClosedBlocks($EX, array($range[0] + 1, $range[1]));
922            $op2 = $this->getOpVal($lastOp['result'], $EX, true);
923
924            $T[$firstOp['result']['var']] = new Decompiler_Binop($this, $op1, $firstOp['opcode'], $op2);
925            return false;
926        }
927        // }}}
928        // {{{ ?: excluding JMP_SET/JMP_SET_VAR
929        if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts'])
930         && $range[1] >= $range[0] + 3
931         && ($opcodes[$firstOp['jmpouts'][0] - 2]['opcode'] == XC_QM_ASSIGN || $opcodes[$firstOp['jmpouts'][0] - 2]['opcode'] == XC_QM_ASSIGN_VAR)
932         && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_JMP && $opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'][0] == $range[1] + 1
933         && ($lastOp['opcode'] == XC_QM_ASSIGN || $lastOp['opcode'] == XC_QM_ASSIGN_VAR)
934        ) {
935            $trueRange = array($range[0] + 1, $firstOp['jmpouts'][0] - 2);
936            $falseRange = array($firstOp['jmpouts'][0], $range[1]);
937            $this->removeJmpInfo($EX, $range[0]);
938
939            $condition = $this->getOpVal($firstOp['op1'], $EX);
940            $this->recognizeAndDecompileClosedBlocks($EX, $trueRange);
941            $trueValue = $this->getOpVal($opcodes[$trueRange[1]]['result'], $EX, true);
942            $this->recognizeAndDecompileClosedBlocks($EX, $falseRange);
943            $falseValue = $this->getOpVal($opcodes[$falseRange[1]]['result'], $EX, true);
944            $T[$opcodes[$trueRange[1]]['result']['var']] = new Decompiler_TriOp($condition, $trueValue, $falseValue);
945            return false;
946        }
947        // }}}
948        // {{{ goto (TODO: recognize BRK which is translated to JMP by optimizer)
949        if ($firstOp['opcode'] == XC_JMP && !empty($firstOp['jmpouts']) && $firstOp['jmpouts'][0] == $range[1] + 1) {
950            $this->removeJmpInfo($EX, $range[0]);
951            assert(XC_GOTO != -1);
952            $firstOp['opcode'] = XC_GOTO;
953            $target = $firstOp['op1']['var'];
954            $firstOp['goto'] = $target;
955            $opcodes[$target]['gofrom'][] = $range[0];
956
957            $this->recognizeAndDecompileClosedBlocks($EX, $range);
958            return false;
959        }
960        // }}}
961        // {{{ for
962        if (!empty($firstOp['jmpins']) && $opcodes[$firstOp['jmpins'][0]]['opcode'] == XC_JMP
963         && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts']) && $lastOp['jmpouts'][0] <= $firstOp['jmpins'][0]
964         && !empty($opcodes[$range[1] + 1]['jmpins']) && $opcodes[$opcodes[$range[1] + 1]['jmpins'][0]]['opcode'] == XC_JMPZNZ
965        ) {
966            $nextRange = array($lastOp['jmpouts'][0], $firstOp['jmpins'][0]);
967            $conditionRange = array($range[0], $nextRange[0] - 1);
968            $this->removeJmpInfo($EX, $conditionRange[1]);
969            $bodyRange = array($nextRange[1], $range[1]);
970            $this->removeJmpInfo($EX, $bodyRange[1]);
971
972            $initial = '';
973            $this->beginScope($EX);
974            $this->dasmBasicBlock($EX, $conditionRange);
975            $conditionCodes = array();
976            for ($i = $conditionRange[0]; $i <= $conditionRange[1]; ++$i) {
977                if (isset($opcodes[$i]['php'])) {
978                    $conditionCodes[] = str($opcodes[$i]['php'], $EX);
979                }
980            }
981            $conditionCodes[] = str($this->getOpVal($opcodes[$conditionRange[1]]['op1'], $EX), $EX);
982            if (implode(',', $conditionCodes) == 'true') {
983                $conditionCodes = array();
984            }
985            $this->endScope($EX);
986
987            $this->beginScope($EX);
988            $this->dasmBasicBlock($EX, $nextRange);
989            $nextCodes = array();
990            for ($i = $nextRange[0]; $i <= $nextRange[1]; ++$i) {
991                if (isset($opcodes[$i]['php'])) {
992                    $nextCodes[] = str($opcodes[$i]['php'], $EX);
993                }
994            }
995            $this->endScope($EX);
996
997            $this->beginComplexBlock($EX);
998            echo $indent, 'for (', str($initial, $EX), '; ', implode(', ', $conditionCodes), '; ', implode(', ', $nextCodes), ') ', '{', PHP_EOL;
999            $this->beginScope($EX);
1000            $this->recognizeAndDecompileClosedBlocks($EX, $bodyRange);
1001            $this->endScope($EX);
1002            echo $indent, '}', PHP_EOL;
1003            $this->endComplexBlock($EX);
1004            return;
1005        }
1006        // }}}
1007        // {{{ if/elseif/else
1008        if ($this->isIfCondition($EX, $range)) {
1009            $this->beginComplexBlock($EX);
1010            $isElseIf = false;
1011            do {
1012                $ifRange = array($range[0], $opcodes[$range[0]]['jmpouts'][0] - 1);
1013                $this->removeJmpInfo($EX, $ifRange[0]);
1014                $this->removeJmpInfo($EX, $ifRange[1]);
1015                $condition = $this->getOpVal($opcodes[$ifRange[0]]['op1'], $EX);
1016
1017                echo $indent, $isElseIf ? 'else if' : 'if', ' (', str($condition, $EX), ') ', '{', PHP_EOL;
1018                $this->beginScope($EX);
1019                $this->recognizeAndDecompileClosedBlocks($EX, $ifRange);
1020                $this->endScope($EX);
1021                $EX['lastBlock'] = null;
1022                echo $indent, '}', PHP_EOL;
1023
1024                $isElseIf = true;
1025                // search for else if
1026                $range[0] = $ifRange[1] + 1;
1027                for ($i = $ifRange[1] + 1; $i <= $range[1]; ++$i) {
1028                    // find first jmpout
1029                    if (!empty($opcodes[$i]['jmpouts'])) {
1030                        if ($this->isIfCondition($EX, array($i, $range[1]))) {
1031                            $this->dasmBasicBlock($EX, array($range[0], $i));
1032                            $range[0] = $i;
1033                        }
1034                        break;
1035                    }
1036                }
1037            } while ($this->isIfCondition($EX, $range));
1038            if ($ifRange[1] < $range[1]) {
1039                $elseRange = array($ifRange[1], $range[1]);
1040                echo $indent, 'else ', '{', PHP_EOL;
1041                $this->beginScope($EX);
1042                $this->recognizeAndDecompileClosedBlocks($EX, $elseRange);
1043                $this->endScope($EX);
1044                $EX['lastBlock'] = null;
1045                echo $indent, '}', PHP_EOL;
1046            }
1047            $this->endComplexBlock($EX);
1048            return;
1049        }
1050        if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts'])
1051         && $firstOp['jmpouts'][0] - 1 == $range[1]
1052         && ($opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_RETURN || $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_GENERATOR_RETURN)) {
1053            $this->beginComplexBlock($EX);
1054            $this->removeJmpInfo($EX, $range[0]);
1055            $condition = $this->getOpVal($opcodes[$range[0]]['op1'], $EX);
1056
1057            echo $indent, 'if (', str($condition, $EX), ') ', '{', PHP_EOL;
1058            $this->beginScope($EX);
1059            $this->recognizeAndDecompileClosedBlocks($EX, $range);
1060            $this->endScope($EX);
1061            echo $indent, '}', PHP_EOL;
1062            $this->endComplexBlock($EX);
1063            return;
1064        }
1065        // }}}
1066        // {{{ try/catch
1067        if (!empty($firstOp['jmpins']) && !empty($opcodes[$firstOp['jmpins'][0]]['isCatchBegin'])) {
1068            $catchBlocks = array();
1069            $catchFirst = $firstOp['jmpins'][0];
1070
1071            $tryRange = array($range[0], $catchFirst - 1);
1072
1073            // search for XC_CATCH
1074            for ($i = $catchFirst; $i <= $range[1]; ) {
1075                if ($opcodes[$i]['opcode'] == XC_CATCH) {
1076                    $catchOpLine = $i;
1077                    $this->removeJmpInfo($EX, $catchFirst);
1078
1079                    $catchNext = $opcodes[$catchOpLine]['extended_value'];
1080                    $catchBodyLast = $catchNext - 1;
1081                    if ($opcodes[$catchBodyLast]['opcode'] == XC_JMP) {
1082                        --$catchBodyLast;
1083                    }
1084
1085                    $catchBlocks[$catchFirst] = array($catchOpLine, $catchBodyLast);
1086
1087                    $i = $catchFirst = $catchNext;
1088                }
1089                else {
1090                    ++$i;
1091                }
1092            }
1093
1094            if ($opcodes[$tryRange[1]]['opcode'] == XC_JMP) {
1095                --$tryRange[1];
1096            }
1097
1098            $this->beginComplexBlock($EX);
1099            echo $indent, "try {", PHP_EOL;
1100            $this->beginScope($EX);
1101            $this->recognizeAndDecompileClosedBlocks($EX, $tryRange);
1102            $this->endScope($EX);
1103            echo $indent, '}', PHP_EOL;
1104            if (!$catchBlocks) {
1105                printBacktrace();
1106                assert($catchBlocks);
1107            }
1108            foreach ($catchBlocks as $catchFirst => $catchInfo) {
1109                list($catchOpLine, $catchBodyLast) = $catchInfo;
1110                $catchBodyFirst = $catchOpLine + 1;
1111                $this->dasmBasicBlock($EX, array($catchFirst, $catchOpLine));
1112                $catchOp = &$opcodes[$catchOpLine];
1113                echo $indent, 'catch ('
1114                        , $this->stripNamespace(isset($catchOp['op1']['constant']) ? $catchOp['op1']['constant'] : str($this->getOpVal($catchOp['op1'], $EX)))
1115                        , ' '
1116                        , isset($catchOp['op2']['constant']) ? '$' . $catchOp['op2']['constant'] : str($this->getOpVal($catchOp['op2'], $EX))
1117                        , ") {", PHP_EOL;
1118                unset($catchOp);
1119
1120                $EX['lastBlock'] = null;
1121                $this->beginScope($EX);
1122                $this->recognizeAndDecompileClosedBlocks($EX, array($catchBodyFirst, $catchBodyLast));
1123                $this->endScope($EX);
1124                echo $indent, '}', PHP_EOL;
1125            }
1126            $this->endComplexBlock($EX);
1127            return;
1128        }
1129        // }}}
1130        // {{{ switch/case
1131        if (
1132            ($firstOp['opcode'] == XC_CASE
1133            || $firstOp['opcode'] == XC_JMP && !empty($firstOp['jmpouts']) && $opcodes[$firstOp['jmpouts'][0]]['opcode'] == XC_CASE
1134            )
1135             && !empty($lastOp['jmpouts'])
1136        ) {
1137            $cases = array();
1138            $caseDefault = null;
1139            $caseOp = null;
1140            for ($i = $range[0]; $i <= $range[1]; ) {
1141                $op = $opcodes[$i];
1142                if ($op['opcode'] == XC_CASE) {
1143                    if (!isset($caseOp)) {
1144                        $caseOp = $op;
1145                    }
1146                    $jmpz = $opcodes[$i + 1];
1147                    assert('$jmpz["opcode"] == XC_JMPZ');
1148                    $caseNext = $jmpz['jmpouts'][0];
1149                    $cases[$i] = $caseNext - 1;
1150                    $i = $caseNext;
1151                }
1152                else if ($op['opcode'] == XC_JMP && $op['jmpouts'][0] >= $i) {
1153                    // default
1154                    $caseNext = $op['jmpouts'][0];
1155                    $caseDefault = $i;
1156                    $cases[$i] = $caseNext - 1;
1157                    $i = $caseNext;
1158                }
1159                else {
1160                    ++$i;
1161                }
1162            }
1163
1164            $this->beginComplexBlock($EX);
1165
1166            echo $indent, 'switch (', str($this->getOpVal($caseOp['op1'], $EX, true), $EX), ") {", PHP_EOL;
1167            $caseIsOut = false;
1168            foreach ($cases as $caseFirst => $caseLast) {
1169                if ($caseIsOut && empty($lastCaseFall)) {
1170                    echo PHP_EOL;
1171                }
1172
1173                $caseOp = $opcodes[$caseFirst];
1174
1175                echo $indent;
1176                if ($caseOp['opcode'] == XC_CASE) {
1177                    echo 'case ';
1178                    echo str($this->getOpVal($caseOp['op2'], $EX), $EX);
1179                    echo ':', PHP_EOL;
1180
1181                    $this->removeJmpInfo($EX, $caseFirst);
1182                    ++$caseFirst;
1183
1184                    assert('$opcodes[$caseFirst]["opcode"] == XC_JMPZ');
1185                    $this->removeJmpInfo($EX, $caseFirst);
1186                    ++$caseFirst;
1187                }
1188                else {
1189                    echo 'default';
1190                    echo ':', PHP_EOL;
1191
1192                    assert('$opcodes[$caseFirst]["opcode"] == XC_JMP');
1193                    $this->removeJmpInfo($EX, $caseFirst);
1194                    ++$caseFirst;
1195                }
1196
1197                assert('$opcodes[$caseLast]["opcode"] == XC_JMP');
1198                $this->removeJmpInfo($EX, $caseLast);
1199                --$caseLast;
1200                switch ($opcodes[$caseLast]['opcode']) {
1201                case XC_BRK:
1202                case XC_CONT:
1203                case XC_GOTO:
1204                    $lastCaseFall = false;
1205                    break;
1206
1207                default:
1208                    $lastCaseFall = true;
1209                }
1210
1211                $this->beginScope($EX);
1212                $this->recognizeAndDecompileClosedBlocks($EX, array($caseFirst, $caseLast));
1213                $this->endScope($EX);
1214                $caseIsOut = true;
1215            }
1216            echo $indent, '}', PHP_EOL;
1217
1218            $this->endComplexBlock($EX);
1219            return;
1220        }
1221        // }}}
1222        // {{{ do/while
1223        if ($lastOp['opcode'] == XC_JMPNZ && !empty($lastOp['jmpouts'])
1224         && $lastOp['jmpouts'][0] == $range[0]) {
1225            $this->removeJmpInfo($EX, $range[1]);
1226            $this->beginComplexBlock($EX);
1227
1228            echo $indent, "do {", PHP_EOL;
1229            $this->beginScope($EX);
1230            $this->recognizeAndDecompileClosedBlocks($EX, $range);
1231            $this->endScope($EX);
1232            echo $indent, "} while (", str($this->getOpVal($lastOp['op1'], $EX)), ');', PHP_EOL;
1233
1234            $this->endComplexBlock($EX);
1235            return;
1236        }
1237        // }}}
1238
1239        // {{{ search firstJmpOp
1240        $firstJmp = -1;
1241        $firstJmpOp = null;
1242        for ($i = $range[0]; $i <= $range[1]; ++$i) {
1243            if (!empty($opcodes[$i]['jmpouts'])) {
1244                $firstJmp = $i;
1245                $firstJmpOp = &$opcodes[$firstJmp];
1246                break;
1247            }
1248        }
1249        // }}}
1250        // {{{ search lastJmpOp
1251        $lastJmp = -1;
1252        $lastJmpOp = null;
1253        for ($i = $range[1]; $i > $firstJmp; --$i) {
1254            if (!empty($opcodes[$i]['jmpouts'])) {
1255                $lastJmp = $i;
1256                $lastJmpOp = &$opcodes[$lastJmp];
1257                break;
1258            }
1259        }
1260        // }}}
1261
1262        // {{{ while
1263        if (isset($firstJmpOp)
1264         && $firstJmpOp['opcode'] == XC_JMPZ
1265         && $firstJmpOp['jmpouts'][0] > $range[1]
1266         && $lastOp['opcode'] == XC_JMP
1267         && !empty($lastOp['jmpouts']) && $lastOp['jmpouts'][0] == $range[0]) {
1268            $this->removeJmpInfo($EX, $firstJmp);
1269            $this->removeJmpInfo($EX, $range[1]);
1270            $this->beginComplexBlock($EX);
1271
1272            ob_start();
1273            $this->beginScope($EX);
1274            $this->recognizeAndDecompileClosedBlocks($EX, $range);
1275            $this->endScope($EX);
1276            $body = ob_get_clean();
1277
1278            echo $indent, 'while (', str($this->getOpVal($firstJmpOp['op1'], $EX)), ") {", PHP_EOL;
1279            echo $body;
1280            echo $indent, '}', PHP_EOL;
1281
1282            $this->endComplexBlock($EX);
1283            return;
1284        }
1285        // }}}
1286        // {{{ foreach
1287        if (isset($firstJmpOp)
1288         && $firstJmpOp['opcode'] == XC_FE_FETCH
1289         && !empty($firstJmpOp['jmpouts']) && $firstJmpOp['jmpouts'][0] > $lastJmp
1290         && isset($lastJmpOp)
1291         && $lastJmpOp['opcode'] == XC_JMP
1292         && !empty($lastJmpOp['jmpouts']) && $lastJmpOp['jmpouts'][0] == $firstJmp) {
1293            $this->removeJmpInfo($EX, $firstJmp);
1294            $this->removeJmpInfo($EX, $lastJmp);
1295            $this->beginComplexBlock($EX);
1296
1297            ob_start();
1298            $this->beginScope($EX);
1299            $this->recognizeAndDecompileClosedBlocks($EX, $range);
1300            $this->endScope($EX);
1301            $body = ob_get_clean();
1302
1303            $as = str(foldToCode($firstJmpOp['fe_as'], $EX), $EX);
1304            if (isset($firstJmpOp['fe_key'])) {
1305                $as = str($firstJmpOp['fe_key'], $EX) . ' => ' . $as;
1306            }
1307
1308            echo $indent, 'foreach (', str($firstJmpOp['fe_src'], $EX), " as $as) {", PHP_EOL;
1309            echo $body;
1310            echo $indent, '}', PHP_EOL;
1311
1312            $this->endComplexBlock($EX);
1313            return;
1314        }
1315        // }}}
1316
1317        $this->decompileBasicBlock($EX, $range, true);
1318    }
1319    // }}}
1320    function recognizeAndDecompileClosedBlocks(&$EX, $range) // {{{ decompile in a tree way
1321    {
1322        $opcodes = &$EX['opcodes'];
1323
1324        $starti = $range[0];
1325        for ($i = $starti; $i <= $range[1]; ) {
1326            if (!empty($opcodes[$i]['jmpins']) || !empty($opcodes[$i]['jmpouts'])) {
1327                $blockFirst = $i;
1328                $blockLast = -1;
1329                $j = $blockFirst;
1330                do {
1331                    $op = $opcodes[$j];
1332                    if (!empty($op['jmpins'])) {
1333                        // care about jumping from blocks behind, not before
1334                        foreach ($op['jmpins'] as $oplineNumber) {
1335                            if ($oplineNumber <= $range[1] && $blockLast < $oplineNumber) {
1336                                $blockLast = $oplineNumber;
1337                            }
1338                        }
1339                    }
1340                    if (!empty($op['jmpouts'])) {
1341                        $blockLast = max($blockLast, max($op['jmpouts']) - 1);
1342                    }
1343                    ++$j;
1344                } while ($j <= $blockLast);
1345                if (!assert('$blockLast <= $range[1]')) {
1346                    var_dump($blockLast, $range[1]);
1347                    printBacktrace();
1348                }
1349
1350                if ($blockLast >= $blockFirst) {
1351                    if ($blockFirst > $starti) {
1352                        $this->decompileBasicBlock($EX, array($starti, $blockFirst - 1));
1353                    }
1354                    if ($this->decompileComplexBlock($EX, array($blockFirst, $blockLast)) === false) {
1355                        if ($EX['lastBlock'] == 'complex') {
1356                            echo PHP_EOL;
1357                        }
1358                        $EX['lastBlock'] = null;
1359                    }
1360                    $starti = $blockLast + 1;
1361                    $i = $starti;
1362                }
1363                else {
1364                    ++$i;
1365                }
1366            }
1367            else {
1368                ++$i;
1369            }
1370        }
1371        if ($starti <= $range[1]) {
1372            $this->decompileBasicBlock($EX, array($starti, $range[1]));
1373        }
1374    }
1375    // }}}
1376    function buildJmpInfo(&$op_array) // {{{ build jmpins/jmpouts to op_array
1377    {
1378        $opcodes = &$op_array['opcodes'];
1379        $last = count($opcodes) - 1;
1380        for ($i = 0; $i <= $last; $i ++) {
1381            $op = &$opcodes[$i];
1382            $op['line'] = $i;
1383            switch ($op['opcode']) {
1384            case XC_CONT:
1385            case XC_BRK:
1386                $op['jmpouts'] = array();
1387                break;
1388
1389            case XC_GOTO:
1390                $target = $op['op1']['var'];
1391                $op['goto'] = $target;
1392                $opcodes[$target]['gofrom'][] = $i;
1393                break;
1394
1395            case XC_JMP:
1396                $target = $op['op1']['var'];
1397                $op['jmpouts'] = array($target);
1398                $opcodes[$target]['jmpins'][] = $i;
1399                break;
1400
1401            case XC_JMPZNZ:
1402                $jmpz = $op['op2']['opline_num'];
1403                $jmpnz = $op['extended_value'];
1404                $op['jmpouts'] = array($jmpz, $jmpnz);
1405                $opcodes[$jmpz]['jmpins'][] = $i;
1406                $opcodes[$jmpnz]['jmpins'][] = $i;
1407                break;
1408
1409            case XC_JMPZ:
1410            case XC_JMPNZ:
1411            case XC_JMPZ_EX:
1412            case XC_JMPNZ_EX:
1413            // case XC_JMP_SET:
1414            // case XC_JMP_SET_VAR:
1415            // case XC_FE_RESET:
1416            case XC_FE_FETCH:
1417            // case XC_JMP_NO_CTOR:
1418                $target = $op['op2']['opline_num'];
1419                //if (!isset($target)) {
1420                //  $this->dumpop($op, $EX);
1421                //  var_dump($op); exit;
1422                //}
1423                $op['jmpouts'] = array($target);
1424                $opcodes[$target]['jmpins'][] = $i;
1425                break;
1426
1427            /*
1428            case XC_RETURN:
1429                $op['jmpouts'] = array();
1430                break;
1431            */
1432
1433            case XC_CASE:
1434                // just to link together
1435                $op['jmpouts'] = array($i + 2);
1436                $opcodes[$i + 2]['jmpins'][] = $i;
1437                break;
1438
1439            case XC_CATCH:
1440                $catchNext = $op['extended_value'];
1441                $catchBegin = $opcodes[$i - 1]['opcode'] == XC_FETCH_CLASS ? $i - 1 : $i;
1442                $opcodes[$catchBegin]['jmpouts'] = array($catchNext);
1443                $opcodes[$catchNext]['jmpins'][] = $catchBegin;
1444                break;
1445            }
1446            /*
1447            if (!empty($op['jmpouts']) || !empty($op['jmpins'])) {
1448                echo $i, "\t", xcache_get_opcode($op['opcode']), PHP_EOL;
1449            }
1450            // */
1451        }
1452        unset($op);
1453        if (isset($op_array['try_catch_array'])) {
1454            foreach ($op_array['try_catch_array'] as $try_catch_element) {
1455                $catch_op = $try_catch_element['catch_op'];
1456                $opcodes[$catch_op]['isCatchBegin'] = true;
1457            }
1458            foreach ($op_array['try_catch_array'] as $try_catch_element) {
1459                $catch_op = $try_catch_element['catch_op'];
1460                $try_op = $try_catch_element['try_op'];
1461                do {
1462                    $opcodes[$try_op]['jmpins'][] = $catch_op;
1463                    $opcodes[$catch_op]['jmpouts'][] = $try_op;
1464                    if ($opcodes[$catch_op]['opcode'] == XC_CATCH) {
1465                        $catch_op = $opcodes[$catch_op]['extended_value'];
1466                    }
1467                    else if ($opcodes[$catch_op + 1]['opcode'] == XC_CATCH) {
1468                        $catch_op = $opcodes[$catch_op + 1]['extended_value'];
1469                    }
1470                    else {
1471                        break;
1472                    }
1473                } while ($catch_op <= $last && empty($opcodes[$catch_op]['isCatchBegin']));
1474            }
1475        }
1476    }
1477    // }}}
1478    function &dop_array($op_array, $indent = '') // {{{
1479    {
1480        $this->fixOpCode($op_array['opcodes'], true, $indent == '' ? 1 : null);
1481        $this->buildJmpInfo($op_array);
1482
1483        $opcodes = &$op_array['opcodes'];
1484        $last = count($opcodes) - 1;
1485        // build semi-basic blocks
1486        $nextbbs = array();
1487        $starti = 0;
1488        for ($i = 1; $i <= $last; $i ++) {
1489            if (isset($opcodes[$i]['jmpins'])
1490             || isset($opcodes[$i - 1]['jmpouts'])) {
1491                $nextbbs[$starti] = $i;
1492                $starti = $i;
1493            }
1494        }
1495        $nextbbs[$starti] = $last + 1;
1496
1497        $EX = array();
1498        $EX['Ts'] = array();
1499        $EX['indent'] = $indent;
1500        $EX['nextbbs'] = $nextbbs;
1501        $EX['op_array'] = &$op_array;
1502        $EX['opcodes'] = &$opcodes;
1503        $EX['range'] = array(0, count($opcodes) - 1);
1504        // func call
1505        $EX['object'] = null;
1506        $EX['called_scope'] = null;
1507        $EX['fbc'] = null;
1508        $EX['argstack'] = array();
1509        $EX['arg_types_stack'] = array();
1510        $EX['scopeStack'] = array();
1511        $EX['silence'] = 0;
1512        $EX['recvs'] = array();
1513        $EX['uses'] = array();
1514        $EX['lastBlock'] = null;
1515        $EX['value2constant'] = array();
1516        if (isset($this->activeFile)) {
1517            $EX['value2constant'][$this->activeFile] = '__FILE__';
1518        }
1519        if (isset($this->activeClass)) {
1520            $EX['value2constant'][$this->activeClass] = '__CLASS__';
1521        }
1522        if (isset($this->activeMethod)) {
1523            $EX['value2constant'][$this->activeMethod] = '__METHOD__';
1524        }
1525        if (isset($this->activeFunction)) {
1526            $EX['value2constant'][$this->activeFunction] = '__FUNCTION__';
1527        }
1528
1529        /* dump whole array
1530        $this->keepTs = true;
1531        $this->dasmBasicBlock($EX, $range);
1532        for ($i = $range[0]; $i <= $range[1]; ++$i) {
1533            echo $i, "\t", $this->dumpop($opcodes[$i], $EX);
1534        }
1535        // */
1536        // decompile in a tree way
1537        $this->recognizeAndDecompileClosedBlocks($EX, $EX['range'], $EX['indent']);
1538        return $EX;
1539    }
1540    // }}}
1541    function dasmBasicBlock(&$EX, $range) // {{{
1542    {
1543        $T = &$EX['Ts'];
1544        $opcodes = &$EX['opcodes'];
1545        $lastphpop = null;
1546        $currentSourceLine = null;
1547
1548        for ($i = $range[0]; $i <= $range[1]; $i ++, unsetArray($EX['value2constant'], $currentSourceLine)) {
1549            // {{{ prepair
1550            $op = &$opcodes[$i];
1551            $opc = $op['opcode'];
1552            if ($opc == XC_NOP) {
1553                $this->usedOps[$opc] = true;
1554                continue;
1555            }
1556
1557            $op1 = $op['op1'];
1558            $op2 = $op['op2'];
1559            $res = $op['result'];
1560            $ext = $op['extended_value'];
1561            $currentSourceLine = $op['lineno'];
1562            $EX['value2constant'][$currentSourceLine] = '__LINE__';
1563
1564            $opname = xcache_get_opcode($opc);
1565
1566            if ($opname == 'UNDEF' || !isset($opname)) {
1567                echo 'UNDEF OP:';
1568                $this->dumpop($op, $EX);
1569                continue;
1570            }
1571            // echo $i, ' '; $this->dumpop($op, $EX); //var_dump($op);
1572
1573            $resvar = null;
1574            unset($curResVar);
1575            if (array_key_exists($res['var'], $T)) {
1576                $curResVar = &$T[$res['var']];
1577            }
1578            if ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) {
1579                $istmpres = false;
1580            }
1581            else {
1582                $istmpres = true;
1583            }
1584            // }}}
1585            // echo $opname, "\n";
1586
1587            $notHandled = false;
1588            switch ($opc) {
1589            case XC_NEW: // {{{
1590                array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
1591                $EX['object'] = $istmpres ? (int) $res['var'] : null;
1592                $EX['called_scope'] = null;
1593                $EX['fbc'] = 'new ' . $this->stripNamespace(isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1, $EX));
1594                break;
1595                // }}}
1596            case XC_THROW: // {{{
1597                $resvar = 'throw ' . str($this->getOpVal($op1, $EX));
1598                break;
1599                // }}}
1600            case XC_CLONE: // {{{
1601                $resvar = 'clone ' . str($this->getOpVal($op1, $EX));
1602                break;
1603                // }}}
1604            case XC_CATCH: // {{{
1605                break;
1606                // }}}
1607            case XC_INSTANCEOF: // {{{
1608                $resvar = str($this->getOpVal($op1, $EX)) . ' instanceof ' . $this->stripNamespace($this->getOpVal($op2, $EX));
1609                break;
1610                // }}}
1611            case XC_FETCH_CLASS: // {{{
1612                if ($op2['op_type'] == XC_IS_UNUSED) {
1613                    switch (($ext & (defined('ZEND_FETCH_CLASS_MASK') ? ZEND_FETCH_CLASS_MASK : 0xFF))) {
1614                    case ZEND_FETCH_CLASS_SELF:
1615                        $class = 'self';
1616                        break;
1617                    case ZEND_FETCH_CLASS_PARENT:
1618                        $class = 'parent';
1619                        break;
1620                    case ZEND_FETCH_CLASS_STATIC:
1621                        $class = 'static';
1622                        break;
1623                    }
1624                    $istmpres = true;
1625                }
1626                else {
1627                    $class = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2, $EX);
1628                }
1629                $resvar = $class;
1630                break;
1631                // }}}
1632            case XC_FETCH_CONSTANT: // {{{
1633                if ($op1['op_type'] == XC_IS_UNUSED) {
1634                    $resvar = $this->stripNamespace($op2['constant']);
1635                    break;
1636                }
1637
1638                if ($op1['op_type'] == XC_IS_CONST) {
1639                    if (!ZEND_ENGINE_2) {
1640                        $resvar = $op1['constant'];
1641                        break;
1642                    }
1643                    $resvar = $this->stripNamespace($op1['constant']);
1644                }
1645                else {
1646                    $resvar = $this->getOpVal($op1, $EX);
1647                }
1648
1649                $resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2, $EX));
1650                break;
1651                // }}}
1652                // {{{ case FETCH_*
1653            case XC_FETCH_R:
1654            case XC_FETCH_W:
1655            case XC_FETCH_RW:
1656            case XC_FETCH_FUNC_ARG:
1657            case XC_FETCH_UNSET:
1658            case XC_FETCH_IS:
1659                $fetchType = defined('ZEND_FETCH_TYPE_MASK') ? ($ext & ZEND_FETCH_TYPE_MASK) : $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type'];
1660                $name = isset($op1['constant']) ? $op1['constant'] : unquoteName($this->getOpVal($op1, $EX), $EX);
1661                if ($fetchType == ZEND_FETCH_STATIC_MEMBER) {
1662                    $class = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2, $EX);
1663                    $rvalue = $this->stripNamespace($class) . '::$' . $name;
1664                }
1665                else {
1666                    $rvalue = isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1, $EX);
1667                    $globalName = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($this->getOpVal($op1, $EX), $EX) . "]";
1668                    $rvalue = new Decompiler_Fetch($rvalue, $fetchType, $globalName);
1669                }
1670
1671                if ($res['op_type'] != XC_IS_UNUSED) {
1672                    $resvar = $rvalue;
1673                }
1674                break;
1675                // }}}
1676            case XC_UNSET_VAR: // {{{
1677                $fetchType = defined('ZEND_FETCH_TYPE_MASK') ? ($ext & ZEND_FETCH_TYPE_MASK) : $op2['EA.type'];
1678                if ($fetchType == ZEND_FETCH_STATIC_MEMBER) {
1679                    $class = isset($op2['constant']) ? $op2['constant'] /* PHP5.3- */ : $this->getOpVal($op2, $EX);
1680                    $rvalue = $this->stripNamespace($class) . '::$' . $op1['constant'];
1681                }
1682                else {
1683                    $rvalue = isset($op1['constant']) ? '$' . $op1['constant'] /* PHP5.1- */ : $this->getOpVal($op1, $EX);
1684                }
1685
1686                $op['php'] = "unset(" . str($rvalue, $EX) . ")";
1687                $lastphpop = &$op;
1688                break;
1689                // }}}
1690                // {{{ case FETCH_DIM_*
1691            case XC_FETCH_DIM_TMP_VAR:
1692            case XC_FETCH_DIM_R:
1693            case XC_FETCH_DIM_W:
1694            case XC_FETCH_DIM_RW:
1695            case XC_FETCH_DIM_FUNC_ARG:
1696            case XC_FETCH_DIM_UNSET:
1697            case XC_FETCH_DIM_IS:
1698            case XC_ASSIGN_DIM:
1699            case XC_UNSET_DIM:
1700            case XC_UNSET_DIM_OBJ:
1701            case XC_UNSET_OBJ:
1702                $src = $this->getOpVal($op1, $EX);
1703                if (is_a($src, "Decompiler_ForeachBox")) {
1704                    assert($opc == XC_FETCH_DIM_TMP_VAR);
1705                    if (ZEND_ENGINE_2) {
1706                        $src = clone($src);
1707                    }
1708                    else {
1709                        $src = new Decompiler_ForeachBox($src->obj);
1710                    }
1711                    $src->iskey = $op2['constant'];
1712                    $resvar = $src;
1713                    break;
1714                }
1715
1716                if (is_a($src, "Decompiler_DimBox")) {
1717                    $dimbox = $src;
1718                }
1719                else {
1720                    if (!is_a($src, "Decompiler_ListBox")) {
1721                        $op1val = $this->getOpVal($op1, $EX);
1722                        $list = new Decompiler_List(isset($op1val) ? $op1val : '$this');
1723
1724                        $src = new Decompiler_ListBox($list);
1725                        if (!isset($op1['var'])) {
1726                            $this->dumpop($op, $EX);
1727                            var_dump($op);
1728                            die('missing var');
1729                        }
1730                        $T[$op1['var']] = $src;
1731                        unset($list);
1732                    }
1733                    $dim = new Decompiler_Dim($src);
1734                    $src->obj->dims[] = &$dim;
1735
1736                    $dimbox = new Decompiler_DimBox($dim);
1737                }
1738                $dim = &$dimbox->obj;
1739                $dim->offsets[] = $this->getOpVal($op2, $EX);
1740                /* TODO: use type mask */
1741                if ($ext == ZEND_FETCH_ADD_LOCK) {
1742                    $src->obj->everLocked = true;
1743                }
1744                else if ($ext == ZEND_FETCH_STANDARD) {
1745                    $dim->isLast = true;
1746                }
1747                if ($opc == XC_UNSET_OBJ) {
1748                    $dim->isObject = true;
1749                }
1750                else if ($opc == XC_UNSET_DIM_OBJ) {
1751                    $dim->isObject = ZEND_ENGINE_2 ? $ext == ZEND_UNSET_OBJ : false /* cannot distingue */;
1752                }
1753                unset($dim);
1754                $rvalue = $dimbox;
1755                unset($dimbox);
1756
1757                if ($opc == XC_ASSIGN_DIM) {
1758                    $lvalue = $rvalue;
1759                    ++ $i;
1760                    $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
1761                    $resvar = str($lvalue, $EX) . ' = ' . str($rvalue);
1762                }
1763                else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ || $opc == XC_UNSET_DIM_OBJ) {
1764                    $op['php'] = "unset(" . str($rvalue, $EX) . ")";
1765                    $lastphpop = &$op;
1766                }
1767                else if ($res['op_type'] != XC_IS_UNUSED) {
1768                    $resvar = $rvalue;
1769                }
1770                break;
1771                // }}}
1772            case XC_ASSIGN: // {{{
1773                $lvalue = $this->getOpVal($op1, $EX);
1774                $rvalue = $this->getOpVal($op2, $EX);
1775                if (is_a($rvalue, 'Decompiler_ForeachBox')) {
1776                    $type = $rvalue->iskey ? 'fe_key' : 'fe_as';
1777                    $rvalue->obj[$type] = $lvalue;
1778                    unset($T[$op2['var']]);
1779                    break;
1780                }
1781                if (is_a($rvalue, "Decompiler_DimBox")) {
1782                    $dim = &$rvalue->obj;
1783                    $dim->assign = $lvalue;
1784                    if ($dim->isLast) {
1785                        $resvar = foldToCode($dim->value, $EX);
1786                    }
1787                    unset($dim);
1788                    break;
1789                }
1790                if (is_a($rvalue, 'Decompiler_Fetch')) {
1791                    $src = str($rvalue->src, $EX);
1792                    if ('$' . unquoteName($src) == $lvalue) {
1793                        switch ($rvalue->fetchType) {
1794                        case ZEND_FETCH_STATIC:
1795                            $statics = &$EX['op_array']['static_variables'];
1796                            if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) {
1797                                $EX['uses'][] = str($lvalue);
1798                                unset($statics);
1799                                break 2;
1800                            }
1801                            unset($statics);
1802                        }
1803                    }
1804                }
1805                $resvar = new Decompiler_Binop($this, $lvalue, XC_ASSIGN, $rvalue);
1806                break;
1807                // }}}
1808            case XC_ASSIGN_REF: // {{{
1809                $lvalue = $this->getOpVal($op1, $EX);
1810                $rvalue = $this->getOpVal($op2, $EX);
1811                if (is_a($rvalue, 'Decompiler_Fetch')) {
1812                    $src = str($rvalue->src, $EX);
1813                    if ('$' . unquoteName($src) == $lvalue) {
1814                        switch ($rvalue->fetchType) {
1815                        case ZEND_FETCH_GLOBAL:
1816                        case ZEND_FETCH_GLOBAL_LOCK:
1817                            $resvar = 'global ' . $lvalue;
1818                            break 2;
1819                        case ZEND_FETCH_STATIC:
1820                            $statics = &$EX['op_array']['static_variables'];
1821                            if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) {
1822                                $EX['uses'][] = '&' . str($lvalue);
1823                                unset($statics);
1824                                break 2;
1825                            }
1826
1827                            $resvar = 'static ' . $lvalue;
1828                            $name = unquoteName($src);
1829                            if (isset($statics[$name])) {
1830                                $var = $statics[$name];
1831                                $resvar .= ' = ';
1832                                $resvar .= str(value($var, $EX), $EX);
1833                            }
1834                            unset($statics);
1835                            break 2;
1836                        default:
1837                        }
1838                    }
1839                }
1840                // TODO: PHP_6 global
1841                $resvar = new Decompiler_Binop($this, $lvalue, XC_ASSIGN_REF, $rvalue);
1842                break;
1843                // }}}
1844            // {{{ case FETCH_OBJ_*
1845            case XC_FETCH_OBJ_R:
1846            case XC_FETCH_OBJ_W:
1847            case XC_FETCH_OBJ_RW:
1848            case XC_FETCH_OBJ_FUNC_ARG:
1849            case XC_FETCH_OBJ_UNSET:
1850            case XC_FETCH_OBJ_IS:
1851            case XC_ASSIGN_OBJ:
1852                $obj = $this->getOpVal($op1, $EX);
1853                if (!isset($obj)) {
1854                    $obj = '$this';
1855                }
1856                $rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
1857                if ($res['op_type'] != XC_IS_UNUSED) {
1858                    $resvar = $rvalue;
1859                }
1860                if ($opc == XC_ASSIGN_OBJ) {
1861                    ++ $i;
1862                    $lvalue = $rvalue;
1863                    $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
1864                    $resvar = "$lvalue = " . str($rvalue);
1865                }
1866                break;
1867                // }}}
1868            case XC_ISSET_ISEMPTY_DIM_OBJ:
1869            case XC_ISSET_ISEMPTY_PROP_OBJ:
1870            case XC_ISSET_ISEMPTY:
1871            case XC_ISSET_ISEMPTY_VAR: // {{{
1872                if ($opc == XC_ISSET_ISEMPTY_VAR) {
1873                    $rvalue = $this->getOpVal($op1, $EX);
1874                    // for < PHP_5_3
1875                    if ($op1['op_type'] == XC_IS_CONST) {
1876                        $rvalue = '$' . unquoteVariableName($this->getOpVal($op1, $EX));
1877                    }
1878                    $fetchtype = defined('ZEND_FETCH_TYPE_MASK') ? ($ext & ZEND_FETCH_TYPE_MASK) : $op2['EA.type'];
1879                    if ($fetchtype == ZEND_FETCH_STATIC_MEMBER) {
1880                        $class = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2, $EX);
1881                        $rvalue = $this->stripNamespace($class) . '::' . unquoteName($rvalue, $EX);
1882                    }
1883                }
1884                else if ($opc == XC_ISSET_ISEMPTY) {
1885                    $rvalue = $this->getOpVal($op1, $EX);
1886                }
1887                else {
1888                    $container = $this->getOpVal($op1, $EX);
1889                    $dim = $this->getOpVal($op2, $EX);
1890                    if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) {
1891                        if (!isset($container)) {
1892                            $container = '$this';
1893                        }
1894                        $rvalue = str($container, $EX) . "->" . unquoteVariableName($dim);
1895                    }
1896                    else {
1897                        $rvalue = str($container, $EX) . '[' . str($dim) .']';
1898                    }
1899                }
1900
1901                switch (((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & ZEND_ISSET_ISEMPTY_MASK)) {
1902                case ZEND_ISSET:
1903                    $rvalue = "isset(" . str($rvalue) . ")";
1904                    break;
1905                case ZEND_ISEMPTY:
1906                    $rvalue = "empty(" . str($rvalue) . ")";
1907                    break;
1908                }
1909                $resvar = $rvalue;
1910                break;
1911                // }}}
1912            case XC_SEND_VAR_NO_REF:
1913            case XC_SEND_VAL:
1914            case XC_SEND_REF:
1915            case XC_SEND_VAR: // {{{
1916                $ref = ($opc == XC_SEND_REF ? '&' : '');
1917                $EX['argstack'][] = $ref . str($this->getOpVal($op1, $EX));
1918                break;
1919                // }}}
1920            case XC_INIT_STATIC_METHOD_CALL:
1921            case XC_INIT_METHOD_CALL: // {{{
1922                array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
1923                if ($opc == XC_INIT_STATIC_METHOD_CALL) {
1924                    $EX['object'] = null;
1925                    $EX['called_scope'] = $this->stripNamespace(isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1, $EX));
1926                }
1927                else {
1928                    $obj = $this->getOpVal($op1, $EX);
1929                    if (!isset($obj)) {
1930                        $obj = '$this';
1931                    }
1932                    $EX['object'] = $obj;
1933                    $EX['called_scope'] = null;
1934                }
1935                if ($res['op_type'] != XC_IS_UNUSED) {
1936                    $resvar = '$obj call$';
1937                }
1938
1939                $EX['fbc'] = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2, $EX);
1940                if (!isset($EX['fbc'])) {
1941                    $EX['fbc'] = '__construct';
1942                }
1943                break;
1944                // }}}
1945            case XC_INIT_NS_FCALL_BY_NAME:
1946            case XC_INIT_FCALL_BY_NAME: // {{{
1947                if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) {
1948                    break;
1949                }
1950                array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
1951                if (!ZEND_ENGINE_2 && ($ext & ZEND_MEMBER_FUNC_CALL)) {
1952                    if (isset($op1['constant'])) {
1953                        $EX['object'] = null;
1954                        $EX['called_scope'] = $this->stripNamespace($op1['constant']);
1955                    }
1956                    else {
1957                        $EX['object'] = $this->getOpVal($op1, $EX);
1958                        $EX['called_scope'] = null;
1959                    }
1960                }
1961                else {
1962                    $EX['object'] = null;
1963                    $EX['called_scope'] = null;
1964                }
1965                $EX['fbc'] = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2, $EX);
1966                break;
1967                // }}}
1968            case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4?
1969                $EX['object'] = null;
1970                $EX['called_scope'] = null;
1971                $which = $op1['var'];
1972                $EX['fbc'] = $EX['op_array']['funcs'][$which]['name'];
1973                break;
1974                // }}}
1975            case XC_DO_FCALL_BY_FUNC:
1976                $which = $op1['var'];
1977                $fname = $EX['op_array']['funcs'][$which]['name'];
1978                $args = $this->popargs($EX, $ext);
1979                $resvar = $fname . "($args)";
1980                break;
1981            case XC_DO_FCALL:
1982                $fname = unquoteName($this->getOpVal($op1, $EX), $EX);
1983                $args = $this->popargs($EX, $ext);
1984                $resvar = $fname . "($args)";
1985                break;
1986            case XC_DO_FCALL_BY_NAME: // {{{
1987                $object = null;
1988
1989                if (!is_int($EX['object'])) {
1990                    $object = $EX['object'];
1991                }
1992
1993                $args = $this->popargs($EX, $ext);
1994
1995                $prefix = (isset($object) ? str($object) . '->' : '' )
1996                    . (isset($EX['called_scope']) ? str($EX['called_scope']) . '::' : '' );
1997                $resvar = $prefix
1998                    . (!$prefix ? $this->stripNamespace($EX['fbc']) : str($EX['fbc']))
1999                    . "($args)";
2000                unset($args);
2001
2002                if (is_int($EX['object'])) {
2003                    $T[$EX['object']] = $resvar;
2004                    $resvar = null;
2005                }
2006                list($EX['fbc'], $EX['object'], $EX['called_scope']) = array_pop($EX['arg_types_stack']);
2007                break;
2008                // }}}
2009            case XC_VERIFY_ABSTRACT_CLASS: // {{{
2010                //unset($T[$op1['var']]);
2011                break;
2012                // }}}
2013            case XC_DECLARE_CLASS: 
2014            case XC_DECLARE_INHERITED_CLASS:
2015            case XC_DECLARE_INHERITED_CLASS_DELAYED: // {{{
2016                $key = $op1['constant'];
2017                if (!isset($this->dc['class_table'][$key])) {
2018                    echo 'class not found: ', $key, 'existing classes are:', "\n";
2019                    var_dump(array_keys($this->dc['class_table']));
2020                    exit;
2021                }
2022                $class = &$this->dc['class_table'][$key];
2023                if (!isset($class['name'])) {
2024                    $class['name'] = unquoteName($this->getOpVal($op2, $EX), $EX);
2025                }
2026                if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) {
2027                    $ext /= XC_SIZEOF_TEMP_VARIABLE;
2028                    $class['parent'] = $T[$ext];
2029                    unset($T[$ext]);
2030                }
2031                else {
2032                    $class['parent'] = null;
2033                }
2034
2035                for (;;) {
2036                    if ($i + 1 <= $range[1]
2037                     && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE
2038                     && $opcodes[$i + 1]['op1']['var'] == $res['var']) {
2039                        // continue
2040                    }
2041                    else if ($i + 2 <= $range[1]
2042                     && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
2043                     && $opcodes[$i + 2]['op1']['var'] == $res['var']
2044                     && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
2045                        // continue
2046                    }
2047                    else {
2048                        break;
2049                    }
2050                    $this->usedOps[XC_ADD_INTERFACE] = true;
2051
2052                    $fetchop = &$opcodes[$i + 1];
2053                    $interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2'], $EX), $EX));
2054                    $addop = &$opcodes[$i + 2];
2055                    $class['interfaces'][$addop['extended_value']] = $interface;
2056                    unset($fetchop, $addop);
2057                    $i += 2;
2058                }
2059                $this->activeClass = $class['name'];
2060                $this->dclass($class, $EX['indent']);
2061                $this->activeClass = null;
2062                echo "\n";
2063                unset($class);
2064                break;
2065                // }}}
2066            case XC_INIT_STRING: // {{{
2067                $resvar = "''";
2068                break;
2069                // }}}
2070            case XC_ADD_CHAR:
2071            case XC_ADD_STRING:
2072            case XC_ADD_VAR: // {{{
2073                $op1val = $this->getOpVal($op1, $EX);
2074                $op2val = $this->getOpVal($op2, $EX);
2075                switch ($opc) {
2076                case XC_ADD_CHAR:
2077                    $op2val = value(chr(str($op2val)), $EX);
2078                    break;
2079                case XC_ADD_STRING:
2080                    break;
2081                case XC_ADD_VAR:
2082                    break;
2083                }
2084                if (str($op1val) == "''") {
2085                    $rvalue = $op2val;
2086                }
2087                else if (str($op2val) == "''") {
2088                    $rvalue = $op1val;
2089                }
2090                else {
2091                    $rvalue = str($op1val) . ' . ' . str($op2val);
2092                }
2093                $resvar = $rvalue;
2094                // }}}
2095                break;
2096            case XC_PRINT: // {{{
2097                $op1val = $this->getOpVal($op1, $EX);
2098                $resvar = "print(" . str($op1val) . ")";
2099                break;
2100                // }}}
2101            case XC_ECHO: // {{{
2102                $op1val = $this->getOpVal($op1, $EX);
2103                $resvar = "echo " . str($op1val);
2104                break;
2105                // }}}
2106            case XC_EXIT: // {{{
2107                $op1val = $this->getOpVal($op1, $EX);
2108                $resvar = "exit($op1val)";
2109                break;
2110                // }}}
2111            case XC_INIT_ARRAY:
2112            case XC_ADD_ARRAY_ELEMENT: // {{{
2113                $rvalue = $this->getOpVal($op1, $EX, true);
2114
2115                if ($opc == XC_ADD_ARRAY_ELEMENT) {
2116                    $assoc = $this->getOpVal($op2, $EX);
2117                    if (isset($assoc)) {
2118                        $curResVar->value[] = array($assoc, $rvalue);
2119                    }
2120                    else {
2121                        $curResVar->value[] = array(null, $rvalue);
2122                    }
2123                }
2124                else {
2125                    if ($opc == XC_INIT_ARRAY) {
2126                        $resvar = new Decompiler_Array();
2127                        if (!isset($rvalue)) {
2128                            continue;
2129                        }
2130                    }
2131
2132                    $assoc = $this->getOpVal($op2, $EX);
2133                    if (isset($assoc)) {
2134                        $resvar->value[] = array($assoc, $rvalue);
2135                    }
2136                    else {
2137                        $resvar->value[] = array(null, $rvalue);
2138                    }
2139                }
2140                break;
2141                // }}}
2142            case XC_QM_ASSIGN:
2143            case XC_QM_ASSIGN_VAR: // {{{
2144                if (isset($curResVar) && is_a($curResVar, 'Decompiler_Binop')) {
2145                    $curResVar->op2 = $this->getOpVal($op1, $EX);
2146                }
2147                else {
2148                    $resvar = $this->getOpVal($op1, $EX);
2149                }
2150                break;
2151                // }}}
2152            case XC_BOOL: // {{{
2153                $resvar = /*'(bool) ' .*/ $this->getOpVal($op1, $EX);
2154                break;
2155                // }}}
2156            case XC_GENERATOR_RETURN:
2157            case XC_RETURN: // {{{
2158                $resvar = "return " . str($this->getOpVal($op1, $EX));
2159                break;
2160                // }}}
2161            case XC_INCLUDE_OR_EVAL: // {{{
2162                $type = ZEND_ENGINE_2_4 ? $ext : $op2['var']; // hack
2163                $keyword = $this->includeTypes[$type];
2164                $resvar = "$keyword " . str($this->getOpVal($op1, $EX));
2165                break;
2166                // }}}
2167            case XC_FE_RESET: // {{{
2168                $resvar = $this->getOpVal($op1, $EX);
2169                break;
2170                // }}}
2171            case XC_FE_FETCH: // {{{
2172                $op['fe_src'] = $this->getOpVal($op1, $EX, true);
2173                $fe = new Decompiler_ForeachBox($op);
2174                $fe->iskey = false;
2175
2176                if (ZEND_ENGINE_2_1) {
2177                    // save current first
2178                    $T[$res['var']] = $fe;
2179
2180                    // move to next opcode
2181                    ++ $i;
2182                    assert($opcodes[$i]['opcode'] == XC_OP_DATA);
2183                    $fe = new Decompiler_ForeachBox($op);
2184                    $fe->iskey = true;
2185
2186                    $res = $opcodes[$i]['result'];
2187                }
2188
2189                $resvar = $fe;
2190                break;
2191                // }}}
2192            case XC_YIELD: // {{{
2193                $resvar = 'yield ' . str($this->getOpVal($op1, $EX));
2194                break;
2195                // }}}
2196            case XC_SWITCH_FREE: // {{{
2197                if (isset($T[$op1['var']])) {
2198                    $this->beginComplexBlock($EX);
2199                    echo $EX['indent'], 'switch (', str($this->getOpVal($op1, $EX)), ") {", PHP_EOL;
2200                    echo $EX['indent'], '}', PHP_EOL;
2201                    $this->endComplexBlock($EX);
2202                }
2203                break;
2204                // }}}
2205            case XC_FREE: // {{{
2206                $free = $T[$op1['var']];
2207                if (!is_a($free, 'Decompiler_Array') && !is_a($free, 'Decompiler_Box')) {
2208                    $op['php'] = is_object($free) ? $free : $this->unquote($free, '(', ')');
2209                    $lastphpop = &$op;
2210                }
2211                unset($T[$op1['var']], $free);
2212                break;
2213                // }}}
2214            case XC_JMP_NO_CTOR:
2215                break;
2216            case XC_JMPZ_EX: // and
2217            case XC_JMPNZ_EX: // or
2218                $resvar = $this->getOpVal($op1, $EX);
2219                break;
2220
2221            case XC_JMPNZ: // while
2222            case XC_JMPZNZ: // for
2223            case XC_JMPZ: // {{{
2224                break;
2225                // }}}
2226            case XC_CONT:
2227            case XC_BRK:
2228                $resvar = $opc == XC_CONT ? 'continue' : 'break';
2229                $count = str($this->getOpVal($op2, $EX));
2230                if ($count != '1') {
2231                    $resvar .= ' ' . $count;
2232                }
2233                break;
2234            case XC_GOTO:
2235                $resvar = 'goto label' . $op['op1']['var'];
2236                $istmpres = false;
2237                break;
2238
2239            case XC_JMP: // {{{
2240                break;
2241                // }}}
2242            case XC_CASE:
2243                // $switchValue = $this->getOpVal($op1, $EX);
2244                $caseValue = $this->getOpVal($op2, $EX);
2245                $resvar = $caseValue;
2246                break;
2247            case XC_RECV_INIT:
2248            case XC_RECV:
2249                $offset = isset($op1['var']) ? $op1['var'] : $op1['constant'];
2250                $lvalue = $this->getOpVal($op['result'], $EX);
2251                if ($opc == XC_RECV_INIT) {
2252                    $default = value($op['op2']['constant'], $EX);
2253                }
2254                else {
2255                    $default = null;
2256                }
2257                $EX['recvs'][$offset] = array($lvalue, $default);
2258                break;
2259            case XC_POST_DEC:
2260            case XC_POST_INC:
2261            case XC_POST_DEC_OBJ:
2262            case XC_POST_INC_OBJ:
2263            case XC_PRE_DEC:
2264            case XC_PRE_INC:
2265            case XC_PRE_DEC_OBJ:
2266            case XC_PRE_INC_OBJ: // {{{
2267                $flags = array_flip(explode('_', $opname));
2268                if (isset($flags['OBJ'])) {
2269                    $resvar = str($this->getOpVal($op1, $EX)) . '->' . $op2['constant'];
2270                }
2271                else {
2272                    $resvar = str($this->getOpVal($op1, $EX));
2273                }
2274                $opstr = isset($flags['DEC']) ? '--' : '++';
2275                if (isset($flags['POST'])) {
2276                    $resvar .= $opstr;
2277                }
2278                else {
2279                    $resvar = "$opstr$resvar";
2280                }
2281                break;
2282                // }}}
2283
2284            case XC_BEGIN_SILENCE: // {{{
2285                $EX['silence'] ++;
2286                break;
2287                // }}}
2288            case XC_END_SILENCE: // {{{
2289                $EX['silence'] --;
2290                $lastresvar = '@' . str($lastresvar, $EX);
2291                break;
2292                // }}}
2293            case XC_CAST: // {{{
2294                $type = $ext;
2295                static $type2cast = array(
2296                        IS_LONG   => '(int)',
2297                        IS_DOUBLE => '(double)',
2298                        IS_STRING => '(string)',
2299                        IS_ARRAY  => '(array)',
2300                        IS_OBJECT => '(object)',
2301                        IS_BOOL   => '(bool)',
2302                        IS_NULL   => '(unset)',
2303                        );
2304                assert(isset($type2cast[$type]));
2305                $cast = $type2cast[$type];
2306                $resvar = $cast . ' ' . str($this->getOpVal($op1, $EX));
2307                break;
2308                // }}}
2309            case XC_EXT_STMT:
2310            case XC_EXT_FCALL_BEGIN:
2311            case XC_EXT_FCALL_END:
2312            case XC_EXT_NOP:
2313            case XC_INIT_CTOR_CALL:
2314                break;
2315            case XC_DECLARE_FUNCTION:
2316                $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
2317                break;
2318            case XC_DECLARE_LAMBDA_FUNCTION: // {{{
2319                ob_start();
2320                $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
2321                $resvar = ob_get_clean();
2322                $istmpres = true;
2323                break;
2324                // }}}
2325            case XC_DECLARE_CONST:
2326                $name = $this->stripNamespace(unquoteName($this->getOpVal($op1, $EX), $EX));
2327                $value = str($this->getOpVal($op2, $EX));
2328                $resvar = 'const ' . $name . ' = ' . $value;
2329                break;
2330            case XC_DECLARE_FUNCTION_OR_CLASS:
2331                /* always removed by compiler */
2332                break;
2333            case XC_TICKS:
2334                $lastphpop['ticks'] = ZEND_ENGINE_2_4 ? $ext : $this->getOpVal($op1, $EX);
2335                // $EX['tickschanged'] = true;
2336                break;
2337            case XC_RAISE_ABSTRACT_ERROR:
2338                // abstract function body is empty, don't need this code
2339                break;
2340            case XC_USER_OPCODE:
2341                echo '// ZEND_USER_OPCODE, impossible to decompile';
2342                break;
2343            case XC_OP_DATA:
2344                break;
2345            default: // {{{
2346                $call = array(&$this, $opname);
2347                if (is_callable($call)) {
2348                    $this->usedOps[$opc] = true;
2349                    $this->{$opname}($op, $EX);
2350                }
2351                else if (isset($this->binops[$opc])) { // {{{
2352                    $this->usedOps[$opc] = true;
2353                    $op1val = $this->getOpVal($op1, $EX);
2354                    $op2val = $this->getOpVal($op2, $EX);
2355                    $rvalue = new Decompiler_Binop($this, $op1val, $opc, $op2val);
2356                    $resvar = $rvalue;
2357                    // }}}
2358                }
2359                else if (isset($this->unaryops[$opc])) { // {{{
2360                    $this->usedOps[$opc] = true;
2361                    $op1val = $this->getOpVal($op1, $EX);
2362                    $myop = $this->unaryops[$opc];
2363                    $rvalue = $myop . str($op1val);
2364                    $resvar = $rvalue;
2365                    // }}}
2366                }
2367                else {
2368                    $notHandled = true;
2369                }
2370                // }}}
2371            }
2372            if ($notHandled) {
2373                echo "\x1B[31m * TODO ", $opname, "\x1B[0m\n";
2374            }
2375            else {
2376                $this->usedOps[$opc] = true;
2377            }
2378
2379            if (isset($resvar)) {
2380                if ($istmpres) {
2381                    $T[$res['var']] = $resvar;
2382                    $lastresvar = &$T[$res['var']];
2383                }
2384                else {
2385                    $op['php'] = $resvar;
2386                    $lastphpop = &$op;
2387                    $lastresvar = &$op['php'];
2388                }
2389            }
2390        }
2391        return $T;
2392    }
2393    // }}}
2394    function unquote($str, $st, $ed) // {{{
2395    {
2396        $l1 = strlen($st);
2397        $l2 = strlen($ed);
2398        if (substr($str, 0, $l1) === $st && substr($str, -$l2) === $ed) {
2399            $str = substr($str, $l1, -$l2);
2400        }
2401        return $str;
2402    }
2403    // }}}
2404    function popargs(&$EX, $n) // {{{
2405    {
2406        $args = array();
2407        for ($i = 0; $i < $n; $i ++) {
2408            $a = array_pop($EX['argstack']);
2409            if (is_array($a)) {
2410                array_unshift($args, foldToCode($a, $EX));
2411            }
2412            else {
2413                array_unshift($args, $a);
2414            }
2415        }
2416        return implode(', ', $args);
2417    }
2418    // }}}
2419    function dumpop($op, &$EX) // {{{
2420    {
2421        assert('isset($op)');
2422        $op1 = $op['op1'];
2423        $op2 = $op['op2'];
2424        $d = array(xcache_get_opcode($op['opcode']), $op['opcode']);
2425
2426        foreach (array('op1' => '1:', 'op2' => '2:', 'result' => '>') as $k => $kk) {
2427            switch ($op[$k]['op_type']) {
2428            case XC_IS_UNUSED:
2429                $d[$kk] = 'U:' . $op[$k]['opline_num'];
2430                break;
2431
2432            case XC_IS_VAR:
2433                $d[$kk] = '$' . $op[$k]['var'];
2434                if ($k != 'result') {
2435                    $d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
2436                }
2437                break;
2438
2439            case XC_IS_TMP_VAR:
2440                $d[$kk] = '#' . $op[$k]['var'];
2441                if ($k != 'result') {
2442                    $d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
2443                }
2444                break;
2445
2446            case XC_IS_CV:
2447                $d[$kk] = $this->getOpVal($op[$k], $EX);
2448                break;
2449
2450            default:
2451                $d[$kk] = $this->getOpVal($op[$k], $EX);
2452            }
2453        }
2454        $d[';'] = $op['extended_value'];
2455        if (!empty($op['jmpouts'])) {
2456            $d['>>'] = implode(',', $op['jmpouts']);
2457        }
2458        if (!empty($op['jmpins'])) {
2459            $d['<<'] = implode(',', $op['jmpins']);
2460        }
2461
2462        foreach ($d as $k => $v) {
2463            echo is_int($k) ? '' : $k, str($v), "\t";
2464        }
2465        echo PHP_EOL;
2466    }
2467    // }}}
2468    function dumpRange(&$EX, $range) // {{{
2469    {
2470        for ($i = $range[0]; $i <= $range[1]; ++$i) {
2471            echo $EX['indent'], $i, "\t"; $this->dumpop($EX['opcodes'][$i], $EX);
2472        }
2473        echo $EX['indent'], "==", PHP_EOL;
2474    }
2475    // }}}
2476    function dargs(&$EX) // {{{
2477    {
2478        $op_array = &$EX['op_array'];
2479
2480        if (isset($op_array['num_args'])) {
2481            $c = $op_array['num_args'];
2482        }
2483        else if (!empty($op_array['arg_types'])) {
2484            $c = count($op_array['arg_types']);
2485        }
2486        else {
2487            // php4
2488            $c = count($EX['recvs']);
2489        }
2490
2491        $refrest = false;
2492        for ($i = 0; $i < $c; $i ++) {
2493            if ($i) {
2494                echo ', ';
2495            }
2496            $arg = $EX['recvs'][$i + 1];
2497            if (isset($op_array['arg_info'])) {
2498                $ai = $op_array['arg_info'][$i];
2499                if (isset($ai['type_hint']) ? ($ai['type_hint'] == IS_CALLABLE || $ai['type_hint'] == IS_OBJECT) : !empty($ai['class_name'])) {
2500                    echo $this->stripNamespace($ai['class_name']), ' ';
2501                    if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
2502                        echo 'or NULL ';
2503                    }
2504                }
2505                else if (isset($ai['type_hint']) ? $ai['type_hint'] == IS_ARRAY : !empty($ai['array_type_hint'])) {
2506                    echo 'array ';
2507                    if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
2508                        echo 'or NULL ';
2509                    }
2510                }
2511                if ($ai['pass_by_reference']) {
2512                    echo '&';
2513                }
2514                printf("\$%s", $ai['name']);
2515            }
2516            else {
2517                if ($refrest) {
2518                    echo '&';
2519                }
2520                else if (!empty($op_array['arg_types']) && isset($op_array['arg_types'][$i])) {
2521                    switch ($op_array['arg_types'][$i]) {
2522                    case BYREF_FORCE_REST:
2523                        $refrest = true;
2524                        /* fall */
2525                    case BYREF_FORCE:
2526                        echo '&';
2527                        break;
2528
2529                    case BYREF_NONE:
2530                    case BYREF_ALLOW:
2531                        break;
2532                    default:
2533                        assert(0);
2534                    }
2535                }
2536                echo str($arg[0], $EX);
2537            }
2538            if (isset($arg[1])) {
2539                echo ' = ', str($arg[1], $EX);
2540            }
2541        }
2542    }
2543    // }}}
2544    function duses(&$EX) // {{{
2545    {
2546        if ($EX['uses']) {
2547            echo ' use(', implode(', ', $EX['uses']), ')';
2548        }
2549    }
2550    // }}}
2551    function dfunction($func, $indent = '', $decorations = array(), $nobody = false) // {{{
2552    {
2553        $this->detectNamespace($func['op_array']['function_name']);
2554
2555        if ($nobody) {
2556            $EX = array();
2557            $EX['op_array'] = &$func['op_array'];
2558            $EX['recvs'] = array();
2559            $EX['uses'] = array();
2560        }
2561        else {
2562            ob_start();
2563            $EX = &$this->dop_array($func['op_array'], $indent . INDENT);
2564            $body = ob_get_clean();
2565        }
2566
2567        $functionName = $this->stripNamespace($func['op_array']['function_name']);
2568        $isExpression = false;
2569        if ($functionName == '{closure}') {
2570            $functionName = '';
2571            $isExpression = true;
2572        }
2573        echo $isExpression ? '' : $indent;
2574        if ($decorations) {
2575            echo implode(' ', $decorations), ' ';
2576        }
2577        echo 'function', $functionName ? ' ' . $functionName : '', '(';
2578        $this->dargs($EX);
2579        echo ")";
2580        $this->duses($EX);
2581        if ($nobody) {
2582            echo ";\n";
2583        }
2584        else {
2585            if (!$isExpression) {
2586                echo "\n";
2587                echo $indent, "{\n";
2588            }
2589            else {
2590                echo " {\n";
2591            }
2592
2593            echo $body;
2594            echo "$indent}";
2595            if (!$isExpression) {
2596                echo "\n";
2597            }
2598        }
2599    }
2600    // }}}
2601    function dclass($class, $indent = '') // {{{
2602    {
2603        $this->detectNamespace($class['name']);
2604
2605        // {{{ class decl
2606        if (!empty($class['doc_comment'])) {
2607            echo $indent;
2608            echo $class['doc_comment'];
2609            echo "\n";
2610        }
2611        $isInterface = false;
2612        $decorations = array();
2613        if (!empty($class['ce_flags'])) {
2614            if ($class['ce_flags'] & ZEND_ACC_INTERFACE) {
2615                $isInterface = true;
2616            }
2617            else {
2618                if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
2619                    $decorations[] = "abstract";
2620                }
2621                if ($class['ce_flags'] & ZEND_ACC_FINAL_CLASS) {
2622                    $decorations[] = "final";
2623                }
2624            }
2625        }
2626
2627        echo $indent;
2628        if ($decorations) {
2629            echo implode(' ', $decorations), ' ';
2630        }
2631        echo $isInterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']);
2632        if ($class['parent']) {
2633            echo ' extends ', $this->stripNamespace($class['parent']);
2634        }
2635        /* TODO */
2636        if (!empty($class['interfaces'])) {
2637            echo ' implements ';
2638            echo implode(', ', $class['interfaces']);
2639        }
2640        echo "\n";
2641        echo $indent, "{";
2642        // }}}
2643        $newindent = INDENT . $indent;
2644        // {{{ const
2645        if (!empty($class['constants_table'])) {
2646            echo "\n";
2647            foreach ($class['constants_table'] as $name => $v) {
2648                echo $newindent;
2649                echo 'const ', $name, ' = ';
2650                echo str(value($v, $EX), $newindent);
2651                echo ";\n";
2652            }
2653        }
2654        // }}}
2655        // {{{ properties
2656        if (ZEND_ENGINE_2 && !ZEND_ENGINE_2_4) {
2657            $default_static_members = $class[ZEND_ENGINE_2_1 ? 'default_static_members' : 'static_members'];
2658        }
2659        $member_variables = $class[ZEND_ENGINE_2 ? 'properties_info' : 'default_properties'];
2660        if ($member_variables) {
2661            echo "\n";
2662            foreach ($member_variables as $name => $dummy) {
2663                $info = isset($class['properties_info']) ? $class['properties_info'][$name] : null;
2664                if (isset($info) && !empty($info['doc_comment'])) {
2665                    echo $newindent;
2666                    echo $info['doc_comment'];
2667                    echo "\n";
2668                }
2669
2670                echo $newindent;
2671                if (ZEND_ENGINE_2) {
2672                    $static = ($info['flags'] & ZEND_ACC_STATIC);
2673
2674                    if ($static) {
2675                        echo "static ";
2676                    }
2677                }
2678
2679                $mangleSuffix = '';
2680                if (!ZEND_ENGINE_2) {
2681                    echo 'var ';
2682                }
2683                else if (!isset($info)) {
2684                    echo 'public ';
2685                }
2686                else {
2687                    if ($info['flags'] & ZEND_ACC_SHADOW) {
2688                        continue;
2689                    }
2690                    switch ($info['flags'] & ZEND_ACC_PPP_MASK) {
2691                    case ZEND_ACC_PUBLIC:
2692                        echo "public ";
2693                        break;
2694                    case ZEND_ACC_PRIVATE:
2695                        echo "private ";
2696                        $mangleSuffix = "\000";
2697                        break;
2698                    case ZEND_ACC_PROTECTED:
2699                        echo "protected ";
2700                        $mangleSuffix = "\000";
2701                        break;
2702                    }
2703                }
2704
2705                echo '$', $name;
2706
2707                if (ZEND_ENGINE_2_4) {
2708                    $value = $class[$static ? 'default_static_members_table' : 'default_properties_table'][$info['offset']];
2709                }
2710                else if (!ZEND_ENGINE_2) {
2711                    $value = $class['default_properties'][$name];
2712                }
2713                else {
2714                    $key = $info['name'] . $mangleSuffix;
2715                    if ($static) {
2716                        $value = $default_static_members[$key];
2717                    }
2718                    else {
2719                        $value = $class['default_properties'][$key];
2720                    }
2721                }
2722                if (isset($value)) {
2723                    echo ' = ';
2724                    echo str(value($value, $EX), $newindent);
2725                }
2726                echo ";\n";
2727            }
2728        }
2729        // }}}
2730        // {{{ function_table
2731        if (isset($class['function_table'])) {
2732            foreach ($class['function_table'] as $func) {
2733                if (!isset($func['scope']) || $func['scope'] == $class['name']) {
2734                    // TODO: skip shadow here
2735                    echo "\n";
2736                    $opa = $func['op_array'];
2737                    if (!empty($opa['doc_comment'])) {
2738                        echo $newindent;
2739                        echo $opa['doc_comment'];
2740                        echo "\n";
2741                    }
2742                    $isAbstractMethod = false;
2743                    $decorations = array();
2744                    if (isset($opa['fn_flags'])) {
2745                        if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isInterface) {
2746                            $decorations[] = "abstract";
2747                            $isAbstractMethod = true;
2748                        }
2749                        if ($opa['fn_flags'] & ZEND_ACC_FINAL) {
2750                            $decorations[] = "final";
2751                        }
2752                        if ($opa['fn_flags'] & ZEND_ACC_STATIC) {
2753                            $decorations[] = "static";
2754                        }
2755
2756                        switch ($opa['fn_flags'] & ZEND_ACC_PPP_MASK) {
2757                            case ZEND_ACC_PUBLIC:
2758                                $decorations[] = "public";
2759                                break;
2760                            case ZEND_ACC_PRIVATE:
2761                                $decorations[] = "private";
2762                                break;
2763                            case ZEND_ACC_PROTECTED:
2764                                $decorations[] = "protected";
2765                                break;
2766                            default:
2767                                $decorations[] = "<visibility error>";
2768                                break;
2769                        }
2770                    }
2771                    $this->activeMethod = $this->activeClass . '::' . $opa['function_name'];
2772                    $this->activeFunction = $opa['function_name'];
2773                    $this->dfunction($func, $newindent, $decorations, $isInterface || $isAbstractMethod);
2774                    $this->activeFunction = null;
2775                    $this->activeMethod = null;
2776                    if ($opa['function_name'] == 'Decompiler') {
2777                        //exit;
2778                    }
2779                }
2780            }
2781        }
2782        // }}}
2783        echo $indent, "}\n";
2784    }
2785    // }}}
2786    function decompileString($string) // {{{
2787    {
2788        $this->dc = xcache_dasm_string($string);
2789        if ($this->dc === false) {
2790            echo "error compling string\n";
2791            return false;
2792        }
2793        $this->activeFile = null;
2794        return true;
2795    }
2796    // }}}
2797    function decompileFile($file) // {{{
2798    {
2799        $this->dc = xcache_dasm_file($file);
2800        if ($this->dc === false) {
2801            echo "error compling $file\n";
2802            return false;
2803        }
2804        $this->activeFile = realpath($file);
2805        return true;
2806    }
2807    // }}}
2808    function decompileDasm($content) // {{{
2809    {
2810        $this->dc = $content;
2811        $this->activeFile = null;
2812        return true;
2813    }
2814    // }}}
2815    function output() // {{{
2816    {
2817        echo "<?". "php\n\n";
2818        foreach ($this->dc['class_table'] as $key => $class) {
2819            if ($key{0} != "\0") {
2820                $this->activeClass = $class['name'];
2821                $this->dclass($class);
2822                $this->activeClass = null;
2823                echo "\n";
2824            }
2825        }
2826
2827        foreach ($this->dc['function_table'] as $key => $func) {
2828            if ($key{0} != "\0") {
2829                $this->activeFunction = $key;
2830                $this->dfunction($func);
2831                $this->activeFunction = null;
2832                echo "\n";
2833            }
2834        }
2835
2836        $this->dop_array($this->dc['op_array']);
2837        echo "\n?" . ">\n";
2838
2839        if (!empty($this->test)) {
2840            $this->outputUnusedOp();
2841        }
2842        return true;
2843    }
2844    // }}}
2845    function outputUnusedOp() // {{{
2846    {
2847        for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
2848            if ($opname == 'UNDEF')  {
2849                continue;
2850            }
2851
2852            if (!isset($this->usedOps[$i])) {
2853                echo "not covered opcode ", $opname, "\n";
2854            }
2855        }
2856    }
2857    // }}}
2858}
2859
2860// {{{ defines
2861define('ZEND_ENGINE_2_6', PHP_VERSION >= "5.6");
2862define('ZEND_ENGINE_2_5', ZEND_ENGINE_2_6 || PHP_VERSION >= "5.5.");
2863define('ZEND_ENGINE_2_4', ZEND_ENGINE_2_5 || PHP_VERSION >= "5.4.");
2864define('ZEND_ENGINE_2_3', ZEND_ENGINE_2_4 || PHP_VERSION >= "5.3.");
2865define('ZEND_ENGINE_2_2', ZEND_ENGINE_2_3 || PHP_VERSION >= "5.2.");
2866define('ZEND_ENGINE_2_1', ZEND_ENGINE_2_2 || PHP_VERSION >= "5.1.");
2867define('ZEND_ENGINE_2',   ZEND_ENGINE_2_1 || PHP_VERSION >= "5.0.");
2868
2869define('ZEND_ACC_STATIC',         0x01);
2870define('ZEND_ACC_ABSTRACT',       0x02);
2871define('ZEND_ACC_FINAL',          0x04);
2872define('ZEND_ACC_IMPLEMENTED_ABSTRACT',       0x08);
2873
2874define('ZEND_ACC_IMPLICIT_ABSTRACT_CLASS',    0x10);
2875define('ZEND_ACC_EXPLICIT_ABSTRACT_CLASS',    0x20);
2876define('ZEND_ACC_FINAL_CLASS',                0x40);
2877define('ZEND_ACC_INTERFACE',                  0x80);
2878if (ZEND_ENGINE_2_4) {
2879    define('ZEND_ACC_TRAIT',                  0x120);
2880}
2881define('ZEND_ACC_PUBLIC',     0x100);
2882define('ZEND_ACC_PROTECTED',  0x200);
2883define('ZEND_ACC_PRIVATE',    0x400);
2884define('ZEND_ACC_PPP_MASK',  (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE));
2885
2886define('ZEND_ACC_CHANGED',    0x800);
2887define('ZEND_ACC_IMPLICIT_PUBLIC',    0x1000);
2888
2889define('ZEND_ACC_CTOR',       0x2000);
2890define('ZEND_ACC_DTOR',       0x4000);
2891define('ZEND_ACC_CLONE',      0x8000);
2892
2893define('ZEND_ACC_ALLOW_STATIC',   0x10000);
2894
2895define('ZEND_ACC_SHADOW', 0x2000);
2896
2897if (ZEND_ENGINE_2_4) {
2898    define('ZEND_FETCH_GLOBAL',           0x00000000);
2899    define('ZEND_FETCH_LOCAL',            0x10000000);
2900    define('ZEND_FETCH_STATIC',           0x20000000);
2901    define('ZEND_FETCH_STATIC_MEMBER',    0x30000000);
2902    define('ZEND_FETCH_GLOBAL_LOCK',      0x40000000);
2903    define('ZEND_FETCH_LEXICAL',          0x50000000);
2904
2905    define('ZEND_FETCH_TYPE_MASK',        0x70000000);
2906
2907    define('ZEND_FETCH_STANDARD',         0x00000000);
2908    define('ZEND_FETCH_ADD_LOCK',         0x08000000);
2909    define('ZEND_FETCH_MAKE_REF',         0x04000000);
2910}
2911else {
2912    define('ZEND_FETCH_GLOBAL',           0);
2913    define('ZEND_FETCH_LOCAL',            1);
2914    define('ZEND_FETCH_STATIC',           2);
2915    define('ZEND_FETCH_STATIC_MEMBER',    3);
2916    define('ZEND_FETCH_GLOBAL_LOCK',      4);
2917
2918    define('ZEND_FETCH_STANDARD',         0);
2919    define('ZEND_FETCH_ADD_LOCK',         1);
2920}
2921
2922if (ZEND_ENGINE_2_4) {
2923    define('ZEND_ISSET',                  0x02000000);
2924    define('ZEND_ISEMPTY',                0x01000000);
2925    define('ZEND_ISSET_ISEMPTY_MASK',     (ZEND_ISSET | ZEND_ISEMPTY));
2926    define('ZEND_QUICK_SET',              0x00800000);
2927}
2928else {
2929    define('ZEND_ISSET',                  (1<<0));
2930    define('ZEND_ISEMPTY',                (1<<1));
2931
2932    define('ZEND_ISSET_ISEMPTY_MASK',     (ZEND_ISSET | ZEND_ISEMPTY));
2933}
2934
2935define('ZEND_FETCH_CLASS_DEFAULT',    0);
2936define('ZEND_FETCH_CLASS_SELF',       1);
2937define('ZEND_FETCH_CLASS_PARENT',     2);
2938define('ZEND_FETCH_CLASS_MAIN',       3);
2939define('ZEND_FETCH_CLASS_GLOBAL',     4);
2940define('ZEND_FETCH_CLASS_AUTO',       5);
2941define('ZEND_FETCH_CLASS_INTERFACE',  6);
2942define('ZEND_FETCH_CLASS_STATIC',     7);
2943if (ZEND_ENGINE_2_4) {
2944    define('ZEND_FETCH_CLASS_TRAIT',     14);
2945}
2946if (ZEND_ENGINE_2_3) {
2947    define('ZEND_FETCH_CLASS_MASK',     0xF);
2948}
2949
2950define('ZEND_EVAL',               (1<<0));
2951define('ZEND_INCLUDE',            (1<<1));
2952define('ZEND_INCLUDE_ONCE',       (1<<2));
2953define('ZEND_REQUIRE',            (1<<3));
2954define('ZEND_REQUIRE_ONCE',       (1<<4));
2955
2956if (ZEND_ENGINE_2_4) {
2957    define('EXT_TYPE_UNUSED',     (1<<5));
2958}
2959else {
2960    define('EXT_TYPE_UNUSED',     (1<<0));
2961}
2962
2963if (ZEND_ENGINE_2_1) {
2964    define('ZEND_FE_FETCH_BYREF',     1);
2965    define('ZEND_FE_FETCH_WITH_KEY',  2);
2966}
2967else {
2968    define('ZEND_UNSET_DIM',          1);
2969    define('ZEND_UNSET_OBJ',          2);
2970}
2971
2972define('ZEND_MEMBER_FUNC_CALL',   1<<0);
2973define('ZEND_CTOR_CALL',          1<<1);
2974
2975define('ZEND_ARG_SEND_BY_REF',        (1<<0));
2976define('ZEND_ARG_COMPILE_TIME_BOUND', (1<<1));
2977define('ZEND_ARG_SEND_FUNCTION',      (1<<2));
2978
2979define('BYREF_NONE',       0);
2980define('BYREF_FORCE',      1);
2981define('BYREF_ALLOW',      2);
2982define('BYREF_FORCE_REST', 3);
2983define('IS_NULL',     0);
2984define('IS_LONG',     1);
2985define('IS_DOUBLE',   2);
2986define('IS_BOOL',     ZEND_ENGINE_2_1 ? 3 : 6);
2987define('IS_ARRAY',    4);
2988define('IS_OBJECT',   5);
2989define('IS_STRING',   ZEND_ENGINE_2_1 ? 6 : 3);
2990define('IS_RESOURCE', 7);
2991define('IS_CONSTANT', 8);
2992if (ZEND_ENGINE_2_6) {
2993    define('IS_CONSTANT_ARRAY', -1);
2994    define('IS_CONSTANT_AST', 9);
2995}
2996else {
2997    define('IS_CONSTANT_ARRAY', 9);
2998}
2999if (ZEND_ENGINE_2_4) {
3000    define('IS_CALLABLE', 10);
3001}
3002/* Ugly hack to support constants as static array indices */
3003define('IS_CONSTANT_TYPE_MASK',   0x0f);
3004define('IS_CONSTANT_UNQUALIFIED', 0x10);
3005define('IS_CONSTANT_INDEX',       0x80);
3006define('IS_LEXICAL_VAR',          0x20);
3007define('IS_LEXICAL_REF',          0x40);
3008
3009if (ZEND_ENGINE_2_6) {
3010    define('ZEND_CONST',          256);
3011    define('ZEND_BOOL_AND',       256 + 1);
3012    define('ZEND_BOOL_OR',        256 + 2);
3013    define('ZEND_SELECT',         256 + 3);
3014    define('ZEND_UNARY_PLUS',     256 + 4);
3015    define('ZEND_UNARY_MINUS',    256 + 5);
3016}
3017
3018@define('XC_IS_CV', 16);
3019
3020/*
3021if (preg_match_all('!XC_[A-Z_]+!', file_get_contents(__FILE__), $ms)) {
3022    $verdiff = array();
3023    foreach ($ms[0] as $k) {
3024        if (!defined($k)) {
3025            $verdiff[$k] = -1;
3026            define($k, -1);
3027        }
3028    }
3029    var_export($verdiff);
3030    exit;
3031}
3032//*/
3033foreach (array (
3034    'XC_ADD_INTERFACE' => -1,
3035    'XC_ASSIGN_DIM' => -1,
3036    'XC_ASSIGN_OBJ' => -1,
3037    'XC_CATCH' => -1,
3038    'XC_CLONE' => -1,
3039    'XC_DECLARE_CLASS' => -1,
3040    'XC_DECLARE_CONST' => -1,
3041    'XC_DECLARE_FUNCTION' => -1,
3042    'XC_DECLARE_FUNCTION_OR_CLASS' => -1,
3043    'XC_DECLARE_INHERITED_CLASS' => -1,
3044    'XC_DECLARE_INHERITED_CLASS_DELAYED' => -1,
3045    'XC_DECLARE_LAMBDA_FUNCTION' => -1,
3046    'XC_DO_FCALL_BY_FUNC' => -1,
3047    'XC_FETCH_CLASS' => -1,
3048    'XC_GENERATOR_RETURN' => -1,
3049    'XC_GOTO' => -1,
3050    'XC_HANDLE_EXCEPTION' => -1,
3051    'XC_INIT_CTOR_CALL' => -1,
3052    'XC_INIT_FCALL_BY_FUNC' => -1,
3053    'XC_INIT_METHOD_CALL' => -1,
3054    'XC_INIT_NS_FCALL_BY_NAME' => -1,
3055    'XC_INIT_STATIC_METHOD_CALL' => -1,
3056    'XC_INSTANCEOF' => -1,
3057    'XC_ISSET_ISEMPTY' => -1,
3058    'XC_ISSET_ISEMPTY_DIM_OBJ' => -1,
3059    'XC_ISSET_ISEMPTY_PROP_OBJ' => -1,
3060    'XC_ISSET_ISEMPTY_VAR' => -1,
3061    'XC_JMP_NO_CTOR' => -1,
3062    'XC_JMP_SET' => -1,
3063    'XC_JMP_SET_VAR' => -1,
3064    'XC_OP_DATA' => -1,
3065    'XC_POST_DEC_OBJ' => -1,
3066    'XC_POST_INC_OBJ' => -1,
3067    'XC_PRE_DEC_OBJ' => -1,
3068    'XC_PRE_INC_OBJ' => -1,
3069    'XC_QM_ASSIGN_VAR' => -1,
3070    'XC_RAISE_ABSTRACT_ERROR' => -1,
3071    'XC_THROW' => -1,
3072    'XC_UNSET_DIM' => -1,
3073    'XC_UNSET_DIM_OBJ' => -1,
3074    'XC_UNSET_OBJ' => -1,
3075    'XC_USER_OPCODE' => -1,
3076    'XC_VERIFY_ABSTRACT_CLASS' => -1,
3077    'XC_YIELD' => -1,
3078) as $k => $v) {
3079    if (!defined($k)) {
3080        define($k, $v);
3081    }
3082}
3083// }}}
3084
Note: See TracBrowser for help on using the repository browser.