source: trunk/Decompiler.class.php @ 787

Last change on this file since 787 was 787, checked in by moo, 3 years ago

decompile code branch

  • Property svn:eol-style set to native
File size: 60.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 str($code, $indent = '') // {{{
12{
13    if (is_array($code)) {
14        $array = array();
15        foreach ($code as $key => $value) {
16            $array[$key] = str($value, $indent);
17        }
18        return $array;
19    }
20    if (is_object($code)) {
21        $code = foldToCode($code, $indent);
22        return $code->toCode($indent);
23    }
24
25    return (string) $code;
26}
27// }}}
28function foldToCode($src, $indent = '') // {{{ wrap or rewrap anything to Decompiler_Code
29{
30    if (is_array($indent)) {
31        $indent = $indent['indent'];
32    }
33
34    if (!is_object($src)) {
35        return new Decompiler_Code($src);
36    }
37
38    if (!method_exists($src, 'toCode')) {
39        var_dump($src);
40        exit('no toCode');
41    }
42    if (get_class($src) != 'Decompiler_Code') {
43        // rewrap it
44        $src = new Decompiler_Code($src->toCode($indent));
45    }
46
47    return $src;
48}
49// }}}
50function value($value) // {{{
51{
52    $spec = xcache_get_special_value($value);
53    if (isset($spec)) {
54        $value = $spec;
55        if (!is_array($value)) {
56            // constant
57            return $value;
58        }
59    }
60
61    if (is_a($value, 'Decompiler_Object')) {
62        // use as is
63    }
64    else if (is_array($value)) {
65        $value = new Decompiler_ConstArray($value);
66    }
67    else {
68        $value = new Decompiler_Value($value);
69    }
70    return $value;
71}
72// }}}
73function unquoteName_($str, $asVariableName, $indent = '') // {{{
74{
75    $str = str($str, $indent);
76    if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
77        return str_replace('\\\\', '\\', substr($str, 1, -1));
78    }
79    else if ($asVariableName) {
80        return "{" . $str . "}";
81    }
82    else {
83        return $str;
84    }
85}
86// }}}
87function unquoteVariableName($str, $indent = '') // {{{
88{
89    return unquoteName_($str, true, $indent);
90}
91// }}}
92function unquoteName($str, $indent = '') // {{{
93{
94    return unquoteName_($str, false, $indent);
95}
96// }}}
97class Decompiler_Object // {{{
98{
99}
100// }}}
101class Decompiler_Value extends Decompiler_Object // {{{
102{
103    var $value;
104
105    function Decompiler_Value($value = null)
106    {
107        $this->value = $value;
108    }
109
110    function toCode($indent)
111    {
112        $code = var_export($this->value, true);
113        if (gettype($this->value) == 'string') {
114            switch ($this->value) {
115            case "\r":
116                return '"\\r"';
117            case "\n":
118                return '"\\n"';
119            case "\r\n":
120                return '"\\r\\n"';
121            }
122            $code = str_replace("\r\n", '\' . "\\r\\n" . \'', $code);
123            $code = str_replace("\r", '\' . "\\r" . \'', $code);
124            $code = str_replace("\n", '\' . "\\n" . \'', $code);
125        }
126        return $code;
127    }
128}
129// }}}
130class Decompiler_Code extends Decompiler_Object // {{{
131{
132    var $src;
133
134    function Decompiler_Code($src)
135    {
136        assert('isset($src)');
137        $this->src = $src;
138    }
139
140    function toCode($indent)
141    {
142        return $this->src;
143    }
144}
145// }}}
146class Decompiler_Binop extends Decompiler_Code // {{{
147{
148    var $opc;
149    var $op1;
150    var $op2;
151    var $parent;
152    var $indent;
153
154    function Decompiler_Binop($parent, $op1, $opc, $op2)
155    {
156        $this->parent = &$parent;
157        $this->opc = $opc;
158        $this->op1 = $op1;
159        $this->op2 = $op2;
160    }
161
162    function toCode($indent)
163    {
164        $opstr = $this->parent->binops[$this->opc];
165
166        $op1 = foldToCode($this->op1, $indent);
167        if (is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) {
168            $op1 = "(" . str($op1, $indent) . ")";
169        }
170        $op2 = foldToCode($this->op2, $indent);
171        if (is_a($this->op2, 'Decompiler_Binop') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') {
172            $op2 = "(" . str($op2, $indent) . ")";
173        }
174
175        if (str($op1) == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
176            return $opstr . str($op2, $indent);
177        }
178
179        return str($op1) . ' ' . $opstr . ' ' . str($op2);
180    }
181}
182// }}}
183class Decompiler_Fetch extends Decompiler_Code // {{{
184{
185    var $src;
186    var $fetchType;
187
188    function Decompiler_Fetch($src, $type, $globalsrc)
189    {
190        $this->src = $src;
191        $this->fetchType = $type;
192        $this->globalsrc = $globalsrc;
193    }
194
195    function toCode($indent)
196    {
197        switch ($this->fetchType) {
198        case ZEND_FETCH_LOCAL:
199            return '$' . substr($this->src, 1, -1);
200        case ZEND_FETCH_STATIC:
201            if (ZEND_ENGINE_2_3) {
202                // closure local variable?
203                return str($this->src);
204            }
205            die('static fetch cant to string');
206        case ZEND_FETCH_GLOBAL:
207        case ZEND_FETCH_GLOBAL_LOCK:
208            return $this->globalsrc;
209        default:
210            var_dump($this->fetchType);
211            assert(0);
212        }
213    }
214}
215// }}}
216class Decompiler_Box // {{{
217{
218    var $obj;
219
220    function Decompiler_Box(&$obj)
221    {
222        $this->obj = &$obj;
223    }
224
225    function toCode($indent)
226    {
227        return $this->obj->toCode($indent);
228    }
229}
230// }}}
231class Decompiler_Dim extends Decompiler_Value // {{{
232{
233    var $offsets = array();
234    var $isLast = false;
235    var $isObject = false;
236    var $assign = null;
237
238    function toCode($indent)
239    {
240        if (is_a($this->value, 'Decompiler_ListBox')) {
241            $exp = str($this->value->obj->src, $indent);
242        }
243        else {
244            $exp = str($this->value, $indent);
245        }
246        $last = count($this->offsets) - 1;
247        foreach ($this->offsets as $i => $dim) {
248            if ($this->isObject && $i == $last) {
249                $exp .= '->' . unquoteVariableName($dim, $indent);
250            }
251            else {
252                $exp .= '[' . str($dim, $indent) . ']';
253            }
254        }
255        return $exp;
256    }
257}
258// }}}
259class Decompiler_DimBox extends Decompiler_Box // {{{
260{
261}
262// }}}
263class Decompiler_List extends Decompiler_Code // {{{
264{
265    var $src;
266    var $dims = array();
267    var $everLocked = false;
268
269    function toCode($indent)
270    {
271        if (count($this->dims) == 1 && !$this->everLocked) {
272            $dim = $this->dims[0];
273            unset($dim->value);
274            $dim->value = $this->src;
275            if (!isset($dim->assign)) {
276                return str($dim, $indent);
277            }
278            return str($this->dims[0]->assign, $indent) . ' = ' . str($dim, $indent);
279        }
280        /* flatten dims */
281        $assigns = array();
282        foreach ($this->dims as $dim) {
283            $assign = &$assigns;
284            foreach ($dim->offsets as $offset) {
285                $assign = &$assign[$offset];
286            }
287            $assign = foldToCode($dim->assign, $indent);
288        }
289        return str($this->toList($assigns)) . ' = ' . str($this->src, $indent);
290    }
291
292    function toList($assigns)
293    {
294        $keys = array_keys($assigns);
295        if (count($keys) < 2) {
296            $keys[] = 0;
297        }
298        $max = call_user_func_array('max', $keys);
299        $list = 'list(';
300        for ($i = 0; $i <= $max; $i ++) {
301            if ($i) {
302                $list .= ', ';
303            }
304            if (!isset($assigns[$i])) {
305                continue;
306            }
307            if (is_array($assigns[$i])) {
308                $list .= $this->toList($assigns[$i]);
309            }
310            else {
311                $list .= $assigns[$i];
312            }
313        }
314        return $list . ')';
315    }
316}
317// }}}
318class Decompiler_ListBox extends Decompiler_Box // {{{
319{
320}
321// }}}
322class Decompiler_Array extends Decompiler_Value // {{{
323{
324    // emenets
325    function Decompiler_Array()
326    {
327        $this->value = array();
328    }
329
330    function toCode($indent)
331    {
332        $subindent = $indent . INDENT;
333
334        $elementsCode = array();
335        $index = 0;
336        foreach ($this->value as $element) {
337            list($key, $value) = $element;
338            if (!isset($key)) {
339                $key = $index++;
340            }
341            $elementsCode[] = array(str($key, $subindent), str($value, $subindent), $key, $value);
342        }
343
344        $exp = "array(";
345        $indent = $indent . INDENT;
346        $assocWidth = 0;
347        $multiline = 0;
348        $i = 0;
349        foreach ($elementsCode as $element) {
350            list($keyCode, $valueCode) = $element;
351            if ((string) $i !== $keyCode) {
352                $assocWidth = 1;
353                break;
354            }
355            ++$i;
356        }
357        foreach ($elementsCode as $element) {
358            list($keyCode, $valueCode, $key, $value) = $element;
359            if ($assocWidth) {
360                $len = strlen($keyCode);
361                if ($assocWidth < $len) {
362                    $assocWidth = $len;
363                }
364            }
365            if (is_array($value) || is_a($value, 'Decompiler_Array')) {
366                $multiline ++;
367            }
368        }
369
370        $i = 0;
371        foreach ($elementsCode as $element) {
372            list($keyCode, $value) = $element;
373            if ($multiline) {
374                if ($i) {
375                    $exp .= ",";
376                }
377                $exp .= "\n";
378                $exp .= $indent;
379            }
380            else {
381                if ($i) {
382                    $exp .= ", ";
383                }
384            }
385
386            if ($assocWidth) {
387                if ($multiline) {
388                    $exp .= sprintf("%-{$assocWidth}s => ", $keyCode);
389                }
390                else {
391                    $exp .= $keyCode . ' => ';
392                }
393            }
394
395            $exp .= $value;
396
397            $i ++;
398        }
399        if ($multiline) {
400            $exp .= "\n$indent)";
401        }
402        else {
403            $exp .= ")";
404        }
405        return $exp;
406    }
407}
408// }}}
409class Decompiler_ConstArray extends Decompiler_Array // {{{
410{
411    function Decompiler_ConstArray($array)
412    {
413        $elements = array();
414        foreach ($array as $key => $value) {
415            $elements[] = array(value($key), value($value));
416        }
417        $this->value = $elements;
418    }
419}
420// }}}
421class Decompiler_ForeachBox extends Decompiler_Box // {{{
422{
423    var $iskey;
424
425    function toCode($indent)
426    {
427        return 'foreach (' . '';
428    }
429}
430// }}}
431
432class Decompiler
433{
434    var $namespace;
435    var $namespaceDecided;
436
437    function Decompiler()
438    {
439        // {{{ testing
440        // XC_UNDEF XC_OP_DATA
441        $this->test = !empty($_ENV['XCACHE_DECOMPILER_TEST']);
442        $this->usedOps = array();
443
444        if ($this->test) {
445            $content = file_get_contents(__FILE__);
446            for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
447                if (!preg_match("/\\bXC_" . $opname . "\\b(?!')/", $content)) {
448                    echo "not recognized opcode ", $opname, "\n";
449                }
450            }
451        }
452        // }}}
453        // {{{ opinfo
454        $this->unaryops = array(
455                XC_BW_NOT   => '~',
456                XC_BOOL_NOT => '!',
457                );
458        $this->binops = array(
459                XC_ADD                 => "+",
460                XC_ASSIGN_ADD          => "+=",
461                XC_SUB                 => "-",
462                XC_ASSIGN_SUB          => "-=",
463                XC_MUL                 => "*",
464                XC_ASSIGN_MUL          => "*=",
465                XC_DIV                 => "/",
466                XC_ASSIGN_DIV          => "/=",
467                XC_MOD                 => "%",
468                XC_ASSIGN_MOD          => "%=",
469                XC_SL                  => "<<",
470                XC_ASSIGN_SL           => "<<=",
471                XC_SR                  => ">>",
472                XC_ASSIGN_SR           => ">>=",
473                XC_CONCAT              => ".",
474                XC_ASSIGN_CONCAT       => ".=",
475                XC_IS_IDENTICAL        => "===",
476                XC_IS_NOT_IDENTICAL    => "!==",
477                XC_IS_EQUAL            => "==",
478                XC_IS_NOT_EQUAL        => "!=",
479                XC_IS_SMALLER          => "<",
480                XC_IS_SMALLER_OR_EQUAL => "<=",
481                XC_BW_OR               => "|",
482                XC_ASSIGN_BW_OR        => "|=",
483                XC_BW_AND              => "&",
484                XC_ASSIGN_BW_AND       => "&=",
485                XC_BW_XOR              => "^",
486                XC_ASSIGN_BW_XOR       => "^=",
487                XC_BOOL_XOR            => "xor",
488                );
489        // }}}
490        $this->includeTypes = array( // {{{
491                ZEND_EVAL         => 'eval',
492                ZEND_INCLUDE      => 'include',
493                ZEND_INCLUDE_ONCE => 'include_once',
494                ZEND_REQUIRE      => 'require',
495                ZEND_REQUIRE_ONCE => 'require_once',
496                );
497                // }}}
498    }
499    function detectNamespace($name) // {{{
500    {
501        if ($this->namespaceDecided) {
502            return;
503        }
504
505        if (strpos($name, '\\') !== false) {
506            $this->namespace = strtok($name, '\\');
507            echo 'namespace ', $this->namespace, ";\n\n";
508        }
509
510        $this->namespaceDecided = true;
511    }
512    // }}}
513    function stripNamespace($name) // {{{
514    {
515        $len = strlen($this->namespace) + 1;
516        if (substr($name, 0, $len) == $this->namespace . '\\') {
517            return substr($name, $len);
518        }
519        else {
520            return $name;
521        }
522    }
523    // }}}
524    function outputPhp(&$EX, $opline, $last, $indent) // {{{
525    {
526        $origindent = $indent;
527        $curticks = 0;
528        for ($i = $opline; $i <= $last; $i ++) {
529            $op = $EX['opcodes'][$i];
530            if (isset($op['gofrom'])) {
531                echo 'label' . $i, ":\n";
532            }
533            if (isset($op['php'])) {
534                $toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0;
535                if ($curticks != $toticks) {
536                    $oldticks = $curticks;
537                    $curticks = $toticks;
538                    if (!$curticks) {
539                        echo $origindent, "}\n\n";
540                        $indent = $origindent;
541                    }
542                    else {
543                        if ($oldticks) {
544                            echo $origindent, "}\n\n";
545                        }
546                        else if (!$oldticks) {
547                            $indent .= INDENT;
548                        }
549                        echo $origindent, "declare (ticks=$curticks) {\n";
550                    }
551                }
552                echo $indent, str($op['php'], $indent), ";\n";
553            }
554        }
555        if ($curticks) {
556            echo $origindent, "}\n";
557        }
558    }
559    // }}}
560    function getOpVal($op, &$EX, $tostr = true, $free = false) // {{{
561    {
562        switch ($op['op_type']) {
563        case XC_IS_CONST:
564            return foldToCode(value($op['constant']), $EX);
565
566        case XC_IS_VAR:
567        case XC_IS_TMP_VAR:
568            $T = &$EX['Ts'];
569            $ret = $T[$op['var']];
570            if ($tostr) {
571                $ret = foldToCode($ret, $EX);
572            }
573            if ($free) {
574                unset($T[$op['var']]);
575            }
576            return $ret;
577
578        case XC_IS_CV:
579            $var = $op['var'];
580            $var = $EX['op_array']['vars'][$var];
581            return '$' . $var['name'];
582
583        case XC_IS_UNUSED:
584            return null;
585        }
586    }
587    // }}}
588    function removeKeyPrefix($array, $prefix) // {{{
589    {
590        $prefixLen = strlen($prefix);
591        $ret = array();
592        foreach ($array as $key => $value) {
593            if (substr($key, 0, $prefixLen) == $prefix) {
594                $key = substr($key, $prefixLen);
595            }
596            $ret[$key] = $value;
597        }
598        return $ret;
599    }
600    // }}}
601    function &fixOpcode($opcodes, $removeTailing = false, $defaultReturnValue = null) // {{{
602    {
603        for ($i = 0, $cnt = count($opcodes); $i < $cnt; $i ++) {
604            if (function_exists('xcache_get_fixed_opcode')) {
605                $opcodes[$i]['opcode'] = xcache_get_fixed_opcode($opcodes[$i]['opcode'], $i);
606            }
607            if (isset($opcodes[$i]['op1'])) {
608                $opcodes[$i]['op1'] = $this->removeKeyPrefix($opcodes[$i]['op1'], 'u.');
609                $opcodes[$i]['op2'] = $this->removeKeyPrefix($opcodes[$i]['op2'], 'u.');
610                $opcodes[$i]['result'] = $this->removeKeyPrefix($opcodes[$i]['result'], 'u.');
611            }
612            else {
613                $op = array(
614                    'op1' => array(),
615                    'op2' => array(),
616                    'op3' => array(),
617                );
618                foreach ($opcodes[$i] as $name => $value) {
619                    if (preg_match('!^(op1|op2|result)\\.(.*)!', $name, $m)) {
620                        list(, $which, $field) = $m;
621                        $op[$which][$field] = $value;
622                    }
623                    else if (preg_match('!^(op1|op2|result)_type$!', $name, $m)) {
624                        list(, $which) = $m;
625                        $op[$which]['op_type'] = $value;
626                    }
627                    else {
628                        $op[$name] = $value;
629                    }
630                }
631                $opcodes[$i] = $op;
632            }
633        }
634
635        if ($removeTailing) {
636            $last = count($opcodes) - 1;
637            if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
638                $this->usedOps[XC_HANDLE_EXCEPTION] = true;
639                $opcodes[$last]['opcode'] = XC_NOP;
640                --$last;
641            }
642            if ($opcodes[$last]['opcode'] == XC_RETURN) {
643                $op1 = $opcodes[$last]['op1'];
644                if ($op1['op_type'] == XC_IS_CONST && array_key_exists('constant', $op1) && $op1['constant'] === $defaultReturnValue) {
645                    $opcodes[$last]['opcode'] = XC_NOP;
646                    --$last;
647                }
648            }
649        }
650        return $opcodes;
651    }
652    // }}}
653    function decompileBasicBlock(&$EX, $first, $last, $indent) // {{{
654    {
655        $this->dasmBasicBlock($EX, $first, $last);
656        // $this->dumpRange($EX, $first, $last);
657        $this->outputPhp($EX, $first, $last, $indent);
658    }
659    // }}}
660    function removeJmpInfo(&$EX, $line) // {{{
661    {
662        $opcodes = &$EX['opcodes'];
663        foreach ($opcodes[$line]['jmpouts'] as $jmpTo) {
664            $jmpins = &$opcodes[$jmpTo]['jmpins'];
665            $jmpins = array_flip($jmpins);
666            unset($jmpins[$line]);
667            $jmpins = array_keys($jmpins);
668        }
669        $opcodes[$line]['opcode'] = XC_NOP;
670        unset($opcodes[$line]['jmpouts']);
671    }
672    // }}}
673    function decompileComplexBlock(&$EX, $first, $last, $indent) // {{{
674    {
675        $opcodes = &$EX['opcodes'];
676
677        $firstOp = &$opcodes[$first];
678        $lastOp = &$opcodes[$last];
679
680        if ($lastOp['opcode'] == XC_JMPNZ
681         && $lastOp['jmpouts'][0] == $first) {
682            $this->removeJmpInfo($EX, $last);
683            echo $indent, 'do {', PHP_EOL;
684            $this->recognizeAndDecompileClosedBlocks($EX, $first, $last, $indent . INDENT);
685            echo $indent, '} while (', str($this->getOpVal($lastOp['op1'], $EX)), ');', PHP_EOL;
686            return;
687        }
688
689        $firstJmp = null;
690        $firstJmpOp = null;
691        for ($i = $first; $i <= $last; ++$i) {
692            if (!empty($opcodes[$i]['jmpouts'])) {
693                $firstJmp = $i;
694                $firstJmpOp = &$opcodes[$firstJmp];
695                break;
696            }
697        }
698
699        if (isset($firstJmpOp)
700         && $firstJmpOp['opcode'] == XC_JMPZ
701         && $lastOp['opcode'] == XC_JMP
702         && $lastOp['jmpouts'][0] == $first) {
703            $this->removeJmpInfo($EX, $firstJmp);
704            $this->removeJmpInfo($EX, $last);
705
706            ob_start();
707            $this->recognizeAndDecompileClosedBlocks($EX, $first, $last, $indent . INDENT);
708            $code = ob_get_clean();
709            echo $indent, 'while (', str($this->getOpVal($firstJmpOp['op1'], $EX)), ') {', PHP_EOL;
710            echo $code;
711            echo $indent, '}', PHP_EOL;
712            return;
713        }
714
715        $this->decompileBasicBlock($EX, $first, $last, $indent);
716    }
717    // }}}
718    function recognizeAndDecompileClosedBlocks(&$EX, $first, $last, $indent) // {{{ decompile in a tree way
719    {
720        $opcodes = &$EX['opcodes'];
721        $firstComplex = false;
722
723        $i = $starti = $first;
724        while ($i <= $last) {
725            $op = $opcodes[$i];
726            if (!empty($op['jmpins']) || !empty($op['jmpouts'])) {
727                $blockFirst = $i;
728                $blockLast = -1;
729                $i = $blockFirst;
730                do {
731                    if (!empty($op['jmpins'])) {
732                        // care about jumping from blocks behind, not before
733                        foreach ($op['jmpins'] as $oplineNumber) {
734                            if ($oplineNumber <= $last && $blockLast < $oplineNumber) {
735                                $blockLast = $oplineNumber;
736                            }
737                        }
738                    }
739                    if (!empty($op['jmpouts'])) {
740                        $blockLast = max($blockLast, max($op['jmpouts']) - 1);
741                    }
742                    ++$i;
743                } while ($i <= $blockLast);
744                assert('$blockLast <= $last');
745
746                if ($blockLast >= $blockFirst) {
747                    if ($blockFirst > $starti) {
748                        $this->decompileBasicBlock($EX, $starti, $blockFirst - 1, $indent);
749                    }
750                    $this->decompileComplexBlock($EX, $blockFirst, $blockLast, $indent);
751                    $i = $starti = $blockLast + 1;
752                }
753                continue;
754            }
755            ++$i;
756        }
757        if ($starti <= $last) {
758            $this->decompileBasicBlock($EX, $starti, $last, $indent);
759        }
760    }
761    // }}}
762    function &dop_array($op_array, $indent = '') // {{{
763    {
764        $op_array['opcodes'] = $this->fixOpcode($op_array['opcodes'], true, $indent == '' ? 1 : null);
765        $opcodes = &$op_array['opcodes'];
766        // {{{ build jmpins/jmpouts to op_array
767        for ($i = 0, $cnt = count($opcodes); $i < $cnt; $i ++) {
768            $op = &$opcodes[$i];
769            $op['line'] = $i;
770            switch ($op['opcode']) {
771            case XC_CONT:
772            case XC_BRK:
773                $op['jmpouts'] = array();
774                break;
775
776            case XC_GOTO:
777                $target = $op['op1']['var'];
778                $op['goto'] = $target;
779                $opcodes[$target]['gofrom'][] = $i;
780                break;
781
782            case XC_JMP:
783                $target = $op['op1']['var'];
784                $op['jmpouts'] = array($target);
785                $opcodes[$target]['jmpins'][] = $i;
786                break;
787
788            case XC_JMPZNZ:
789                $jmpz = $op['op2']['opline_num'];
790                $jmpnz = $op['extended_value'];
791                $op['jmpouts'] = array($jmpz, $jmpnz);
792                $opcodes[$jmpz]['jmpins'][] = $i;
793                $opcodes[$jmpnz]['jmpins'][] = $i;
794                break;
795
796            case XC_JMPZ:
797            case XC_JMPNZ:
798            case XC_JMPZ_EX:
799            case XC_JMPNZ_EX:
800            case XC_JMP_SET:
801            // case XC_FE_RESET:
802            case XC_FE_FETCH:
803            // case XC_JMP_NO_CTOR:
804                $target = $op['op2']['opline_num'];
805                //if (!isset($target)) {
806                //  $this->dumpop($op, $EX);
807                //  var_dump($op); exit;
808                //}
809                $op['jmpouts'] = array($target);
810                $opcodes[$target]['jmpins'][] = $i;
811                break;
812
813            /*
814            case XC_RETURN:
815                $op['jmpouts'] = array();
816                break;
817            */
818            }
819            /*
820            if (!empty($op['jmpouts']) || !empty($op['jmpins'])) {
821                echo $i, "\t", xcache_get_opcode($op['opcode']), PHP_EOL;
822            }
823            // */
824        }
825        unset($op);
826        // }}}
827        // build semi-basic blocks
828        $nextbbs = array();
829        $starti = 0;
830        for ($i = 1, $cnt = count($opcodes); $i < $cnt; $i ++) {
831            if (isset($opcodes[$i]['jmpins'])
832             || isset($opcodes[$i - 1]['jmpouts'])) {
833                $nextbbs[$starti] = $i;
834                $starti = $i;
835            }
836        }
837        $nextbbs[$starti] = $cnt;
838
839        $EX = array();
840        $EX['Ts'] = array();
841        $EX['indent'] = $indent;
842        $EX['nextbbs'] = $nextbbs;
843        $EX['op_array'] = &$op_array;
844        $EX['opcodes'] = &$opcodes;
845        // func call
846        $EX['object'] = null;
847        $EX['called_scope'] = null;
848        $EX['fbc'] = null;
849        $EX['argstack'] = array();
850        $EX['arg_types_stack'] = array();
851        $EX['last'] = count($opcodes) - 1;
852        $EX['silence'] = 0;
853        $EX['recvs'] = array();
854        $EX['uses'] = array();
855
856        // decompile in a tree way
857        $this->recognizeAndDecompileClosedBlocks($EX, 0, count($opcodes) - 1, $EX['indent']);
858        return $EX;
859    }
860    // }}}
861    function outputCode(&$EX, $opline, $last, $indent, $loop = false) // {{{
862    {
863        $op = &$EX['opcodes'][$opline];
864        $next = $EX['nextbbs'][$opline];
865
866        $end = $next - 1;
867        if ($end > $last) {
868            $end = $last;
869        }
870
871        if (isset($op['jmpins'])) {
872            echo "\nline", $op['line'], ":\n";
873        }
874        else {
875            // echo ";;;\n";
876        }
877        $this->dasmBasicBlock($EX, $opline, $end);
878        $this->outputPhp($EX, $opline, $end, $indent);
879        // jmpout op
880        $op = &$EX['opcodes'][$end];
881        $op1 = $op['op1'];
882        $op2 = $op['op2'];
883        $ext = $op['extended_value'];
884        $line = $op['line'];
885
886        if (isset($EX['opcodes'][$next])) {
887            if (isset($last) && $next > $last) {
888                $next = null;
889            }
890        }
891        else {
892            $next = null;
893        }
894        if ($op['opcode'] == XC_FE_FETCH) {
895            $opline = $next;
896            $next = $op['op2']['opline_num'];
897            $end = $next - 1;
898
899            ob_start();
900            $this->outputCode($EX, $opline, $end /* - 1 skip last jmp */, $indent . INDENT);
901            $body = ob_get_clean();
902
903            $as = foldToCode($op['fe_as'], $EX);
904            if (isset($op['fe_key'])) {
905                $as = str($op['fe_key'], $EX) . ' => ' . str($as);
906            }
907            echo "{$indent}foreach (" . str($op['fe_src'], $EX) . " as $as) {\n";
908            echo $body;
909            echo "{$indent}}";
910            // $this->outputCode($EX, $next, $last, $indent);
911            // return;
912        }
913        /*
914        if ($op['opcode'] == XC_JMPZ) {
915            $target = $op2['opline_num'];
916            if ($line + 1) {
917                $nextblock = $EX['nextbbs'][$next];
918                $jmpop = end($nextblock);
919                if ($jmpop['opcode'] == XC_JMP) {
920                    $ifendline = $op2['opline_num'];
921                    if ($ifendline >= $line) {
922                        $cond = $op['cond'];
923                        echo "{$indent}if ($cond) {\n";
924                        $this->outputCode($EX, $next, $last, INDENT . $indent);
925                        echo "$indent}\n";
926                        $this->outputCode($EX, $target, $last, $indent);
927                        return;
928                    }
929                }
930            }
931        }
932        */
933        if (!isset($next)) {
934            return;
935        }
936        if (isset($op['jmpouts']) && isset($op['isjmp'])) {
937            if (isset($op['cond'])) {
938                echo "{$indent}check (" . str($op["cond"]) . ") {\n";
939                echo INDENT;
940            }
941            switch ($op['opcode']) {
942            case XC_CONT:
943            case XC_BRK:
944                break;
945
946            default:
947                echo $indent;
948                echo xcache_get_opcode($op['opcode']), ' line', $op['jmpouts'][0];
949                if (isset($op['jmpouts'][1])) {
950                    echo ', line', $op['jmpouts'][1];
951                }
952                echo ";";
953                // echo ' // <- line', $op['line'];
954                echo "\n";
955            }
956            if (isset($op['cond'])) echo "$indent}\n";
957        }
958
959        // proces JMPZ_EX/JMPNZ_EX for AND,OR
960        $op = &$EX['opcodes'][$next];
961        /*
962        if (isset($op['jmpins'])) {
963            foreach (array_reverse($op['jmpins']) as $fromline) {
964                $fromop = $EX['opcodes'][$fromline];
965                switch ($fromop['opcode']) {
966                case XC_JMPZ_EX: $opstr = 'and'; break;
967                case XC_JMPNZ_EX: $opstr = 'or'; break;
968                case XC_JMPZNZ: var_dump($fromop); exit;
969                default: continue 2;
970                }
971
972                $var = $fromop['result']['var'];
973                var_dump($EX['Ts'][$var]);
974                $EX['Ts'][$var] = '(' . $fromop['and_or'] . " $opstr " . $EX['Ts'][$var] . ')';
975            }
976            #$this->outputCode($EX, $next, $last, $indent);
977            #return;
978        }
979        */
980        if (isset($op['cond_false'])) {
981            // $this->dumpop($op, $EX);
982            // any true comes here, so it's a "or"
983            $cond = implode(' and ', str($op['cond_false']));
984            // var_dump($op['cond'] = $cond);
985            /*
986            $rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue;
987            unset($op['cond_true']);
988            */
989        }
990
991        if ($loop) {
992            return array($next, $last);
993        }
994        $this->outputCode($EX, $next, $last, $indent);
995    }
996    // }}}
997    function dasmBasicBlock(&$EX, $opline, $last) // {{{
998    {
999        $T = &$EX['Ts'];
1000        $opcodes = &$EX['opcodes'];
1001        $lastphpop = null;
1002
1003        for ($i = $opline; $i <= $last; $i ++) {
1004            // {{{ prepair
1005            $op = &$opcodes[$i];
1006            $opc = $op['opcode'];
1007            if ($opc == XC_NOP) {
1008                $this->usedOps[$opc] = true;
1009                continue;
1010            }
1011
1012            $op1 = $op['op1'];
1013            $op2 = $op['op2'];
1014            $res = $op['result'];
1015            $ext = $op['extended_value'];
1016
1017            $opname = xcache_get_opcode($opc);
1018
1019            if ($opname == 'UNDEF' || !isset($opname)) {
1020                echo 'UNDEF OP:';
1021                $this->dumpop($op, $EX);
1022                continue;
1023            }
1024            // echo $i, ' '; $this->dumpop($op, $EX); //var_dump($op);
1025
1026            $resvar = null;
1027            if ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) {
1028                $istmpres = false;
1029            }
1030            else {
1031                $istmpres = true;
1032            }
1033            // }}}
1034            // echo $opname, "\n";
1035
1036            $call = array(&$this, $opname);
1037            if (is_callable($call)) {
1038                $this->usedOps[$opc] = true;
1039                $this->{$opname}($op, $EX);
1040            }
1041            else if (isset($this->binops[$opc])) { // {{{
1042                $this->usedOps[$opc] = true;
1043                $op1val = $this->getOpVal($op1, $EX, false);
1044                $op2val = $this->getOpVal($op2, $EX, false);
1045                $rvalue = new Decompiler_Binop($this, $op1val, $opc, $op2val);
1046                $resvar = $rvalue;
1047                // }}}
1048            }
1049            else if (isset($this->unaryops[$opc])) { // {{{
1050                $this->usedOps[$opc] = true;
1051                $op1val = $this->getOpVal($op1, $EX);
1052                $myop = $this->unaryops[$opc];
1053                $rvalue = $myop . str($op1val);
1054                $resvar = $rvalue;
1055                // }}}
1056            }
1057            else {
1058                $covered = true;
1059                switch ($opc) {
1060                case XC_NEW: // {{{
1061                    array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
1062                    $EX['object'] = (int) $res['var'];
1063                    $EX['called_scope'] = null;
1064                    $EX['fbc'] = 'new ' . unquoteName($this->getOpVal($op1, $EX), $EX);
1065                    if (!ZEND_ENGINE_2) {
1066                        $resvar = '$new object$';
1067                    }
1068                    break;
1069                    // }}}
1070                case XC_THROW: // {{{
1071                    $resvar = 'throw ' . str($this->getOpVal($op1, $EX));
1072                    break;
1073                    // }}}
1074                case XC_CLONE: // {{{
1075                    $resvar = 'clone ' . str($this->getOpVal($op1, $EX));
1076                    break;
1077                    // }}}
1078                case XC_CATCH: // {{{
1079                    $resvar = 'catch (' . str($this->getOpVal($op1, $EX)) . ' ' . str($this->getOpVal($op2, $EX)) . ')';
1080                    break;
1081                    // }}}
1082                case XC_INSTANCEOF: // {{{
1083                    $resvar = str($this->getOpVal($op1, $EX)) . ' instanceof ' . str($this->getOpVal($op2, $EX));
1084                    break;
1085                    // }}}
1086                case XC_FETCH_CLASS: // {{{
1087                    if ($op2['op_type'] == XC_IS_UNUSED) {
1088                        switch (($ext & (defined('ZEND_FETCH_CLASS_MASK') ? ZEND_FETCH_CLASS_MASK : 0xFF))) {
1089                        case ZEND_FETCH_CLASS_SELF:
1090                            $class = 'self';
1091                            break;
1092                        case ZEND_FETCH_CLASS_PARENT:
1093                            $class = 'parent';
1094                            break;
1095                        case ZEND_FETCH_CLASS_STATIC:
1096                            $class = 'static';
1097                            break;
1098                        }
1099                        $istmpres = true;
1100                    }
1101                    else {
1102                        $class = $this->getOpVal($op2, $EX);
1103                        if (isset($op2['constant'])) {
1104                            $class = $this->stripNamespace(unquoteName($class));
1105                        }
1106                    }
1107                    $resvar = $class;
1108                    break;
1109                    // }}}
1110                case XC_FETCH_CONSTANT: // {{{
1111                    if ($op1['op_type'] == XC_IS_UNUSED) {
1112                        $resvar = $this->stripNamespace($op2['constant']);
1113                        break;
1114                    }
1115
1116                    if ($op1['op_type'] == XC_IS_CONST) {
1117                        $resvar = $this->stripNamespace($op1['constant']);
1118                    }
1119                    else {
1120                        $resvar = $this->getOpVal($op1, $EX);
1121                    }
1122
1123                    $resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2, $EX));
1124                    break;
1125                    // }}}
1126                    // {{{ case XC_FETCH_*
1127                case XC_FETCH_R:
1128                case XC_FETCH_W:
1129                case XC_FETCH_RW:
1130                case XC_FETCH_FUNC_ARG:
1131                case XC_FETCH_UNSET:
1132                case XC_FETCH_IS:
1133                case XC_UNSET_VAR:
1134                    $rvalue = $this->getOpVal($op1, $EX);
1135                    if (defined('ZEND_FETCH_TYPE_MASK')) {
1136                        $fetchtype = ($ext & ZEND_FETCH_TYPE_MASK);
1137                    }
1138                    else {
1139                        $fetchtype = $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type'];
1140                    }
1141                    switch ($fetchtype) {
1142                    case ZEND_FETCH_STATIC_MEMBER:
1143                        $class = $this->getOpVal($op2, $EX);
1144                        $rvalue = str($class) . '::$' . unquoteName($rvalue, $EX);
1145                        break;
1146                    default:
1147                        $name = unquoteName($rvalue, $EX);
1148                        $globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($rvalue) . "]";
1149                        $rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname);
1150                        break;
1151                    }
1152                    if ($opc == XC_UNSET_VAR) {
1153                        $op['php'] = "unset(" . str($rvalue, $EX) . ")";
1154                        $lastphpop = &$op;
1155                    }
1156                    else if ($res['op_type'] != XC_IS_UNUSED) {
1157                        $resvar = $rvalue;
1158                    }
1159                    break;
1160                    // }}}
1161                    // {{{ case XC_FETCH_DIM_*
1162                case XC_FETCH_DIM_TMP_VAR:
1163                case XC_FETCH_DIM_R:
1164                case XC_FETCH_DIM_W:
1165                case XC_FETCH_DIM_RW:
1166                case XC_FETCH_DIM_FUNC_ARG:
1167                case XC_FETCH_DIM_UNSET:
1168                case XC_FETCH_DIM_IS:
1169                case XC_ASSIGN_DIM:
1170                case XC_UNSET_DIM_OBJ: // PHP 4 only
1171                case XC_UNSET_DIM:
1172                case XC_UNSET_OBJ:
1173                    $src = $this->getOpVal($op1, $EX, false);
1174                    if (is_a($src, "Decompiler_ForeachBox")) {
1175                        $src->iskey = $this->getOpVal($op2, $EX);
1176                        $resvar = $src;
1177                        break;
1178                    }
1179
1180                    if (is_a($src, "Decompiler_DimBox")) {
1181                        $dimbox = $src;
1182                    }
1183                    else {
1184                        if (!is_a($src, "Decompiler_ListBox")) {
1185                            $op1val = $this->getOpVal($op1, $EX, false);
1186                            $list = new Decompiler_List(isset($op1val) ? $op1val : '$this');
1187
1188                            $src = new Decompiler_ListBox($list);
1189                            if (!isset($op1['var'])) {
1190                                $this->dumpop($op, $EX);
1191                                var_dump($op);
1192                                die('missing var');
1193                            }
1194                            $T[$op1['var']] = $src;
1195                            unset($list);
1196                        }
1197                        $dim = new Decompiler_Dim($src);
1198                        $src->obj->dims[] = &$dim;
1199
1200                        $dimbox = new Decompiler_DimBox($dim);
1201                    }
1202                    $dim = &$dimbox->obj;
1203                    $dim->offsets[] = $this->getOpVal($op2, $EX);
1204                    if ($ext == ZEND_FETCH_ADD_LOCK) {
1205                        $src->obj->everLocked = true;
1206                    }
1207                    else if ($ext == ZEND_FETCH_STANDARD) {
1208                        $dim->isLast = true;
1209                    }
1210                    if ($opc == XC_UNSET_OBJ) {
1211                        $dim->isObject = true;
1212                    }
1213                    unset($dim);
1214                    $rvalue = $dimbox;
1215                    unset($dimbox);
1216
1217                    if ($opc == XC_ASSIGN_DIM) {
1218                        $lvalue = $rvalue;
1219                        ++ $i;
1220                        $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
1221                        $resvar = str($lvalue, $EX) . ' = ' . str($rvalue);
1222                    }
1223                    else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ) {
1224                        $op['php'] = "unset(" . str($rvalue, $EX) . ")";
1225                        $lastphpop = &$op;
1226                    }
1227                    else if ($res['op_type'] != XC_IS_UNUSED) {
1228                        $resvar = $rvalue;
1229                    }
1230                    break;
1231                    // }}}
1232                case XC_ASSIGN: // {{{
1233                    $lvalue = $this->getOpVal($op1, $EX);
1234                    $rvalue = $this->getOpVal($op2, $EX, false);
1235                    if (is_a($rvalue, 'Decompiler_ForeachBox')) {
1236                        $type = $rvalue->iskey ? 'fe_key' : 'fe_as';
1237                        $rvalue->obj[$type] = $lvalue;
1238                        unset($T[$op2['var']]);
1239                        break;
1240                    }
1241                    if (is_a($rvalue, "Decompiler_DimBox")) {
1242                        $dim = &$rvalue->obj;
1243                        $dim->assign = $lvalue;
1244                        if ($dim->isLast) {
1245                            $resvar = foldToCode($dim->value, $EX);
1246                        }
1247                        unset($dim);
1248                        break;
1249                    }
1250                    if (is_a($rvalue, 'Decompiler_Fetch')) {
1251                        $src = str($rvalue->src, $EX);
1252                        if ('$' . unquoteName($src) == $lvalue) {
1253                            switch ($rvalue->fetchType) {
1254                            case ZEND_FETCH_STATIC:
1255                                $statics = &$EX['op_array']['static_variables'];
1256                                if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) {
1257                                    $EX['uses'][] = str($lvalue);
1258                                    unset($statics);
1259                                    break 2;
1260                                }
1261                                unset($statics);
1262                            }
1263                        }
1264                    }
1265                    $resvar = "$lvalue = " . str($rvalue, $EX);
1266                    break;
1267                    // }}}
1268                case XC_ASSIGN_REF: // {{{
1269                    $lvalue = $this->getOpVal($op1, $EX);
1270                    $rvalue = $this->getOpVal($op2, $EX, false);
1271                    if (is_a($rvalue, 'Decompiler_Fetch')) {
1272                        $src = str($rvalue->src, $EX);
1273                        if ('$' . unquoteName($src) == $lvalue) {
1274                            switch ($rvalue->fetchType) {
1275                            case ZEND_FETCH_GLOBAL:
1276                            case ZEND_FETCH_GLOBAL_LOCK:
1277                                $resvar = 'global ' . $lvalue;
1278                                break 2;
1279                            case ZEND_FETCH_STATIC:
1280                                $statics = &$EX['op_array']['static_variables'];
1281                                if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) {
1282                                    $EX['uses'][] = '&' . str($lvalue);
1283                                    unset($statics);
1284                                    break 2;
1285                                }
1286
1287                                $resvar = 'static ' . $lvalue;
1288                                $name = unquoteName($src);
1289                                if (isset($statics[$name])) {
1290                                    $var = $statics[$name];
1291                                    $resvar .= ' = ';
1292                                    $resvar .= str(value($var), $EX);
1293                                }
1294                                unset($statics);
1295                                break 2;
1296                            default:
1297                            }
1298                        }
1299                    }
1300                    // TODO: PHP_6 global
1301                    $rvalue = str($rvalue, $EX);
1302                    $resvar = "$lvalue = &$rvalue";
1303                    break;
1304                    // }}}
1305                // {{{ case XC_FETCH_OBJ_*
1306                case XC_FETCH_OBJ_R:
1307                case XC_FETCH_OBJ_W:
1308                case XC_FETCH_OBJ_RW:
1309                case XC_FETCH_OBJ_FUNC_ARG:
1310                case XC_FETCH_OBJ_UNSET:
1311                case XC_FETCH_OBJ_IS:
1312                case XC_ASSIGN_OBJ:
1313                    $obj = $this->getOpVal($op1, $EX);
1314                    if (!isset($obj)) {
1315                        $obj = '$this';
1316                    }
1317                    $rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
1318                    if ($res['op_type'] != XC_IS_UNUSED) {
1319                        $resvar = $rvalue;
1320                    }
1321                    if ($opc == XC_ASSIGN_OBJ) {
1322                        ++ $i;
1323                        $lvalue = $rvalue;
1324                        $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
1325                        $resvar = "$lvalue = " . str($rvalue);
1326                    }
1327                    break;
1328                    // }}}
1329                case XC_ISSET_ISEMPTY_DIM_OBJ:
1330                case XC_ISSET_ISEMPTY_PROP_OBJ:
1331                case XC_ISSET_ISEMPTY:
1332                case XC_ISSET_ISEMPTY_VAR: // {{{
1333                    if ($opc == XC_ISSET_ISEMPTY_VAR) {
1334                        $rvalue = $this->getOpVal($op1, $EX);
1335                        // for < PHP_5_3
1336                        if ($op1['op_type'] == XC_IS_CONST) {
1337                            $rvalue = '$' . unquoteVariableName($this->getOpVal($op1, $EX));
1338                        }
1339                        if ($op2['EA.type'] == ZEND_FETCH_STATIC_MEMBER) {
1340                            $class = $this->getOpVal($op2, $EX);
1341                            $rvalue = $class . '::' . $rvalue;
1342                        }
1343                    }
1344                    else if ($opc == XC_ISSET_ISEMPTY) {
1345                        $rvalue = $this->getOpVal($op1, $EX);
1346                    }
1347                    else {
1348                        $container = $this->getOpVal($op1, $EX);
1349                        $dim = $this->getOpVal($op2, $EX);
1350                        if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) {
1351                            if (!isset($container)) {
1352                                $container = '$this';
1353                            }
1354                            $rvalue = $container . "->" . unquoteVariableName($dim);
1355                        }
1356                        else {
1357                            $rvalue = $container . '[' . str($dim) .']';
1358                        }
1359                    }
1360
1361                    switch ((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & (ZEND_ISSET|ZEND_ISEMPTY)) {
1362                    case ZEND_ISSET:
1363                        $rvalue = "isset(" . str($rvalue) . ")";
1364                        break;
1365                    case ZEND_ISEMPTY:
1366                        $rvalue = "empty(" . str($rvalue) . ")";
1367                        break;
1368                    }
1369                    $resvar = $rvalue;
1370                    break;
1371                    // }}}
1372                case XC_SEND_VAR_NO_REF:
1373                case XC_SEND_VAL:
1374                case XC_SEND_REF:
1375                case XC_SEND_VAR: // {{{
1376                    $ref = ($opc == XC_SEND_REF ? '&' : '');
1377                    $EX['argstack'][] = $ref . str($this->getOpVal($op1, $EX));
1378                    break;
1379                    // }}}
1380                case XC_INIT_STATIC_METHOD_CALL:
1381                case XC_INIT_METHOD_CALL: // {{{
1382                    array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
1383                    if ($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) {
1384                        $obj = $this->getOpVal($op1, $EX);
1385                        if (!isset($obj)) {
1386                            $obj = '$this';
1387                        }
1388                        if ($opc == XC_INIT_STATIC_METHOD_CALL || /* PHP4 */ isset($op1['constant'])) {
1389                            $EX['object'] = null;
1390                            $EX['called_scope'] = $this->stripNamespace(unquoteName($obj, $EX));
1391                        }
1392                        else {
1393                            $EX['object'] = $obj;
1394                            $EX['called_scope'] = null;
1395                        }
1396                        if ($res['op_type'] != XC_IS_UNUSED) {
1397                            $resvar = '$obj call$';
1398                        }
1399                    }
1400                    else {
1401                        $EX['object'] = null;
1402                        $EX['called_scope'] = null;
1403                    }
1404
1405                    $EX['fbc'] = $this->getOpVal($op2, $EX, false);
1406                    if (($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL) && !isset($EX['fbc'])) {
1407                        $EX['fbc'] = '__construct';
1408                    }
1409                    break;
1410                    // }}}
1411                case XC_INIT_NS_FCALL_BY_NAME:
1412                case XC_INIT_FCALL_BY_NAME: // {{{
1413                    array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
1414                    if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) {
1415                        break;
1416                    }
1417                    $EX['object'] = null;
1418                    $EX['called_scope'] = null;
1419                    $EX['fbc'] = $this->getOpVal($op2, $EX);
1420                    break;
1421                    // }}}
1422                case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4?
1423                    $EX['object'] = null;
1424                    $EX['called_scope'] = null;
1425                    $which = $op1['var'];
1426                    $EX['fbc'] = $EX['op_array']['funcs'][$which]['name'];
1427                    break;
1428                    // }}}
1429                case XC_DO_FCALL_BY_FUNC:
1430                    $which = $op1['var'];
1431                    $fname = $EX['op_array']['funcs'][$which]['name'];
1432                    $args = $this->popargs($EX, $ext);
1433                    $resvar = $fname . "($args)";
1434                    break;
1435                case XC_DO_FCALL:
1436                    $fname = unquoteName($this->getOpVal($op1, $EX, false), $EX);
1437                    $args = $this->popargs($EX, $ext);
1438                    $resvar = $fname . "($args)";
1439                    break;
1440                case XC_DO_FCALL_BY_NAME: // {{{
1441                    $object = null;
1442
1443                    $fname = unquoteName($EX['fbc'], $EX);
1444                    if (!is_int($EX['object'])) {
1445                        $object = $EX['object'];
1446                    }
1447
1448                    $args = $this->popargs($EX, $ext);
1449
1450                    $prefix = (isset($object) ? $object . '->' : '' )
1451                        . (isset($EX['called_scope']) ? $EX['called_scope'] . '::' : '' );
1452                    $resvar = $prefix
1453                        . (!$prefix ? $this->stripNamespace($fname) : $fname)
1454                        . "($args)";
1455                    unset($args);
1456
1457                    if (is_int($EX['object'])) {
1458                        $T[$EX['object']] = $resvar;
1459                        $resvar = null;
1460                    }
1461                    list($EX['fbc'], $EX['object'], $EX['called_scope']) = array_pop($EX['arg_types_stack']);
1462                    break;
1463                    // }}}
1464                case XC_VERIFY_ABSTRACT_CLASS: // {{{
1465                    //unset($T[$op1['var']]);
1466                    break;
1467                    // }}}
1468                case XC_DECLARE_CLASS: 
1469                case XC_DECLARE_INHERITED_CLASS:
1470                case XC_DECLARE_INHERITED_CLASS_DELAYED: // {{{
1471                    $key = $op1['constant'];
1472                    if (!isset($this->dc['class_table'][$key])) {
1473                        echo 'class not found: ', $key, 'existing classes are:', "\n";
1474                        var_dump(array_keys($this->dc['class_table']));
1475                        exit;
1476                    }
1477                    $class = &$this->dc['class_table'][$key];
1478                    if (!isset($class['name'])) {
1479                        $class['name'] = unquoteName($this->getOpVal($op2, $EX), $EX);
1480                    }
1481                    if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) {
1482                        $ext /= XC_SIZEOF_TEMP_VARIABLE;
1483                        $class['parent'] = $T[$ext];
1484                        unset($T[$ext]);
1485                    }
1486                    else {
1487                        $class['parent'] = null;
1488                    }
1489
1490                    for (;;) {
1491                        if ($i + 1 <= $last
1492                         && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE
1493                         && $opcodes[$i + 1]['op1']['var'] == $res['var']) {
1494                            // continue
1495                        }
1496                        else if ($i + 2 <= $last
1497                         && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
1498                         && $opcodes[$i + 2]['op1']['var'] == $res['var']
1499                         && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
1500                            // continue
1501                        }
1502                        else {
1503                            break;
1504                        }
1505                        $this->usedOps[XC_ADD_INTERFACE] = true;
1506
1507                        $fetchop = &$opcodes[$i + 1];
1508                        $interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2'], $EX), $EX));
1509                        $addop = &$opcodes[$i + 2];
1510                        $class['interfaces'][$addop['extended_value']] = $interface;
1511                        unset($fetchop, $addop);
1512                        $i += 2;
1513                    }
1514                    $this->dclass($class);
1515                    echo "\n";
1516                    unset($class);
1517                    break;
1518                    // }}}
1519                case XC_INIT_STRING: // {{{
1520                    $resvar = "''";
1521                    break;
1522                    // }}}
1523                case XC_ADD_CHAR:
1524                case XC_ADD_STRING:
1525                case XC_ADD_VAR: // {{{
1526                    $op1val = $this->getOpVal($op1, $EX);
1527                    $op2val = $this->getOpVal($op2, $EX);
1528                    switch ($opc) {
1529                    case XC_ADD_CHAR:
1530                        $op2val = value(chr(str($op2val)));
1531                        break;
1532                    case XC_ADD_STRING:
1533                        break;
1534                    case XC_ADD_VAR:
1535                        break;
1536                    }
1537                    if (str($op1val) == "''") {
1538                        $rvalue = $op2val;
1539                    }
1540                    else if (str($op2val) == "''") {
1541                        $rvalue = $op1val;
1542                    }
1543                    else {
1544                        $rvalue = str($op1val) . ' . ' . str($op2val);
1545                    }
1546                    $resvar = $rvalue;
1547                    // }}}
1548                    break;
1549                case XC_PRINT: // {{{
1550                    $op1val = $this->getOpVal($op1, $EX);
1551                    $resvar = "print(" . str($op1val) . ")";
1552                    break;
1553                    // }}}
1554                case XC_ECHO: // {{{
1555                    $op1val = $this->getOpVal($op1, $EX);
1556                    $resvar = "echo " . str($op1val);
1557                    break;
1558                    // }}}
1559                case XC_EXIT: // {{{
1560                    $op1val = $this->getOpVal($op1, $EX);
1561                    $resvar = "exit($op1val)";
1562                    break;
1563                    // }}}
1564                case XC_INIT_ARRAY:
1565                case XC_ADD_ARRAY_ELEMENT: // {{{
1566                    $rvalue = $this->getOpVal($op1, $EX, false, true);
1567
1568                    if ($opc == XC_ADD_ARRAY_ELEMENT) {
1569                        $assoc = $this->getOpVal($op2, $EX);
1570                        if (isset($assoc)) {
1571                            $T[$res['var']]->value[] = array($assoc, $rvalue);
1572                        }
1573                        else {
1574                            $T[$res['var']]->value[] = array(null, $rvalue);
1575                        }
1576                    }
1577                    else {
1578                        if ($opc == XC_INIT_ARRAY) {
1579                            $resvar = new Decompiler_Array();
1580                            if (!isset($rvalue)) {
1581                                continue;
1582                            }
1583                        }
1584
1585                        $assoc = $this->getOpVal($op2, $EX);
1586                        if (isset($assoc)) {
1587                            $resvar->value[] = array($assoc, $rvalue);
1588                        }
1589                        else {
1590                            $resvar->value[] = array(null, $rvalue);
1591                        }
1592                    }
1593                    break;
1594                    // }}}
1595                case XC_QM_ASSIGN: // {{{
1596                    $resvar = $this->getOpVal($op1, $EX);
1597                    break;
1598                    // }}}
1599                case XC_BOOL: // {{{
1600                    $resvar = /*'(bool) ' .*/ $this->getOpVal($op1, $EX);
1601                    break;
1602                    // }}}
1603                case XC_RETURN: // {{{
1604                    $resvar = "return " . str($this->getOpVal($op1, $EX));
1605                    break;
1606                    // }}}
1607                case XC_INCLUDE_OR_EVAL: // {{{
1608                    $type = $op2['var']; // hack
1609                    $keyword = $this->includeTypes[$type];
1610                    $resvar = "$keyword " . str($this->getOpVal($op1, $EX));
1611                    break;
1612                    // }}}
1613                case XC_FE_RESET: // {{{
1614                    $resvar = $this->getOpVal($op1, $EX);
1615                    break;
1616                    // }}}
1617                case XC_FE_FETCH: // {{{
1618                    $op['fe_src'] = $this->getOpVal($op1, $EX);
1619                    $fe = new Decompiler_ForeachBox($op);
1620                    $fe->iskey = false;
1621                    $T[$res['var']] = $fe;
1622
1623                    ++ $i;
1624                    if (($ext & ZEND_FE_FETCH_WITH_KEY)) {
1625                        $fe = new Decompiler_ForeachBox($op);
1626                        $fe->iskey = true;
1627
1628                        $res = $opcodes[$i]['result'];
1629                        $T[$res['var']] = $fe;
1630                    }
1631                    break;
1632                    // }}}
1633                case XC_SWITCH_FREE: // {{{
1634                    // unset($T[$op1['var']]);
1635                    break;
1636                    // }}}
1637                case XC_FREE: // {{{
1638                    $free = $T[$op1['var']];
1639                    if (!is_a($free, 'Decompiler_Array') && !is_a($free, 'Decompiler_Box')) {
1640                        $op['php'] = is_object($free) ? $free : $this->unquote($free, '(', ')');
1641                        $lastphpop = &$op;
1642                    }
1643                    unset($T[$op1['var']], $free);
1644                    break;
1645                    // }}}
1646                case XC_JMP_NO_CTOR:
1647                    break;
1648                case XC_JMP_SET: // ?:
1649                    $resvar = $this->getOpVal($op1, $EX);
1650                    $op['cond'] = $resvar; 
1651                    $op['isjmp'] = true;
1652                    break;
1653                case XC_JMPNZ: // while
1654                case XC_JMPZNZ: // for
1655                case XC_JMPZ_EX: // and
1656                case XC_JMPNZ_EX: // or
1657                case XC_JMPZ: // {{{
1658                    if ($opc == XC_JMP_NO_CTOR && $EX['object']) {
1659                        $rvalue = $EX['object'];
1660                    }
1661                    else {
1662                        $rvalue = $this->getOpVal($op1, $EX);
1663                    }
1664
1665                    if (isset($op['cond_true'])) {
1666                        // any true comes here, so it's a "or"
1667                        $rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue;
1668                        unset($op['cond_true']);
1669                    }
1670                    if (isset($op['cond_false'])) {
1671                        echo "TODO(cond_false):\n";
1672                        var_dump($op);// exit;
1673                    }
1674                    if ($opc == XC_JMPZ_EX || $opc == XC_JMPNZ_EX) {
1675                        $targetop = &$EX['opcodes'][$op2['opline_num']];
1676                        if ($opc == XC_JMPNZ_EX) {
1677                            $targetop['cond_true'][] = foldToCode($rvalue, $EX);
1678                        }
1679                        else {
1680                            $targetop['cond_false'][] = foldToCode($rvalue, $EX);
1681                        }
1682                        unset($targetop);
1683                    }
1684                    else {
1685                        $op['cond'] = $rvalue; 
1686                        $op['isjmp'] = true;
1687                    }
1688                    break;
1689                    // }}}
1690                case XC_CONT:
1691                case XC_BRK:
1692                    $op['cond'] = null;
1693                    $op['isjmp'] = true;
1694                    $resvar = $opc == XC_CONT ? 'continue' : 'break';
1695                    $count = str($this->getOpVal($op2, $EX));
1696                    if ($count != '1') {
1697                        $resvar .= ' ' . $count;
1698                    }
1699                    break;
1700                case XC_GOTO:
1701                    $resvar = 'goto label' . $op['op1']['var'];
1702                    $istmpres = false;
1703                    break;
1704
1705                case XC_JMP: // {{{
1706                    $op['cond'] = null;
1707                    $op['isjmp'] = true;
1708                    break;
1709                    // }}}
1710                case XC_CASE:
1711                    $switchValue = $this->getOpVal($op1, $EX);
1712                    $caseValue = $this->getOpVal($op2, $EX);
1713                    $resvar = str($switchValue) . ' == ' . str($caseValue);
1714                    break;
1715                case XC_RECV_INIT:
1716                case XC_RECV:
1717                    $offset = $this->getOpVal($op1, $EX);
1718                    $lvalue = $this->getOpVal($op['result'], $EX);
1719                    if ($opc == XC_RECV_INIT) {
1720                        $default = value($op['op2']['constant']);
1721                    }
1722                    else {
1723                        $default = null;
1724                    }
1725                    $EX['recvs'][str($offset)] = array($lvalue, $default);
1726                    break;
1727                case XC_POST_DEC:
1728                case XC_POST_INC:
1729                case XC_POST_DEC_OBJ:
1730                case XC_POST_INC_OBJ:
1731                case XC_PRE_DEC:
1732                case XC_PRE_INC:
1733                case XC_PRE_DEC_OBJ:
1734                case XC_PRE_INC_OBJ: // {{{
1735                    $flags = array_flip(explode('_', $opname));
1736                    if (isset($flags['OBJ'])) {
1737                        $resvar = $this->getOpVal($op1, $EX) . '->' . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
1738                    }
1739                    else {
1740                        $resvar = $this->getOpVal($op1, $EX);
1741                    }
1742                    $opstr = isset($flags['DEC']) ? '--' : '++';
1743                    if (isset($flags['POST'])) {
1744                        $resvar .= $opstr;
1745                    }
1746                    else {
1747                        $resvar = "$opstr$resvar";
1748                    }
1749                    break;
1750                    // }}}
1751
1752                case XC_BEGIN_SILENCE: // {{{
1753                    $EX['silence'] ++;
1754                    break;
1755                    // }}}
1756                case XC_END_SILENCE: // {{{
1757                    $EX['silence'] --;
1758                    $lastresvar = '@' . str($lastresvar, $EX);
1759                    break;
1760                    // }}}
1761                case XC_CAST: // {{{
1762                    $type = $ext;
1763                    static $type2cast = array(
1764                            IS_LONG   => '(int)',
1765                            IS_DOUBLE => '(double)',
1766                            IS_STRING => '(string)',
1767                            IS_ARRAY  => '(array)',
1768                            IS_OBJECT => '(object)',
1769                            IS_BOOL   => '(bool)',
1770                            IS_NULL   => '(unset)',
1771                            );
1772                    assert(isset($type2cast[$type]));
1773                    $cast = $type2cast[$type];
1774                    $resvar = $cast . ' ' . $this->getOpVal($op1, $EX);
1775                    break;
1776                    // }}}
1777                case XC_EXT_STMT:
1778                case XC_EXT_FCALL_BEGIN:
1779                case XC_EXT_FCALL_END:
1780                case XC_EXT_NOP:
1781                    break;
1782                case XC_DECLARE_FUNCTION:
1783                    $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
1784                    break;
1785                case XC_DECLARE_LAMBDA_FUNCTION: // {{{
1786                    ob_start();
1787                    $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
1788                    $resvar = ob_get_clean();
1789                    $istmpres = true;
1790                    break;
1791                    // }}}
1792                case XC_DECLARE_CONST:
1793                    $name = $this->stripNamespace(unquoteName($this->getOpVal($op1, $EX), $EX));
1794                    $value = str($this->getOpVal($op2, $EX));
1795                    $resvar = 'const ' . $name . ' = ' . $value;
1796                    break;
1797                case XC_DECLARE_FUNCTION_OR_CLASS:
1798                    /* always removed by compiler */
1799                    break;
1800                case XC_TICKS:
1801                    $lastphpop['ticks'] = $this->getOpVal($op1, $EX);
1802                    // $EX['tickschanged'] = true;
1803                    break;
1804                case XC_RAISE_ABSTRACT_ERROR:
1805                    // abstract function body is empty, don't need this code
1806                    break;
1807                case XC_USER_OPCODE:
1808                    echo '// ZEND_USER_OPCODE, impossible to decompile';
1809                    break;
1810                case XC_OP_DATA:
1811                    break;
1812                default: // {{{
1813                    echo "\x1B[31m * TODO ", $opname, "\x1B[0m\n";
1814                    $covered = false;
1815                    // }}}
1816                }
1817                if ($covered) {
1818                    $this->usedOps[$opc] = true;
1819                }
1820            }
1821            if (isset($resvar)) {
1822                if ($istmpres) {
1823                    $T[$res['var']] = $resvar;
1824                    $lastresvar = &$T[$res['var']];
1825                }
1826                else {
1827                    $op['php'] = $resvar;
1828                    $lastphpop = &$op;
1829                    $lastresvar = &$op['php'];
1830                }
1831            }
1832        }
1833        return $T;
1834    }
1835    // }}}
1836    function unquote($str, $st, $ed) // {{{
1837    {
1838        $l1 = strlen($st);
1839        $l2 = strlen($ed);
1840        if (substr($str, 0, $l1) === $st && substr($str, -$l2) === $ed) {
1841            $str = substr($str, $l1, -$l2);
1842        }
1843        return $str;
1844    }
1845    // }}}
1846    function popargs(&$EX, $n) // {{{
1847    {
1848        $args = array();
1849        for ($i = 0; $i < $n; $i ++) {
1850            $a = array_pop($EX['argstack']);
1851            if (is_array($a)) {
1852                array_unshift($args, foldToCode($a, $EX));
1853            }
1854            else {
1855                array_unshift($args, $a);
1856            }
1857        }
1858        return implode(', ', $args);
1859    }
1860    // }}}
1861    function dumpop($op, &$EX) // {{{
1862    {
1863        $op1 = $op['op1'];
1864        $op2 = $op['op2'];
1865        $d = array(xcache_get_opcode($op['opcode']), $op['opcode']);
1866
1867        foreach (array('op1' => '1:', 'op2' => '2:', 'result' => '>') as $k => $kk) {
1868            switch ($op[$k]['op_type']) {
1869            case XC_IS_UNUSED:
1870                $d[$kk] = 'U:' . $op[$k]['opline_num'];
1871                break;
1872
1873            case XC_IS_VAR:
1874                $d[$kk] = '$' . $op[$k]['var'];
1875                if ($k != 'result') {
1876                    $d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
1877                }
1878                break;
1879
1880            case XC_IS_TMP_VAR:
1881                $d[$kk] = '#' . $op[$k]['var'];
1882                if ($k != 'result') {
1883                    $d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
1884                }
1885                break;
1886
1887            case XC_IS_CV:
1888                $d[$kk] = $this->getOpVal($op[$k], $EX);
1889                break;
1890
1891            default:
1892                if ($k == 'result') {
1893                    var_dump($op);
1894                    exit;
1895                    assert(0);
1896                }
1897                else {
1898                    $d[$kk] = $this->getOpVal($op[$k], $EX);
1899                }
1900            }
1901        }
1902        $d[';'] = $op['extended_value'];
1903
1904        foreach ($d as $k => $v) {
1905            echo is_int($k) ? '' : $k, str($v), "\t";
1906        }
1907        echo PHP_EOL;
1908    }
1909    // }}}
1910    function dumpRange(&$EX, $first, $last) // {{{
1911    {
1912        for ($i = $first; $i <= $last; ++$i) {
1913            echo $i, "\t"; $this->dumpop($EX['opcodes'][$i], $EX);
1914        }
1915        echo "==", PHP_EOL;
1916    }
1917    // }}}
1918    function dargs(&$EX, $indent) // {{{
1919    {
1920        $op_array = &$EX['op_array'];
1921
1922        if (isset($op_array['num_args'])) {
1923            $c = $op_array['num_args'];
1924        }
1925        else if ($op_array['arg_types']) {
1926            $c = count($op_array['arg_types']);
1927        }
1928        else {
1929            // php4
1930            $c = count($EX['recvs']);
1931        }
1932
1933        $refrest = false;
1934        for ($i = 0; $i < $c; $i ++) {
1935            if ($i) {
1936                echo ', ';
1937            }
1938            $arg = $EX['recvs'][$i + 1];
1939            if (isset($op_array['arg_info'])) {
1940                $ai = $op_array['arg_info'][$i];
1941                if (!empty($ai['class_name'])) {
1942                    echo $this->stripNamespace($ai['class_name']), ' ';
1943                    if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
1944                        echo 'or NULL ';
1945                    }
1946                }
1947                else if (!empty($ai['array_type_hint'])) {
1948                    echo 'array ';
1949                    if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
1950                        echo 'or NULL ';
1951                    }
1952                }
1953                if ($ai['pass_by_reference']) {
1954                    echo '&';
1955                }
1956                printf("\$%s", $ai['name']);
1957            }
1958            else {
1959                if ($refrest) {
1960                    echo '&';
1961                }
1962                else if (isset($op_array['arg_types'][$i])) {
1963                    switch ($op_array['arg_types'][$i]) {
1964                    case BYREF_FORCE_REST:
1965                        $refrest = true;
1966                        /* fall */
1967                    case BYREF_FORCE:
1968                        echo '&';
1969                        break;
1970
1971                    case BYREF_NONE:
1972                    case BYREF_ALLOW:
1973                        break;
1974                    default:
1975                        assert(0);
1976                    }
1977                }
1978                echo str($arg[0], $indent);
1979            }
1980            if (isset($arg[1])) {
1981                echo ' = ', str($arg[1], $indent);
1982            }
1983        }
1984    }
1985    // }}}
1986    function duses(&$EX, $indent) // {{{
1987    {
1988        if ($EX['uses']) {
1989            echo ' use(', implode(', ', $EX['uses']), ')';
1990        }
1991    }
1992    // }}}
1993    function dfunction($func, $indent = '', $nobody = false) // {{{
1994    {
1995        $this->detectNamespace($func['op_array']['function_name']);
1996
1997        if ($nobody) {
1998            $EX = array();
1999            $EX['op_array'] = &$func['op_array'];
2000            $EX['recvs'] = array();
2001            $EX['uses'] = array();
2002        }
2003        else {
2004            ob_start();
2005            $newindent = INDENT . $indent;
2006            $EX = &$this->dop_array($func['op_array'], $newindent);
2007            $body = ob_get_clean();
2008        }
2009
2010        $functionName = $this->stripNamespace($func['op_array']['function_name']);
2011        if ($functionName == '{closure}') {
2012            $functionName = '';
2013        }
2014        echo 'function', $functionName ? ' ' . $functionName : '', '(';
2015        $this->dargs($EX, $indent);
2016        echo ")";
2017        $this->duses($EX, $indent);
2018        if ($nobody) {
2019            echo ";\n";
2020        }
2021        else {
2022            if ($functionName !== '') {
2023                echo "\n";
2024                echo $indent, "{\n";
2025            }
2026            else {
2027                echo " {\n";
2028            }
2029
2030            echo $body;
2031            echo "$indent}";
2032            if ($functionName !== '') {
2033                echo "\n";
2034            }
2035        }
2036    }
2037    // }}}
2038    function dclass($class, $indent = '') // {{{
2039    {
2040        $this->detectNamespace($class['name']);
2041
2042        // {{{ class decl
2043        if (!empty($class['doc_comment'])) {
2044            echo $indent;
2045            echo $class['doc_comment'];
2046            echo "\n";
2047        }
2048        $isinterface = false;
2049        if (!empty($class['ce_flags'])) {
2050            if ($class['ce_flags'] & ZEND_ACC_INTERFACE) {
2051                $isinterface = true;
2052            }
2053            else {
2054                if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
2055                    echo "abstract ";
2056                }
2057                if ($class['ce_flags'] & ZEND_ACC_FINAL_CLASS) {
2058                    echo "final ";
2059                }
2060            }
2061        }
2062        echo $isinterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']);
2063        if ($class['parent']) {
2064            echo ' extends ', $class['parent'];
2065        }
2066        /* TODO */
2067        if (!empty($class['interfaces'])) {
2068            echo ' implements ';
2069            echo implode(', ', $class['interfaces']);
2070        }
2071        echo "\n";
2072        echo $indent, "{";
2073        // }}}
2074        $newindent = INDENT . $indent;
2075        // {{{ const, static
2076        foreach (array('constants_table' => 'const '
2077                    , 'static_members' => 'static $') as $type => $prefix) {
2078            if (!empty($class[$type])) {
2079                echo "\n";
2080                // TODO: skip shadow?
2081                foreach ($class[$type] as $name => $v) {
2082                    echo $newindent;
2083                    echo $prefix, $name, ' = ';
2084                    echo str(value($v), $newindent);
2085                    echo ";\n";
2086                }
2087            }
2088        }
2089        // }}}
2090        // {{{ properties
2091        $member_variables = isset($class['properties_info']) ? $class['properties_info'] : ($class['default_static_members'] + $class['default_properties']);
2092        if ($member_variables) {
2093            echo "\n";
2094            $infos = !empty($class['properties_info']) ? $class['properties_info'] : null;
2095            foreach ($member_variables as $name => $dummy) {
2096                $info = (isset($infos) && isset($infos[$name])) ? $infos[$name] : null;
2097                if (isset($info)) {
2098                    if (!empty($info['doc_comment'])) {
2099                        echo $newindent;
2100                        echo $info['doc_comment'];
2101                        echo "\n";
2102                    }
2103                }
2104
2105                echo $newindent;
2106                $static = false;
2107                if (isset($info)) {
2108                    if ($info['flags'] & ZEND_ACC_STATIC) {
2109                        $static = true;
2110                    }
2111                }
2112                else if (isset($class['default_static_members'][$name])) {
2113                    $static = true;
2114                }
2115
2116                if ($static) {
2117                    echo "static ";
2118                }
2119
2120                $mangled = false;
2121                if (!ZEND_ENGINE_2) {
2122                    echo 'var ';
2123                }
2124                else if (!isset($info)) {
2125                    echo 'public ';
2126                }
2127                else {
2128                    if ($info['flags'] & ZEND_ACC_SHADOW) {
2129                        continue;
2130                    }
2131                    switch ($info['flags'] & ZEND_ACC_PPP_MASK) {
2132                    case ZEND_ACC_PUBLIC:
2133                        echo "public ";
2134                        break;
2135                    case ZEND_ACC_PRIVATE:
2136                        echo "private ";
2137                        $mangled = true;
2138                        break;
2139                    case ZEND_ACC_PROTECTED:
2140                        echo "protected ";
2141                        $mangled = true;
2142                        break;
2143                    }
2144                }
2145
2146                echo '$', $name;
2147
2148                if (isset($info['offset'])) {
2149                    $value = $class[$static ? 'default_static_members_table' : 'default_properties_table'][$info['offset']];
2150                }
2151                else {
2152                    $key = isset($info) ? $info['name'] . ($mangled ? "\000" : "") : $name;
2153
2154                    $value = $class[$static ? 'default_static_members' : 'default_properties'][$key];
2155                }
2156                if (isset($value)) {
2157                    echo ' = ';
2158                    echo str(value($value), $newindent);
2159                }
2160                echo ";\n";
2161            }
2162        }
2163        // }}}
2164        // {{{ function_table
2165        if (isset($class['function_table'])) {
2166            foreach ($class['function_table'] as $func) {
2167                if (!isset($func['scope']) || $func['scope'] == $class['name']) {
2168                    // TODO: skip shadow here
2169                    echo "\n";
2170                    $opa = $func['op_array'];
2171                    if (!empty($opa['doc_comment'])) {
2172                        echo $newindent;
2173                        echo $opa['doc_comment'];
2174                        echo "\n";
2175                    }
2176                    echo $newindent;
2177                    $isAbstractMethod = false;
2178                    if (isset($opa['fn_flags'])) {
2179                        if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isinterface) {
2180                            echo "abstract ";
2181                            $isAbstractMethod = true;
2182                        }
2183                        if ($opa['fn_flags'] & ZEND_ACC_FINAL) {
2184                            echo "final ";
2185                        }
2186                        if ($opa['fn_flags'] & ZEND_ACC_STATIC) {
2187                            echo "static ";
2188                        }
2189
2190                        switch ($opa['fn_flags'] & ZEND_ACC_PPP_MASK) {
2191                            case ZEND_ACC_PUBLIC:
2192                                echo "public ";
2193                                break;
2194                            case ZEND_ACC_PRIVATE:
2195                                echo "private ";
2196                                break;
2197                            case ZEND_ACC_PROTECTED:
2198                                echo "protected ";
2199                                break;
2200                            default:
2201                                echo "<visibility error> ";
2202                                break;
2203                        }
2204                    }
2205                    $this->dfunction($func, $newindent, $isinterface || $isAbstractMethod);
2206                    if ($opa['function_name'] == 'Decompiler') {
2207                        //exit;
2208                    }
2209                }
2210            }
2211        }
2212        // }}}
2213        echo $indent, "}\n";
2214    }
2215    // }}}
2216    function decompileString($string) // {{{
2217    {
2218        $this->dc = xcache_dasm_string($string);
2219        if ($this->dc === false) {
2220            echo "error compling string\n";
2221            return false;
2222        }
2223    }
2224    // }}}
2225    function decompileFile($file) // {{{
2226    {
2227        $this->dc = xcache_dasm_file($file);
2228        if ($this->dc === false) {
2229            echo "error compling $file\n";
2230            return false;
2231        }
2232    }
2233    // }}}
2234    function output() // {{{
2235    {
2236        echo "<?". "php\n\n";
2237        foreach ($this->dc['class_table'] as $key => $class) {
2238            if ($key{0} != "\0") {
2239                $this->dclass($class);
2240                echo "\n";
2241            }
2242        }
2243
2244        foreach ($this->dc['function_table'] as $key => $func) {
2245            if ($key{0} != "\0") {
2246                $this->dfunction($func);
2247                echo "\n";
2248            }
2249        }
2250
2251        $this->dop_array($this->dc['op_array']);
2252        echo "\n?" . ">\n";
2253
2254        if (!empty($this->test)) {
2255            $this->outputUnusedOp();
2256        }
2257        return true;
2258    }
2259    // }}}
2260    function outputUnusedOp() // {{{
2261    {
2262        for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
2263            if ($opname == 'UNDEF')  {
2264                continue;
2265            }
2266
2267            if (!isset($this->usedOps[$i])) {
2268                echo "not covered opcode ", $opname, "\n";
2269            }
2270        }
2271    }
2272    // }}}
2273}
2274
2275// {{{ defines
2276define('ZEND_ENGINE_2_4', PHP_VERSION >= "5.3.99");
2277define('ZEND_ENGINE_2_3', ZEND_ENGINE_2_4 || PHP_VERSION >= "5.3.");
2278define('ZEND_ENGINE_2_2', ZEND_ENGINE_2_3 || PHP_VERSION >= "5.2.");
2279define('ZEND_ENGINE_2_1', ZEND_ENGINE_2_2 || PHP_VERSION >= "5.1.");
2280define('ZEND_ENGINE_2',   ZEND_ENGINE_2_1 || PHP_VERSION >= "5.0.");
2281
2282define('ZEND_ACC_STATIC',         0x01);
2283define('ZEND_ACC_ABSTRACT',       0x02);
2284define('ZEND_ACC_FINAL',          0x04);
2285define('ZEND_ACC_IMPLEMENTED_ABSTRACT',       0x08);
2286
2287define('ZEND_ACC_IMPLICIT_ABSTRACT_CLASS',    0x10);
2288define('ZEND_ACC_EXPLICIT_ABSTRACT_CLASS',    0x20);
2289define('ZEND_ACC_FINAL_CLASS',                0x40);
2290define('ZEND_ACC_INTERFACE',                  0x80);
2291if (ZEND_ENGINE_2_4) {
2292    define('ZEND_ACC_TRAIT',                  0x120);
2293}
2294define('ZEND_ACC_PUBLIC',     0x100);
2295define('ZEND_ACC_PROTECTED',  0x200);
2296define('ZEND_ACC_PRIVATE',    0x400);
2297define('ZEND_ACC_PPP_MASK',  (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE));
2298
2299define('ZEND_ACC_CHANGED',    0x800);
2300define('ZEND_ACC_IMPLICIT_PUBLIC',    0x1000);
2301
2302define('ZEND_ACC_CTOR',       0x2000);
2303define('ZEND_ACC_DTOR',       0x4000);
2304define('ZEND_ACC_CLONE',      0x8000);
2305
2306define('ZEND_ACC_ALLOW_STATIC',   0x10000);
2307
2308define('ZEND_ACC_SHADOW', 0x2000);
2309
2310if (ZEND_ENGINE_2_4) {
2311    define('ZEND_FETCH_GLOBAL',           0x00000000);
2312    define('ZEND_FETCH_LOCAL',            0x10000000);
2313    define('ZEND_FETCH_STATIC',           0x20000000);
2314    define('ZEND_FETCH_STATIC_MEMBER',    0x30000000);
2315    define('ZEND_FETCH_GLOBAL_LOCK',      0x40000000);
2316    define('ZEND_FETCH_LEXICAL',          0x50000000);
2317
2318    define('ZEND_FETCH_TYPE_MASK',        0x70000000);
2319}
2320else {
2321    define('ZEND_FETCH_GLOBAL',           0);
2322    define('ZEND_FETCH_LOCAL',            1);
2323    define('ZEND_FETCH_STATIC',           2);
2324    define('ZEND_FETCH_STATIC_MEMBER',    3);
2325    define('ZEND_FETCH_GLOBAL_LOCK',      4);
2326}
2327
2328define('ZEND_FETCH_CLASS_DEFAULT',    0);
2329define('ZEND_FETCH_CLASS_SELF',       1);
2330define('ZEND_FETCH_CLASS_PARENT',     2);
2331define('ZEND_FETCH_CLASS_MAIN',       3);
2332define('ZEND_FETCH_CLASS_GLOBAL',     4);
2333define('ZEND_FETCH_CLASS_AUTO',       5);
2334define('ZEND_FETCH_CLASS_INTERFACE',  6);
2335define('ZEND_FETCH_CLASS_STATIC',     7);
2336if (ZEND_ENGINE_2_4) {
2337    define('ZEND_FETCH_CLASS_TRAIT',     14);
2338}
2339if (ZEND_ENGINE_2_3) {
2340    define('ZEND_FETCH_CLASS_MASK',     0xF);
2341}
2342
2343define('ZEND_EVAL',               (1<<0));
2344define('ZEND_INCLUDE',            (1<<1));
2345define('ZEND_INCLUDE_ONCE',       (1<<2));
2346define('ZEND_REQUIRE',            (1<<3));
2347define('ZEND_REQUIRE_ONCE',       (1<<4));
2348
2349define('ZEND_ISSET',              (1<<0));
2350define('ZEND_ISEMPTY',            (1<<1));
2351if (ZEND_ENGINE_2_4) {
2352    define('EXT_TYPE_UNUSED',     (1<<5));
2353}
2354else {
2355    define('EXT_TYPE_UNUSED',     (1<<0));
2356}
2357
2358define('ZEND_FETCH_STANDARD',     0);
2359define('ZEND_FETCH_ADD_LOCK',     1);
2360
2361define('ZEND_FE_FETCH_BYREF',     1);
2362define('ZEND_FE_FETCH_WITH_KEY',  2);
2363
2364define('ZEND_MEMBER_FUNC_CALL',   1<<0);
2365define('ZEND_CTOR_CALL',          1<<1);
2366
2367define('ZEND_ARG_SEND_BY_REF',        (1<<0));
2368define('ZEND_ARG_COMPILE_TIME_BOUND', (1<<1));
2369define('ZEND_ARG_SEND_FUNCTION',      (1<<2));
2370
2371define('BYREF_NONE',       0);
2372define('BYREF_FORCE',      1);
2373define('BYREF_ALLOW',      2);
2374define('BYREF_FORCE_REST', 3);
2375define('IS_NULL',     0);
2376define('IS_LONG',     1);
2377define('IS_DOUBLE',   2);
2378define('IS_BOOL',     ZEND_ENGINE_2 ? 3 : 6);
2379define('IS_ARRAY',    4);
2380define('IS_OBJECT',   5);
2381define('IS_STRING',   ZEND_ENGINE_2 ? 6 : 3);
2382define('IS_RESOURCE', 7);
2383define('IS_CONSTANT', 8);
2384define('IS_CONSTANT_ARRAY',   9);
2385/* Ugly hack to support constants as static array indices */
2386define('IS_CONSTANT_TYPE_MASK',   0x0f);
2387define('IS_CONSTANT_UNQUALIFIED', 0x10);
2388define('IS_CONSTANT_INDEX',       0x80);
2389define('IS_LEXICAL_VAR',          0x20);
2390define('IS_LEXICAL_REF',          0x40);
2391
2392@define('XC_IS_CV', 16);
2393
2394/*
2395if (preg_match_all('!XC_[A-Z_]+!', file_get_contents(__FILE__), $ms)) {
2396    $verdiff = array();
2397    foreach ($ms[0] as $k) {
2398        if (!defined($k)) {
2399            $verdiff[$k] = -1;
2400            define($k, -1);
2401        }
2402    }
2403    var_export($verdiff);
2404}
2405/*/
2406foreach (array (
2407    'XC_HANDLE_EXCEPTION' => -1,
2408    'XC_FETCH_CLASS' => -1,
2409    'XC_FETCH_' => -1,
2410    'XC_FETCH_DIM_' => -1,
2411    'XC_ASSIGN_DIM' => -1,
2412    'XC_UNSET_DIM' => -1,
2413    'XC_UNSET_OBJ' => -1,
2414    'XC_ASSIGN_OBJ' => -1,
2415    'XC_ISSET_ISEMPTY_DIM_OBJ' => -1,
2416    'XC_ISSET_ISEMPTY_PROP_OBJ' => -1,
2417    'XC_ISSET_ISEMPTY_VAR' => -1,
2418    'XC_INIT_STATIC_METHOD_CALL' => -1,
2419    'XC_INIT_METHOD_CALL' => -1,
2420    'XC_VERIFY_ABSTRACT_CLASS' => -1,
2421    'XC_DECLARE_CLASS' => -1,
2422    'XC_DECLARE_INHERITED_CLASS' => -1,
2423    'XC_DECLARE_INHERITED_CLASS_DELAYED' => -1,
2424    'XC_ADD_INTERFACE' => -1,
2425    'XC_POST_DEC_OBJ' => -1,
2426    'XC_POST_INC_OBJ' => -1,
2427    'XC_PRE_DEC_OBJ' => -1,
2428    'XC_PRE_INC_OBJ' => -1,
2429    'XC_UNSET_OBJ' => -1,
2430    'XC_JMP_NO_CTOR' => -1,
2431    'XC_FETCH_' => -1,
2432    'XC_FETCH_DIM_' => -1,
2433    'XC_UNSET_DIM_OBJ' => -1,
2434    'XC_ISSET_ISEMPTY' => -1,
2435    'XC_INIT_FCALL_BY_FUNC' => -1,
2436    'XC_DO_FCALL_BY_FUNC' => -1,
2437    'XC_DECLARE_FUNCTION_OR_CLASS' => -1,
2438    'XC_INIT_NS_FCALL_BY_NAME' => -1,
2439    'XC_GOTO' => -1,
2440    'XC_CATCH' => -1,
2441    'XC_THROW' => -1,
2442    'XC_INSTANCEOF' => -1,
2443    'XC_DECLARE_FUNCTION' => -1,
2444    'XC_RAISE_ABSTRACT_ERROR' => -1,
2445    'XC_DECLARE_CONST' => -1,
2446    'XC_USER_OPCODE' => -1,
2447    'XC_JMP_SET' => -1,
2448    'XC_DECLARE_LAMBDA_FUNCTION' => -1,
2449) as $k => $v) {
2450    if (!defined($k)) {
2451        define($k, $v);
2452    }
2453}
2454// }}}
2455
Note: See TracBrowser for help on using the repository browser.