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

Last change on this file since 1374 was 1374, checked in by moo, 14 months ago

Decompiler: fixes #318 (support for const in static array index/value)

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