source: trunk/Decompiler.class.php @ 789

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

Decompile: implement foreach, improve blank line

  • Property svn:eol-style set to native
File size: 61.2 KB
RevLine 
[1]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
[737]11function str($code, $indent = '') // {{{
12{
[744]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    }
[737]20    if (is_object($code)) {
[744]21        $code = foldToCode($code, $indent);
22        return $code->toCode($indent);
[737]23    }
24
25    return (string) $code;
26}
27// }}}
[744]28function foldToCode($src, $indent = '') // {{{ wrap or rewrap anything to Decompiler_Code
[1]29{
30    if (is_array($indent)) {
31        $indent = $indent['indent'];
32    }
33
[741]34    if (!is_object($src)) {
35        return new Decompiler_Code($src);
[1]36    }
37
[741]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;
[1]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
[737]61    if (is_a($value, 'Decompiler_Object')) {
[723]62        // use as is
63    }
64    else if (is_array($value)) {
[737]65        $value = new Decompiler_ConstArray($value);
[1]66    }
67    else {
[713]68        $value = new Decompiler_Value($value);
[1]69    }
70    return $value;
71}
72// }}}
[761]73function unquoteName_($str, $asVariableName, $indent = '') // {{{
[749]74{
75    $str = str($str, $indent);
[753]76    if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
77        return str_replace('\\\\', '\\', substr($str, 1, -1));
[749]78    }
[761]79    else if ($asVariableName) {
[749]80        return "{" . $str . "}";
81    }
[753]82    else {
83        return $str;
84    }
[749]85}
86// }}}
[761]87function unquoteVariableName($str, $indent = '') // {{{
[753]88{
89    return unquoteName_($str, true, $indent);
90}
91// }}}
[749]92function unquoteName($str, $indent = '') // {{{
93{
[753]94    return unquoteName_($str, false, $indent);
[749]95}
96// }}}
[1]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
[732]110    function toCode($indent)
[1]111    {
[750]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;
[1]127    }
128}
129// }}}
130class Decompiler_Code extends Decompiler_Object // {{{
131{
132    var $src;
133
134    function Decompiler_Code($src)
135    {
[743]136        assert('isset($src)');
[1]137        $this->src = $src;
138    }
139
[732]140    function toCode($indent)
[1]141    {
[741]142        return $this->src;
[737]143    }
[1]144}
145// }}}
146class Decompiler_Binop extends Decompiler_Code // {{{
147{
148    var $opc;
149    var $op1;
150    var $op2;
151    var $parent;
[732]152    var $indent;
[1]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
[732]162    function toCode($indent)
[1]163    {
[757]164        $opstr = $this->parent->binops[$this->opc];
165
[744]166        $op1 = foldToCode($this->op1, $indent);
[1]167        if (is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) {
[757]168            $op1 = "(" . str($op1, $indent) . ")";
[1]169        }
[757]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) . ")";
[1]173        }
[757]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);
[1]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
[732]195    function toCode($indent)
[1]196    {
197        switch ($this->fetchType) {
198        case ZEND_FETCH_LOCAL:
199            return '$' . substr($this->src, 1, -1);
200        case ZEND_FETCH_STATIC:
[749]201            if (ZEND_ENGINE_2_3) {
202                // closure local variable?
203                return str($this->src);
204            }
[1]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
[732]225    function toCode($indent)
[1]226    {
[732]227        return $this->obj->toCode($indent);
[1]228    }
229}
230// }}}
231class Decompiler_Dim extends Decompiler_Value // {{{
232{
233    var $offsets = array();
234    var $isLast = false;
[749]235    var $isObject = false;
[1]236    var $assign = null;
237
[732]238    function toCode($indent)
[1]239    {
240        if (is_a($this->value, 'Decompiler_ListBox')) {
[749]241            $exp = str($this->value->obj->src, $indent);
[1]242        }
243        else {
[749]244            $exp = str($this->value, $indent);
[1]245        }
[749]246        $last = count($this->offsets) - 1;
247        foreach ($this->offsets as $i => $dim) {
248            if ($this->isObject && $i == $last) {
[761]249                $exp .= '->' . unquoteVariableName($dim, $indent);
[749]250            }
251            else {
252                $exp .= '[' . str($dim, $indent) . ']';
253            }
[1]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
[732]269    function toCode($indent)
[1]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)) {
[754]276                return str($dim, $indent);
[1]277            }
[754]278            return str($this->dims[0]->assign, $indent) . ' = ' . str($dim, $indent);
[1]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            }
[744]287            $assign = foldToCode($dim->assign, $indent);
[1]288        }
[754]289        return str($this->toList($assigns)) . ' = ' . str($this->src, $indent);
[1]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{
[737]324    // emenets
325    function Decompiler_Array()
[1]326    {
[737]327        $this->value = array();
[1]328    }
329
[732]330    function toCode($indent)
[1]331    {
[737]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
[1]344        $exp = "array(";
[732]345        $indent = $indent . INDENT;
[735]346        $assocWidth = 0;
[1]347        $multiline = 0;
348        $i = 0;
[737]349        foreach ($elementsCode as $element) {
350            list($keyCode, $valueCode) = $element;
351            if ((string) $i !== $keyCode) {
[735]352                $assocWidth = 1;
[737]353                break;
[735]354            }
355            ++$i;
356        }
[737]357        foreach ($elementsCode as $element) {
358            list($keyCode, $valueCode, $key, $value) = $element;
[735]359            if ($assocWidth) {
[737]360                $len = strlen($keyCode);
[735]361                if ($assocWidth < $len) {
362                    $assocWidth = $len;
[1]363                }
364            }
[737]365            if (is_array($value) || is_a($value, 'Decompiler_Array')) {
[1]366                $multiline ++;
367            }
368        }
369
370        $i = 0;
[737]371        foreach ($elementsCode as $element) {
372            list($keyCode, $value) = $element;
[1]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
[735]386            if ($assocWidth) {
387                if ($multiline) {
[737]388                    $exp .= sprintf("%-{$assocWidth}s => ", $keyCode);
[735]389                }
390                else {
[737]391                    $exp .= $keyCode . ' => ';
[735]392                }
[1]393            }
394
[737]395            $exp .= $value;
[1]396
397            $i ++;
398        }
399        if ($multiline) {
[732]400            $exp .= "\n$indent)";
[1]401        }
402        else {
403            $exp .= ")";
404        }
405        return $exp;
406    }
407}
408// }}}
[737]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// }}}
[1]421class Decompiler_ForeachBox extends Decompiler_Box // {{{
422{
423    var $iskey;
424
[732]425    function toCode($indent)
[1]426    {
427        return 'foreach (' . '';
428    }
429}
430// }}}
431
432class Decompiler
433{
[753]434    var $namespace;
435    var $namespaceDecided;
[1]436
437    function Decompiler()
438    {
[761]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        // }}}
[1]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    }
[753]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    // }}}
[787]524    function outputPhp(&$EX, $opline, $last, $indent) // {{{
[1]525    {
[789]526        $needBlankline = isset($EX['lastBlock']);
[1]527        $origindent = $indent;
528        $curticks = 0;
529        for ($i = $opline; $i <= $last; $i ++) {
[787]530            $op = $EX['opcodes'][$i];
531            if (isset($op['gofrom'])) {
[789]532                if ($needBlankline) {
533                    $needBlankline = false;
534                    echo PHP_EOL;
535                }
[787]536                echo 'label' . $i, ":\n";
537            }
[1]538            if (isset($op['php'])) {
[746]539                $toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0;
[1]540                if ($curticks != $toticks) {
[746]541                    $oldticks = $curticks;
542                    $curticks = $toticks;
543                    if (!$curticks) {
[749]544                        echo $origindent, "}\n\n";
[1]545                        $indent = $origindent;
546                    }
547                    else {
[746]548                        if ($oldticks) {
[749]549                            echo $origindent, "}\n\n";
[1]550                        }
[746]551                        else if (!$oldticks) {
[1]552                            $indent .= INDENT;
553                        }
[789]554                        if ($needBlankline) {
555                            $needBlankline = false;
556                            echo PHP_EOL;
557                        }
[746]558                        echo $origindent, "declare (ticks=$curticks) {\n";
[1]559                    }
560                }
[789]561                if ($needBlankline) {
562                    $needBlankline = false;
563                    echo PHP_EOL;
564                }
[744]565                echo $indent, str($op['php'], $indent), ";\n";
[1]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:
[744]577            return foldToCode(value($op['constant']), $EX);
[1]578
579        case XC_IS_VAR:
580        case XC_IS_TMP_VAR:
581            $T = &$EX['Ts'];
[716]582            $ret = $T[$op['var']];
[1]583            if ($tostr) {
[744]584                $ret = foldToCode($ret, $EX);
[1]585            }
586            if ($free) {
[716]587                unset($T[$op['var']]);
[1]588            }
589            return $ret;
590
591        case XC_IS_CV:
[716]592            $var = $op['var'];
[1]593            $var = $EX['op_array']['vars'][$var];
594            return '$' . $var['name'];
595
596        case XC_IS_UNUSED:
597            return null;
598        }
599    }
600    // }}}
[716]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    // }}}
[735]614    function &fixOpcode($opcodes, $removeTailing = false, $defaultReturnValue = null) // {{{
[1]615    {
[716]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        }
[735]647
648        if ($removeTailing) {
649            $last = count($opcodes) - 1;
650            if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
[761]651                $this->usedOps[XC_HANDLE_EXCEPTION] = true;
[762]652                $opcodes[$last]['opcode'] = XC_NOP;
[735]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) {
[762]658                    $opcodes[$last]['opcode'] = XC_NOP;
[735]659                    --$last;
660                }
661            }
662        }
[731]663        return $opcodes;
664    }
665    // }}}
[787]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        }
[789]682        // $opcodes[$line]['opcode'] = XC_NOP;
[787]683        unset($opcodes[$line]['jmpouts']);
684    }
685    // }}}
[788]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    // }}}
[787]699    function decompileComplexBlock(&$EX, $first, $last, $indent) // {{{
700    {
701        $opcodes = &$EX['opcodes'];
702
703        $firstOp = &$opcodes[$first];
704        $lastOp = &$opcodes[$last];
705
[789]706        if ($lastOp['opcode'] == XC_JMPNZ && !empty($lastOp['jmpouts'])
[787]707         && $lastOp['jmpouts'][0] == $first) {
708            $this->removeJmpInfo($EX, $last);
[788]709            $this->beginComplexBlock($EX);
[789]710
[787]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;
[789]714
[788]715            $this->endComplexBlock($EX);
[787]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
[789]731         && $firstJmpOp['jmpouts'][0] > $last
732         && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts'])
[787]733         && $lastOp['jmpouts'][0] == $first) {
734            $this->removeJmpInfo($EX, $firstJmp);
735            $this->removeJmpInfo($EX, $last);
[789]736            $this->beginComplexBlock($EX);
[787]737
738            ob_start();
739            $this->recognizeAndDecompileClosedBlocks($EX, $first, $last, $indent . INDENT);
[789]740            $body = ob_get_clean();
741
[787]742            echo $indent, 'while (', str($this->getOpVal($firstJmpOp['op1'], $EX)), ') {', PHP_EOL;
[789]743            echo $body;
[787]744            echo $indent, '}', PHP_EOL;
[789]745
[788]746            $this->endComplexBlock($EX);
[787]747            return;
748        }
749
[789]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
[787]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    // }}}
[731]822    function &dop_array($op_array, $indent = '') // {{{
823    {
[735]824        $op_array['opcodes'] = $this->fixOpcode($op_array['opcodes'], true, $indent == '' ? 1 : null);
[731]825        $opcodes = &$op_array['opcodes'];
[787]826        // {{{ build jmpins/jmpouts to op_array
[1]827        for ($i = 0, $cnt = count($opcodes); $i < $cnt; $i ++) {
828            $op = &$opcodes[$i];
829            $op['line'] = $i;
830            switch ($op['opcode']) {
[758]831            case XC_CONT:
832            case XC_BRK:
833                $op['jmpouts'] = array();
834                break;
835
[749]836            case XC_GOTO:
[787]837                $target = $op['op1']['var'];
838                $op['goto'] = $target;
839                $opcodes[$target]['gofrom'][] = $i;
840                break;
841
[1]842            case XC_JMP:
[716]843                $target = $op['op1']['var'];
[1]844                $op['jmpouts'] = array($target);
845                $opcodes[$target]['jmpins'][] = $i;
846                break;
847
848            case XC_JMPZNZ:
[716]849                $jmpz = $op['op2']['opline_num'];
[1]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:
[749]860            case XC_JMP_SET:
[1]861            // case XC_FE_RESET:
862            case XC_FE_FETCH:
863            // case XC_JMP_NO_CTOR:
[716]864                $target = $op['op2']['opline_num'];
[1]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            }
[787]879            /*
880            if (!empty($op['jmpouts']) || !empty($op['jmpins'])) {
881                echo $i, "\t", xcache_get_opcode($op['opcode']), PHP_EOL;
882            }
883            // */
[1]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;
[720]907        $EX['called_scope'] = null;
[1]908        $EX['fbc'] = null;
909        $EX['argstack'] = array();
910        $EX['arg_types_stack'] = array();
911        $EX['last'] = count($opcodes) - 1;
912        $EX['silence'] = 0;
[780]913        $EX['recvs'] = array();
914        $EX['uses'] = array();
[1]915
[787]916        // decompile in a tree way
917        $this->recognizeAndDecompileClosedBlocks($EX, 0, count($opcodes) - 1, $EX['indent']);
[1]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);
[787]938        $this->outputPhp($EX, $opline, $end, $indent);
[1]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) {
[716]956            $target = $op2['opline_num'];
[1]957            if ($line + 1) {
958                $nextblock = $EX['nextbbs'][$next];
959                $jmpop = end($nextblock);
960                if ($jmpop['opcode'] == XC_JMP) {
[716]961                    $ifendline = $op2['opline_num'];
[1]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        }
[758]977        if (isset($op['jmpouts']) && isset($op['isjmp'])) {
[1]978            if (isset($op['cond'])) {
[746]979                echo "{$indent}check (" . str($op["cond"]) . ") {\n";
[1]980                echo INDENT;
981            }
[758]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";
[1]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
[716]1013                $var = $fromop['result']['var'];
[1]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"
[744]1024            $cond = implode(' and ', str($op['cond_false']));
[1]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
[787]1044        for ($i = $opline; $i <= $last; $i ++) {
[1]1045            // {{{ prepair
1046            $op = &$opcodes[$i];
1047            $opc = $op['opcode'];
1048            if ($opc == XC_NOP) {
[761]1049                $this->usedOps[$opc] = true;
[1]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            }
[762]1065            // echo $i, ' '; $this->dumpop($op, $EX); //var_dump($op);
[1]1066
1067            $resvar = null;
[731]1068            if ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) {
[1]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)) {
[761]1079                $this->usedOps[$opc] = true;
[1]1080                $this->{$opname}($op, $EX);
1081            }
1082            else if (isset($this->binops[$opc])) { // {{{
[761]1083                $this->usedOps[$opc] = true;
[1]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])) { // {{{
[761]1091                $this->usedOps[$opc] = true;
[1]1092                $op1val = $this->getOpVal($op1, $EX);
1093                $myop = $this->unaryops[$opc];
[757]1094                $rvalue = $myop . str($op1val);
[1]1095                $resvar = $rvalue;
1096                // }}}
1097            }
1098            else {
[761]1099                $covered = true;
[1]1100                switch ($opc) {
1101                case XC_NEW: // {{{
[720]1102                    array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
[716]1103                    $EX['object'] = (int) $res['var'];
[720]1104                    $EX['called_scope'] = null;
[749]1105                    $EX['fbc'] = 'new ' . unquoteName($this->getOpVal($op1, $EX), $EX);
[731]1106                    if (!ZEND_ENGINE_2) {
[1]1107                        $resvar = '$new object$';
1108                    }
1109                    break;
1110                    // }}}
[749]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                    // }}}
[1]1127                case XC_FETCH_CLASS: // {{{
1128                    if ($op2['op_type'] == XC_IS_UNUSED) {
[731]1129                        switch (($ext & (defined('ZEND_FETCH_CLASS_MASK') ? ZEND_FETCH_CLASS_MASK : 0xFF))) {
[1]1130                        case ZEND_FETCH_CLASS_SELF:
1131                            $class = 'self';
1132                            break;
1133                        case ZEND_FETCH_CLASS_PARENT:
1134                            $class = 'parent';
[722]1135                            break;
1136                        case ZEND_FETCH_CLASS_STATIC:
1137                            $class = 'static';
1138                            break;
[1]1139                        }
[722]1140                        $istmpres = true;
[1]1141                    }
1142                    else {
[749]1143                        $class = $this->getOpVal($op2, $EX);
1144                        if (isset($op2['constant'])) {
[753]1145                            $class = $this->stripNamespace(unquoteName($class));
[1]1146                        }
1147                    }
1148                    $resvar = $class;
1149                    break;
1150                    // }}}
1151                case XC_FETCH_CONSTANT: // {{{
[749]1152                    if ($op1['op_type'] == XC_IS_UNUSED) {
[753]1153                        $resvar = $this->stripNamespace($op2['constant']);
[749]1154                        break;
1155                    }
1156
[1]1157                    if ($op1['op_type'] == XC_IS_CONST) {
[753]1158                        $resvar = $this->stripNamespace($op1['constant']);
[1]1159                    }
1160                    else {
[749]1161                        $resvar = $this->getOpVal($op1, $EX);
[1]1162                    }
[749]1163
1164                    $resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2, $EX));
[1]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);
[731]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                    }
[1]1182                    switch ($fetchtype) {
1183                    case ZEND_FETCH_STATIC_MEMBER:
1184                        $class = $this->getOpVal($op2, $EX);
[749]1185                        $rvalue = str($class) . '::$' . unquoteName($rvalue, $EX);
[1]1186                        break;
1187                    default:
[749]1188                        $name = unquoteName($rvalue, $EX);
1189                        $globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($rvalue) . "]";
[1]1190                        $rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname);
1191                        break;
1192                    }
1193                    if ($opc == XC_UNSET_VAR) {
[744]1194                        $op['php'] = "unset(" . str($rvalue, $EX) . ")";
[1]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:
[749]1211                case XC_UNSET_DIM_OBJ: // PHP 4 only
[1]1212                case XC_UNSET_DIM:
[749]1213                case XC_UNSET_OBJ:
[1]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                    }
[749]1220
1221                    if (is_a($src, "Decompiler_DimBox")) {
[1]1222                        $dimbox = $src;
1223                    }
1224                    else {
1225                        if (!is_a($src, "Decompiler_ListBox")) {
[749]1226                            $op1val = $this->getOpVal($op1, $EX, false);
1227                            $list = new Decompiler_List(isset($op1val) ? $op1val : '$this');
[1]1228
1229                            $src = new Decompiler_ListBox($list);
[716]1230                            if (!isset($op1['var'])) {
[1]1231                                $this->dumpop($op, $EX);
1232                                var_dump($op);
[716]1233                                die('missing var');
[1]1234                            }
[716]1235                            $T[$op1['var']] = $src;
[1]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                    }
[749]1251                    if ($opc == XC_UNSET_OBJ) {
1252                        $dim->isObject = true;
1253                    }
[1]1254                    unset($dim);
1255                    $rvalue = $dimbox;
[749]1256                    unset($dimbox);
[1]1257
1258                    if ($opc == XC_ASSIGN_DIM) {
1259                        $lvalue = $rvalue;
1260                        ++ $i;
1261                        $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
[749]1262                        $resvar = str($lvalue, $EX) . ' = ' . str($rvalue);
[1]1263                    }
[749]1264                    else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ) {
[744]1265                        $op['php'] = "unset(" . str($rvalue, $EX) . ")";
[1]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;
[716]1279                        unset($T[$op2['var']]);
[1]1280                        break;
1281                    }
1282                    if (is_a($rvalue, "Decompiler_DimBox")) {
1283                        $dim = &$rvalue->obj;
1284                        $dim->assign = $lvalue;
1285                        if ($dim->isLast) {
[744]1286                            $resvar = foldToCode($dim->value, $EX);
[1]1287                        }
1288                        unset($dim);
1289                        break;
1290                    }
[781]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                    }
[744]1306                    $resvar = "$lvalue = " . str($rvalue, $EX);
[1]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')) {
[749]1313                        $src = str($rvalue->src, $EX);
[754]1314                        if ('$' . unquoteName($src) == $lvalue) {
[1]1315                            switch ($rvalue->fetchType) {
1316                            case ZEND_FETCH_GLOBAL:
[731]1317                            case ZEND_FETCH_GLOBAL_LOCK:
[1]1318                                $resvar = 'global ' . $lvalue;
1319                                break 2;
1320                            case ZEND_FETCH_STATIC:
1321                                $statics = &$EX['op_array']['static_variables'];
[781]1322                                if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) {
1323                                    $EX['uses'][] = '&' . str($lvalue);
1324                                    unset($statics);
1325                                    break 2;
1326                                }
1327
[1]1328                                $resvar = 'static ' . $lvalue;
[754]1329                                $name = unquoteName($src);
[1]1330                                if (isset($statics[$name])) {
1331                                    $var = $statics[$name];
1332                                    $resvar .= ' = ';
[744]1333                                    $resvar .= str(value($var), $EX);
[1]1334                                }
1335                                unset($statics);
1336                                break 2;
[731]1337                            default:
[1]1338                            }
1339                        }
1340                    }
[731]1341                    // TODO: PHP_6 global
[754]1342                    $rvalue = str($rvalue, $EX);
[1]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                    }
[761]1358                    $rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
[1]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);
[749]1366                        $resvar = "$lvalue = " . str($rvalue);
[1]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) {
[777]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                        }
[716]1380                        if ($op2['EA.type'] == ZEND_FETCH_STATIC_MEMBER) {
[1]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);
[717]1391                        if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) {
[761]1392                            if (!isset($container)) {
1393                                $container = '$this';
[717]1394                            }
[761]1395                            $rvalue = $container . "->" . unquoteVariableName($dim);
[717]1396                        }
1397                        else {
[761]1398                            $rvalue = $container . '[' . str($dim) .']';
[717]1399                        }
[1]1400                    }
1401
[731]1402                    switch ((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & (ZEND_ISSET|ZEND_ISEMPTY)) {
[1]1403                    case ZEND_ISSET:
[775]1404                        $rvalue = "isset(" . str($rvalue) . ")";
[1]1405                        break;
1406                    case ZEND_ISEMPTY:
[775]1407                        $rvalue = "empty(" . str($rvalue) . ")";
[1]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 ? '&' : '');
[744]1418                    $EX['argstack'][] = $ref . str($this->getOpVal($op1, $EX));
[1]1419                    break;
1420                    // }}}
[720]1421                case XC_INIT_STATIC_METHOD_CALL:
[749]1422                case XC_INIT_METHOD_CALL: // {{{
[720]1423                    array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
[722]1424                    if ($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) {
[1]1425                        $obj = $this->getOpVal($op1, $EX);
1426                        if (!isset($obj)) {
1427                            $obj = '$this';
1428                        }
[722]1429                        if ($opc == XC_INIT_STATIC_METHOD_CALL || /* PHP4 */ isset($op1['constant'])) {
[720]1430                            $EX['object'] = null;
[753]1431                            $EX['called_scope'] = $this->stripNamespace(unquoteName($obj, $EX));
[720]1432                        }
1433                        else {
1434                            $EX['object'] = $obj;
1435                            $EX['called_scope'] = null;
1436                        }
[1]1437                        if ($res['op_type'] != XC_IS_UNUSED) {
1438                            $resvar = '$obj call$';
1439                        }
1440                    }
1441                    else {
1442                        $EX['object'] = null;
[720]1443                        $EX['called_scope'] = null;
[1]1444                    }
1445
[749]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';
[1]1449                    }
[749]1450                    break;
1451                    // }}}
[753]1452                case XC_INIT_NS_FCALL_BY_NAME:
[749]1453                case XC_INIT_FCALL_BY_NAME: // {{{
[755]1454                    array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
[749]1455                    if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) {
1456                        break;
[1]1457                    }
[749]1458                    $EX['object'] = null;
1459                    $EX['called_scope'] = null;
1460                    $EX['fbc'] = $this->getOpVal($op2, $EX);
[1]1461                    break;
1462                    // }}}
[749]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                    // }}}
[1]1470                case XC_DO_FCALL_BY_FUNC:
[716]1471                    $which = $op1['var'];
[1]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:
[749]1477                    $fname = unquoteName($this->getOpVal($op1, $EX, false), $EX);
[1]1478                    $args = $this->popargs($EX, $ext);
1479                    $resvar = $fname . "($args)";
1480                    break;
1481                case XC_DO_FCALL_BY_NAME: // {{{
1482                    $object = null;
1483
[749]1484                    $fname = unquoteName($EX['fbc'], $EX);
[1]1485                    if (!is_int($EX['object'])) {
1486                        $object = $EX['object'];
1487                    }
1488
1489                    $args = $this->popargs($EX, $ext);
1490
[753]1491                    $prefix = (isset($object) ? $object . '->' : '' )
1492                        . (isset($EX['called_scope']) ? $EX['called_scope'] . '::' : '' );
1493                    $resvar = $prefix
1494                        . (!$prefix ? $this->stripNamespace($fname) : $fname)
1495                        . "($args)";
[1]1496                    unset($args);
1497
1498                    if (is_int($EX['object'])) {
1499                        $T[$EX['object']] = $resvar;
1500                        $resvar = null;
1501                    }
[720]1502                    list($EX['fbc'], $EX['object'], $EX['called_scope']) = array_pop($EX['arg_types_stack']);
[1]1503                    break;
1504                    // }}}
1505                case XC_VERIFY_ABSTRACT_CLASS: // {{{
[716]1506                    //unset($T[$op1['var']]);
[1]1507                    break;
1508                    // }}}
1509                case XC_DECLARE_CLASS: 
[714]1510                case XC_DECLARE_INHERITED_CLASS:
1511                case XC_DECLARE_INHERITED_CLASS_DELAYED: // {{{
[716]1512                    $key = $op1['constant'];
[714]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']));
[1]1516                        exit;
1517                    }
[714]1518                    $class = &$this->dc['class_table'][$key];
[751]1519                    if (!isset($class['name'])) {
1520                        $class['name'] = unquoteName($this->getOpVal($op2, $EX), $EX);
1521                    }
[714]1522                    if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) {
[1]1523                        $ext /= XC_SIZEOF_TEMP_VARIABLE;
1524                        $class['parent'] = $T[$ext];
1525                        unset($T[$ext]);
1526                    }
1527                    else {
1528                        $class['parent'] = null;
1529                    }
1530
[749]1531                    for (;;) {
[787]1532                        if ($i + 1 <= $last
[749]1533                         && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE
1534                         && $opcodes[$i + 1]['op1']['var'] == $res['var']) {
1535                            // continue
1536                        }
[787]1537                        else if ($i + 2 <= $last
[749]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                        }
[761]1546                        $this->usedOps[XC_ADD_INTERFACE] = true;
[749]1547
[1]1548                        $fetchop = &$opcodes[$i + 1];
[753]1549                        $interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2'], $EX), $EX));
[1]1550                        $addop = &$opcodes[$i + 2];
[753]1551                        $class['interfaces'][$addop['extended_value']] = $interface;
[1]1552                        unset($fetchop, $addop);
1553                        $i += 2;
1554                    }
1555                    $this->dclass($class);
[749]1556                    echo "\n";
[1]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:
[749]1571                        $op2val = value(chr(str($op2val)));
[1]1572                        break;
1573                    case XC_ADD_STRING:
1574                        break;
1575                    case XC_ADD_VAR:
1576                        break;
1577                    }
[749]1578                    if (str($op1val) == "''") {
[1]1579                        $rvalue = $op2val;
1580                    }
[749]1581                    else if (str($op2val) == "''") {
[1]1582                        $rvalue = $op1val;
1583                    }
1584                    else {
[744]1585                        $rvalue = str($op1val) . ' . ' . str($op2val);
[1]1586                    }
1587                    $resvar = $rvalue;
1588                    // }}}
1589                    break;
1590                case XC_PRINT: // {{{
1591                    $op1val = $this->getOpVal($op1, $EX);
[761]1592                    $resvar = "print(" . str($op1val) . ")";
[1]1593                    break;
1594                    // }}}
1595                case XC_ECHO: // {{{
1596                    $op1val = $this->getOpVal($op1, $EX);
[744]1597                    $resvar = "echo " . str($op1val);
[1]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) {
[737]1610                        $assoc = $this->getOpVal($op2, $EX);
1611                        if (isset($assoc)) {
1612                            $T[$res['var']]->value[] = array($assoc, $rvalue);
[1]1613                        }
1614                        else {
[737]1615                            $T[$res['var']]->value[] = array(null, $rvalue);
[1]1616                        }
1617                    }
1618                    else {
1619                        if ($opc == XC_INIT_ARRAY) {
1620                            $resvar = new Decompiler_Array();
1621                            if (!isset($rvalue)) {
1622                                continue;
1623                            }
1624                        }
1625
[737]1626                        $assoc = $this->getOpVal($op2, $EX);
1627                        if (isset($assoc)) {
1628                            $resvar->value[] = array($assoc, $rvalue);
[1]1629                        }
1630                        else {
[737]1631                            $resvar->value[] = array(null, $rvalue);
[1]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: // {{{
[744]1645                    $resvar = "return " . str($this->getOpVal($op1, $EX));
[1]1646                    break;
1647                    // }}}
1648                case XC_INCLUDE_OR_EVAL: // {{{
[716]1649                    $type = $op2['var']; // hack
[1]1650                    $keyword = $this->includeTypes[$type];
[749]1651                    $resvar = "$keyword " . str($this->getOpVal($op1, $EX));
[1]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;
[716]1662                    $T[$res['var']] = $fe;
[1]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'];
[716]1670                        $T[$res['var']] = $fe;
[1]1671                    }
1672                    break;
1673                    // }}}
1674                case XC_SWITCH_FREE: // {{{
[716]1675                    // unset($T[$op1['var']]);
[1]1676                    break;
1677                    // }}}
1678                case XC_FREE: // {{{
[716]1679                    $free = $T[$op1['var']];
[1]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                    }
[716]1684                    unset($T[$op1['var']], $free);
[1]1685                    break;
1686                    // }}}
1687                case XC_JMP_NO_CTOR:
1688                    break;
[749]1689                case XC_JMP_SET: // ?:
[760]1690                    $resvar = $this->getOpVal($op1, $EX);
1691                    $op['cond'] = $resvar; 
1692                    $op['isjmp'] = true;
1693                    break;
[1]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'])) {
[723]1712                        echo "TODO(cond_false):\n";
[1]1713                        var_dump($op);// exit;
1714                    }
[760]1715                    if ($opc == XC_JMPZ_EX || $opc == XC_JMPNZ_EX) {
[716]1716                        $targetop = &$EX['opcodes'][$op2['opline_num']];
[1]1717                        if ($opc == XC_JMPNZ_EX) {
[744]1718                            $targetop['cond_true'][] = foldToCode($rvalue, $EX);
[1]1719                        }
1720                        else {
[744]1721                            $targetop['cond_false'][] = foldToCode($rvalue, $EX);
[1]1722                        }
1723                        unset($targetop);
1724                    }
1725                    else {
1726                        $op['cond'] = $rvalue; 
1727                        $op['isjmp'] = true;
1728                    }
1729                    break;
1730                    // }}}
[758]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;
[749]1741                case XC_GOTO:
[787]1742                    $resvar = 'goto label' . $op['op1']['var'];
1743                    $istmpres = false;
1744                    break;
1745
[1]1746                case XC_JMP: // {{{
1747                    $op['cond'] = null;
1748                    $op['isjmp'] = true;
1749                    break;
1750                    // }}}
1751                case XC_CASE:
[743]1752                    $switchValue = $this->getOpVal($op1, $EX);
1753                    $caseValue = $this->getOpVal($op2, $EX);
[744]1754                    $resvar = str($switchValue) . ' == ' . str($caseValue);
[743]1755                    break;
[1]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) {
[716]1761                        $default = value($op['op2']['constant']);
[1]1762                    }
1763                    else {
1764                        $default = null;
1765                    }
[737]1766                    $EX['recvs'][str($offset)] = array($lvalue, $default);
[1]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'])) {
[761]1778                        $resvar = $this->getOpVal($op1, $EX) . '->' . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
[1]1779                    }
1780                    else {
1781                        $resvar = $this->getOpVal($op1, $EX);
1782                    }
1783                    $opstr = isset($flags['DEC']) ? '--' : '++';
1784                    if (isset($flags['POST'])) {
[749]1785                        $resvar .= $opstr;
[1]1786                    }
1787                    else {
[749]1788                        $resvar = "$opstr$resvar";
[1]1789                    }
1790                    break;
1791                    // }}}
1792
1793                case XC_BEGIN_SILENCE: // {{{
1794                    $EX['silence'] ++;
1795                    break;
1796                    // }}}
1797                case XC_END_SILENCE: // {{{
1798                    $EX['silence'] --;
[744]1799                    $lastresvar = '@' . str($lastresvar, $EX);
[1]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;
[751]1823                case XC_DECLARE_FUNCTION:
1824                    $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
1825                    break;
[752]1826                case XC_DECLARE_LAMBDA_FUNCTION: // {{{
[749]1827                    ob_start();
1828                    $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
1829                    $resvar = ob_get_clean();
1830                    $istmpres = true;
1831                    break;
[752]1832                    // }}}
1833                case XC_DECLARE_CONST:
[753]1834                    $name = $this->stripNamespace(unquoteName($this->getOpVal($op1, $EX), $EX));
1835                    $value = str($this->getOpVal($op2, $EX));
1836                    $resvar = 'const ' . $name . ' = ' . $value;
[752]1837                    break;
[1]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;
[749]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;
[1]1853                default: // {{{
1854                    echo "\x1B[31m * TODO ", $opname, "\x1B[0m\n";
[761]1855                    $covered = false;
[1]1856                    // }}}
1857                }
[761]1858                if ($covered) {
1859                    $this->usedOps[$opc] = true;
1860                }
[1]1861            }
1862            if (isset($resvar)) {
1863                if ($istmpres) {
[716]1864                    $T[$res['var']] = $resvar;
1865                    $lastresvar = &$T[$res['var']];
[1]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)) {
[744]1893                array_unshift($args, foldToCode($a, $EX));
[1]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'];
[763]1906        $d = array(xcache_get_opcode($op['opcode']), $op['opcode']);
[1]1907
[762]1908        foreach (array('op1' => '1:', 'op2' => '2:', 'result' => '>') as $k => $kk) {
[1]1909            switch ($op[$k]['op_type']) {
1910            case XC_IS_UNUSED:
[762]1911                $d[$kk] = 'U:' . $op[$k]['opline_num'];
[1]1912                break;
1913
1914            case XC_IS_VAR:
[716]1915                $d[$kk] = '$' . $op[$k]['var'];
[762]1916                if ($k != 'result') {
[749]1917                    $d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
[1]1918                }
1919                break;
1920
1921            case XC_IS_TMP_VAR:
[716]1922                $d[$kk] = '#' . $op[$k]['var'];
[762]1923                if ($k != 'result') {
[749]1924                    $d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
[1]1925                }
1926                break;
1927
1928            case XC_IS_CV:
1929                $d[$kk] = $this->getOpVal($op[$k], $EX);
1930                break;
1931
1932            default:
[762]1933                if ($k == 'result') {
[731]1934                    var_dump($op);
1935                    exit;
[1]1936                    assert(0);
1937                }
1938                else {
1939                    $d[$kk] = $this->getOpVal($op[$k], $EX);
1940                }
1941            }
1942        }
[762]1943        $d[';'] = $op['extended_value'];
[1]1944
[762]1945        foreach ($d as $k => $v) {
[763]1946            echo is_int($k) ? '' : $k, str($v), "\t";
[762]1947        }
1948        echo PHP_EOL;
[1]1949    }
1950    // }}}
[787]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    // }}}
[1]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            }
[736]1979            $arg = $EX['recvs'][$i + 1];
[1]1980            if (isset($op_array['arg_info'])) {
1981                $ai = $op_array['arg_info'][$i];
1982                if (!empty($ai['class_name'])) {
[753]1983                    echo $this->stripNamespace($ai['class_name']), ' ';
[736]1984                    if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
[1]1985                        echo 'or NULL ';
1986                    }
1987                }
1988                else if (!empty($ai['array_type_hint'])) {
1989                    echo 'array ';
[736]1990                    if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
[1]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                }
[744]2019                echo str($arg[0], $indent);
[1]2020            }
[736]2021            if (isset($arg[1])) {
[744]2022                echo ' = ', str($arg[1], $indent);
[736]2023            }
[1]2024        }
2025    }
2026    // }}}
[780]2027    function duses(&$EX, $indent) // {{{
2028    {
[781]2029        if ($EX['uses']) {
2030            echo ' use(', implode(', ', $EX['uses']), ')';
[780]2031        }
2032    }
2033    // }}}
[1]2034    function dfunction($func, $indent = '', $nobody = false) // {{{
2035    {
[753]2036        $this->detectNamespace($func['op_array']['function_name']);
2037
[1]2038        if ($nobody) {
2039            $EX = array();
2040            $EX['op_array'] = &$func['op_array'];
2041            $EX['recvs'] = array();
[780]2042            $EX['uses'] = array();
[1]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
[753]2051        $functionName = $this->stripNamespace($func['op_array']['function_name']);
[749]2052        if ($functionName == '{closure}') {
2053            $functionName = '';
2054        }
[781]2055        echo 'function', $functionName ? ' ' . $functionName : '', '(';
[1]2056        $this->dargs($EX, $indent);
[749]2057        echo ")";
[780]2058        $this->duses($EX, $indent);
[749]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        }
[1]2077    }
2078    // }}}
2079    function dclass($class, $indent = '') // {{{
2080    {
[753]2081        $this->detectNamespace($class['name']);
2082
[1]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 {
[749]2095                if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
[1]2096                    echo "abstract ";
2097                }
[749]2098                if ($class['ce_flags'] & ZEND_ACC_FINAL_CLASS) {
[1]2099                    echo "final ";
2100                }
2101            }
2102        }
[753]2103        echo $isinterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']);
[1]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, ' = ';
[744]2125                    echo str(value($v), $newindent);
[1]2126                    echo ";\n";
2127                }
2128            }
2129        }
2130        // }}}
2131        // {{{ properties
[730]2132        $member_variables = isset($class['properties_info']) ? $class['properties_info'] : ($class['default_static_members'] + $class['default_properties']);
2133        if ($member_variables) {
[1]2134            echo "\n";
[713]2135            $infos = !empty($class['properties_info']) ? $class['properties_info'] : null;
[730]2136            foreach ($member_variables as $name => $dummy) {
[1]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;
[713]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;
[731]2162                if (!ZEND_ENGINE_2) {
[1]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 ";
[713]2178                        $mangled = true;
[1]2179                        break;
2180                    case ZEND_ACC_PROTECTED:
2181                        echo "protected ";
[713]2182                        $mangled = true;
[1]2183                        break;
2184                    }
2185                }
2186
2187                echo '$', $name;
[713]2188
[730]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;
[713]2194
[730]2195                    $value = $class[$static ? 'default_static_members' : 'default_properties'][$key];
2196                }
[713]2197                if (isset($value)) {
[1]2198                    echo ' = ';
[744]2199                    echo str(value($value), $newindent);
[1]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;
[749]2218                    $isAbstractMethod = false;
[1]2219                    if (isset($opa['fn_flags'])) {
[749]2220                        if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isinterface) {
[1]2221                            echo "abstract ";
[749]2222                            $isAbstractMethod = true;
[1]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                    }
[749]2246                    $this->dfunction($func, $newindent, $isinterface || $isAbstractMethod);
[1]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    {
[749]2277        echo "<?". "php\n\n";
[1]2278        foreach ($this->dc['class_table'] as $key => $class) {
2279            if ($key{0} != "\0") {
[749]2280                $this->dclass($class);
[1]2281                echo "\n";
2282            }
2283        }
2284
2285        foreach ($this->dc['function_table'] as $key => $func) {
2286            if ($key{0} != "\0") {
[749]2287                $this->dfunction($func);
[1]2288                echo "\n";
2289            }
2290        }
2291
2292        $this->dop_array($this->dc['op_array']);
2293        echo "\n?" . ">\n";
[761]2294
2295        if (!empty($this->test)) {
2296            $this->outputUnusedOp();
2297        }
[1]2298        return true;
2299    }
2300    // }}}
[761]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    // }}}
[1]2314}
2315
2316// {{{ defines
[749]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
[1]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);
[749]2332if (ZEND_ENGINE_2_4) {
2333    define('ZEND_ACC_TRAIT',                  0x120);
2334}
[1]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
[731]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
[1]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);
[722]2376define('ZEND_FETCH_CLASS_STATIC',     7);
[731]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}
[1]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));
[731]2392if (ZEND_ENGINE_2_4) {
2393    define('EXT_TYPE_UNUSED',     (1<<5));
2394}
2395else {
2396    define('EXT_TYPE_UNUSED',     (1<<0));
2397}
[1]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);
[761]2419define('IS_BOOL',     ZEND_ENGINE_2 ? 3 : 6);
[1]2420define('IS_ARRAY',    4);
2421define('IS_OBJECT',   5);
[761]2422define('IS_STRING',   ZEND_ENGINE_2 ? 6 : 3);
[1]2423define('IS_RESOURCE', 7);
2424define('IS_CONSTANT', 8);
2425define('IS_CONSTANT_ARRAY',   9);
[781]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);
[1]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,
[749]2454    'XC_UNSET_OBJ' => -1,
[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,
[720]2459    'XC_INIT_STATIC_METHOD_CALL' => -1,
[1]2460    'XC_INIT_METHOD_CALL' => -1,
2461    'XC_VERIFY_ABSTRACT_CLASS' => -1,
2462    'XC_DECLARE_CLASS' => -1,
2463    'XC_DECLARE_INHERITED_CLASS' => -1,
[714]2464    'XC_DECLARE_INHERITED_CLASS_DELAYED' => -1,
[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,
[749]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,
[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.