source: trunk/Decompiler.class.php @ 788

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

Decompiler: adds blank line between blocks

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