source: trunk/Decompiler.class.php @ 799

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

Decompiler: ?: and ? :

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