source: trunk/Decompiler.class.php @ 789

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

Decompile: implement foreach, improve blank line

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