source: trunk/Decompiler.class.php @ 777

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

decompiler: refix isset/empty on variable

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