Changeset 817 in svn
- Timestamp:
- 2011-06-04T03:34:36Z (7 years ago)
- Location:
- branches/1.3
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/1.3
- Property svn:mergeinfo changed
/trunk merged: 785-797,799-811
- Property svn:mergeinfo changed
-
branches/1.3/ChangeLog
r814 r817 4 4 * adds 30 seconds timeout to "compiling" flag 5 5 * decompiler: improves decompiling 6 * disassembler: improper handling of null field 6 7 * disassembler: DECLARE_INHERITED_CLASS/DELAYED class not found 7 8 * disassembler: don't dump builtin functions -
branches/1.3/Decompiler.class.php
r784 r817 150 150 var $op2; 151 151 var $parent; 152 var $indent;153 152 154 153 function Decompiler_Binop($parent, $op1, $opc, $op2) … … 164 163 $opstr = $this->parent->binops[$this->opc]; 165 164 166 $op1 = foldToCode($this->op1, $indent); 167 if (is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) { 168 $op1 = "(" . str($op1, $indent) . ")"; 169 } 170 $op2 = foldToCode($this->op2, $indent); 171 if (is_a($this->op2, 'Decompiler_Binop') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') { 172 $op2 = "(" . str($op2, $indent) . ")"; 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; 173 177 } 174 178 … … 177 181 } 178 182 179 return str($op1) . ' ' . $opstr . ' ' . str($op2); 183 return str($op1, $indent) . ' ' . $opstr . ($this->opc == XC_ASSIGN_REF ? '' : ' ') . str($op2, $indent); 184 } 185 } 186 // }}} 187 class 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); 180 212 } 181 213 } … … 486 518 XC_ASSIGN_BW_XOR => "^=", 487 519 XC_BOOL_XOR => "xor", 520 XC_ASSIGN => "=", 521 XC_ASSIGN_REF => "= &", 522 XC_JMP_SET => "?:", 523 XC_JMPZ_EX => "&&", 524 XC_JMPNZ_EX => "||", 488 525 ); 489 526 // }}} … … 522 559 } 523 560 // }}} 524 function outputPhp(&$opcodes, $opline, $last, $indent) // {{{ 525 { 526 $origindent = $indent; 561 function outputPhp(&$EX, $range) // {{{ 562 { 563 $needBlankline = isset($EX['lastBlock']); 564 $indent = $EX['indent']; 527 565 $curticks = 0; 528 for ($i = $opline; $i <= $last; $i ++) { 529 $op = $opcodes[$i]; 566 for ($i = $range[0]; $i <= $range[1]; $i ++) { 567 $op = $EX['opcodes'][$i]; 568 if (isset($op['gofrom'])) { 569 if ($needBlankline) { 570 $needBlankline = false; 571 echo PHP_EOL; 572 } 573 echo 'label' . $i, ":\n"; 574 } 530 575 if (isset($op['php'])) { 531 576 $toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0; … … 534 579 $curticks = $toticks; 535 580 if (!$curticks) { 536 echo $ origindent, "}\n\n";537 $indent = $ origindent;581 echo $EX['indent'], "}\n\n"; 582 $indent = $EX['indent']; 538 583 } 539 584 else { 540 585 if ($oldticks) { 541 echo $ origindent, "}\n\n";586 echo $EX['indent'], "}\n\n"; 542 587 } 543 588 else if (!$oldticks) { 544 589 $indent .= INDENT; 545 590 } 546 echo $origindent, "declare (ticks=$curticks) {\n"; 547 } 591 if ($needBlankline) { 592 $needBlankline = false; 593 echo PHP_EOL; 594 } 595 echo $EX['indent'], "declare (ticks=$curticks) {\n"; 596 } 597 } 598 if ($needBlankline) { 599 $needBlankline = false; 600 echo PHP_EOL; 548 601 } 549 602 echo $indent, str($op['php'], $indent), ";\n"; 603 $EX['lastBlock'] = 'basic'; 550 604 } 551 605 } 552 606 if ($curticks) { 553 echo $ origindent, "}\n";554 } 555 } 556 // }}} 557 function getOpVal($op, &$EX, $ tostr = true, $free = false) // {{{607 echo $EX['indent'], "}\n"; 608 } 609 } 610 // }}} 611 function getOpVal($op, &$EX, $free = false) // {{{ 558 612 { 559 613 switch ($op['op_type']) { 560 614 case XC_IS_CONST: 561 return foldToCode(value($op['constant']), $EX);615 return value($op['constant']); 562 616 563 617 case XC_IS_VAR: … … 565 619 $T = &$EX['Ts']; 566 620 $ret = $T[$op['var']]; 567 if ($tostr) { 568 $ret = foldToCode($ret, $EX); 569 } 570 if ($free) { 621 if ($free && empty($this->keepTs)) { 571 622 unset($T[$op['var']]); 572 623 } … … 598 649 function &fixOpcode($opcodes, $removeTailing = false, $defaultReturnValue = null) // {{{ 599 650 { 600 for ($i = 0, $cnt = count($opcodes); $i < $cnt; $i ++) { 651 $last = count($opcodes) - 1; 652 for ($i = 0; $i <= $last; $i ++) { 601 653 if (function_exists('xcache_get_fixed_opcode')) { 602 654 $opcodes[$i]['opcode'] = xcache_get_fixed_opcode($opcodes[$i]['opcode'], $i); … … 648 700 } 649 701 // }}} 702 function decompileBasicBlock(&$EX, $range, $unhandled = false) // {{{ 703 { 704 $this->dasmBasicBlock($EX, $range); 705 if ($unhandled) { 706 $this->dumpRange($EX, $range); 707 } 708 $this->outputPhp($EX, $range); 709 } 710 // }}} 711 function isIfCondition(&$EX, $range) // {{{ 712 { 713 $opcodes = &$EX['opcodes']; 714 $firstOp = &$opcodes[$range[0]]; 715 return $firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts']) && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_JMP 716 && !empty($opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts']) 717 && $opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'][0] == $range[1] + 1; 718 } 719 // }}} 720 function removeJmpInfo(&$EX, $line) // {{{ 721 { 722 $opcodes = &$EX['opcodes']; 723 foreach ($opcodes[$line]['jmpouts'] as $jmpTo) { 724 $jmpins = &$opcodes[$jmpTo]['jmpins']; 725 $jmpins = array_flip($jmpins); 726 unset($jmpins[$line]); 727 $jmpins = array_keys($jmpins); 728 } 729 // $opcodes[$line]['opcode'] = XC_NOP; 730 unset($opcodes[$line]['jmpouts']); 731 } 732 // }}} 733 function beginScope(&$EX, $doIndent = true) // {{{ 734 { 735 array_push($EX['scopeStack'], array($EX['lastBlock'], $EX['indent'])); 736 if ($doIndent) { 737 $EX['indent'] .= INDENT; 738 } 739 $EX['lastBlock'] = null; 740 } 741 // }}} 742 function endScope(&$EX) // {{{ 743 { 744 list($EX['lastBlock'], $EX['indent']) = array_pop($EX['scopeStack']); 745 } 746 // }}} 747 function beginComplexBlock(&$EX) // {{{ 748 { 749 if (isset($EX['lastBlock'])) { 750 echo PHP_EOL; 751 $EX['lastBlock'] = null; 752 } 753 } 754 // }}} 755 function endComplexBlock(&$EX) // {{{ 756 { 757 $EX['lastBlock'] = 'complex'; 758 } 759 // }}} 760 function decompileComplexBlock(&$EX, $range) // {{{ 761 { 762 $T = &$EX['Ts']; 763 $opcodes = &$EX['opcodes']; 764 $indent = $EX['indent']; 765 766 $firstOp = &$opcodes[$range[0]]; 767 $lastOp = &$opcodes[$range[1]]; 768 769 // {{{ && || and or 770 if (($firstOp['opcode'] == XC_JMPZ_EX || $firstOp['opcode'] == XC_JMPNZ_EX) && !empty($firstOp['jmpouts']) 771 && $firstOp['jmpouts'][0] == $range[1] + 1 772 && $lastOp['opcode'] == XC_BOOL 773 && $firstOp['opcode']['result']['var'] == $lastOp['opcode']['result']['var'] 774 ) { 775 $this->removeJmpInfo($EX, $range[0]); 776 777 $this->recognizeAndDecompileClosedBlocks($EX, array($range[0], $range[0])); 778 $op1 = $this->getOpVal($firstOp['result'], $EX, true); 779 780 $this->recognizeAndDecompileClosedBlocks($EX, array($range[0] + 1, $range[1])); 781 $op2 = $this->getOpVal($lastOp['result'], $EX, true); 782 783 $T[$firstOp['result']['var']] = new Decompiler_Binop($this, $op1, $firstOp['opcode'], $op2); 784 return false; 785 } 786 // }}} 787 // {{{ ?: excluding JMP_SET 788 if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts']) 789 && $range[1] >= $range[0] + 3 790 && $opcodes[$firstOp['jmpouts'][0] - 2]['opcode'] == XC_QM_ASSIGN 791 && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_JMP && $opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'][0] == $range[1] + 1 792 && $lastOp['opcode'] == XC_QM_ASSIGN 793 ) { 794 $trueRange = array($range[0] + 1, $firstOp['jmpouts'][0] - 2); 795 $falseRange = array($firstOp['jmpouts'][0], $range[1]); 796 $this->removeJmpInfo($EX, $range[0]); 797 798 $condition = $this->getOpVal($firstOp['op1'], $EX); 799 $this->recognizeAndDecompileClosedBlocks($EX, $trueRange); 800 $trueValue = $this->getOpVal($opcodes[$trueRange[1]]['result'], $EX, true); 801 $this->recognizeAndDecompileClosedBlocks($EX, $falseRange); 802 $falseValue = $this->getOpVal($opcodes[$falseRange[1]]['result'], $EX, true); 803 $T[$opcodes[$trueRange[1]]['result']['var']] = new Decompiler_TriOp($condition, $trueValue, $falseValue); 804 return false; 805 } 806 // }}} 807 // {{{ goto 808 if ($firstOp['opcode'] == XC_JMP && !empty($firstOp['jmpouts']) && $firstOp['jmpouts'][0] == $range[1] + 1) { 809 $this->removeJmpInfo($EX, $range[0]); 810 $firstOp['opcode'] = XC_GOTO; 811 $target = $firstOp['op1']['var']; 812 $firstOp['goto'] = $target; 813 $opcodes[$target]['gofrom'][] = $range[0]; 814 815 $this->recognizeAndDecompileClosedBlocks($EX, $range); 816 return false; 817 } 818 // }}} 819 // {{{ for 820 if (!empty($firstOp['jmpins']) && $opcodes[$firstOp['jmpins'][0]]['opcode'] == XC_JMP 821 && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts']) && $lastOp['jmpouts'][0] <= $firstOp['jmpins'][0] 822 && !empty($opcodes[$range[1] + 1]['jmpins']) && $opcodes[$opcodes[$range[1] + 1]['jmpins'][0]]['opcode'] == XC_JMPZNZ 823 ) { 824 $nextRange = array($lastOp['jmpouts'][0], $firstOp['jmpins'][0]); 825 $conditionRange = array($range[0], $nextRange[0] - 1); 826 $this->removeJmpInfo($EX, $conditionRange[1]); 827 $bodyRange = array($nextRange[1], $range[1]); 828 $this->removeJmpInfo($EX, $bodyRange[1]); 829 830 $initial = ''; 831 $this->beginScope($EX); 832 $this->dasmBasicBlock($EX, $conditionRange); 833 $conditionCodes = array(); 834 for ($i = $conditionRange[0]; $i <= $conditionRange[1]; ++$i) { 835 if (isset($opcodes[$i]['php'])) { 836 $conditionCodes[] = str($opcodes[$i]['php'], $EX); 837 } 838 } 839 $conditionCodes[] = str($this->getOpVal($opcodes[$conditionRange[1]]['op1'], $EX), $EX); 840 if (implode(',', $conditionCodes) == 'true') { 841 $conditionCodes = array(); 842 } 843 $this->endScope($EX); 844 845 $this->beginScope($EX); 846 $this->dasmBasicBlock($EX, $nextRange); 847 $nextCodes = array(); 848 for ($i = $nextRange[0]; $i <= $nextRange[1]; ++$i) { 849 if (isset($opcodes[$i]['php'])) { 850 $nextCodes[] = str($opcodes[$i]['php'], $EX); 851 } 852 } 853 $this->endScope($EX); 854 855 $this->beginComplexBlock($EX); 856 echo $indent, 'for (', str($initial, $EX), '; ', implode(', ', $conditionCodes), '; ', implode(', ', $nextCodes), ') ', '{', PHP_EOL; 857 $this->beginScope($EX); 858 $this->recognizeAndDecompileClosedBlocks($EX, $bodyRange); 859 $this->endScope($EX); 860 echo $indent, '}', PHP_EOL; 861 $this->endComplexBlock($EX); 862 return; 863 } 864 // }}} 865 // {{{ if/elseif/else 866 if ($this->isIfCondition($EX, $range)) { 867 $this->beginComplexBlock($EX); 868 $isElseIf = false; 869 do { 870 $ifRange = array($range[0], $opcodes[$range[0]]['jmpouts'][0] - 1); 871 $this->removeJmpInfo($EX, $ifRange[0]); 872 $this->removeJmpInfo($EX, $ifRange[1]); 873 $condition = $this->getOpVal($opcodes[$ifRange[0]]['op1'], $EX); 874 875 echo $indent, $isElseIf ? 'else if' : 'if', ' (', str($condition, $EX), ') ', '{', PHP_EOL; 876 $this->beginScope($EX); 877 $this->recognizeAndDecompileClosedBlocks($EX, $ifRange); 878 $this->endScope($EX); 879 $EX['lastBlock'] = null; 880 echo $indent, '}', PHP_EOL; 881 882 $isElseIf = true; 883 // search for else if 884 $range[0] = $ifRange[1] + 1; 885 for ($i = $ifRange[1] + 1; $i <= $range[1]; ++$i) { 886 // find first jmpout 887 if (!empty($opcodes[$i]['jmpouts'])) { 888 if ($this->isIfCondition($EX, array($i, $range[1]))) { 889 $this->dasmBasicBlock($EX, array($range[0], $i)); 890 $range[0] = $i; 891 } 892 break; 893 } 894 } 895 } while ($this->isIfCondition($EX, $range)); 896 if ($ifRange[1] < $range[1]) { 897 $elseRange = array($ifRange[1], $range[1]); 898 echo $indent, 'else ', '{', PHP_EOL; 899 $this->beginScope($EX); 900 $this->recognizeAndDecompileClosedBlocks($EX, $elseRange); 901 $this->endScope($EX); 902 $EX['lastBlock'] = null; 903 echo $indent, '}', PHP_EOL; 904 } 905 $this->endComplexBlock($EX); 906 return; 907 } 908 // }}} 909 // {{{ try/catch 910 if (!empty($firstOp['jmpins']) && !empty($opcodes[$firstOp['jmpins'][0]]['isCatchBegin'])) { 911 $catchBlocks = array(); 912 $catchFirst = $firstOp['jmpins'][0]; 913 914 $tryRange = array($range[0], $catchFirst - 1); 915 916 // search for XC_CATCH 917 $this->removeJmpInfo($EX, $catchFirst); 918 for ($i = $catchFirst; $i <= $range[1]; ) { 919 if ($opcodes[$i]['opcode'] == XC_CATCH) { 920 $catchOpLine = $i; 921 $this->removeJmpInfo($EX, $catchOpLine); 922 923 $catchNext = $opcodes[$catchOpLine]['extended_value']; 924 $catchBodyLast = $catchNext - 1; 925 if ($opcodes[$catchBodyLast]['opcode'] == XC_JMP) { 926 --$catchBodyLast; 927 } 928 929 $catchBlocks[$catchFirst] = array($catchOpLine, $catchBodyLast); 930 931 $i = $catchFirst = $catchNext; 932 } 933 else { 934 ++$i; 935 } 936 } 937 938 if ($opcodes[$tryRange[1]]['opcode'] == XC_JMP) { 939 --$tryRange[1]; 940 } 941 942 $this->beginComplexBlock($EX); 943 echo $indent, "try {", PHP_EOL; 944 $this->beginScope($EX); 945 $this->recognizeAndDecompileClosedBlocks($EX, $tryRange); 946 $this->endScope($EX); 947 echo $indent, '}', PHP_EOL; 948 foreach ($catchBlocks as $catchFirst => $catchInfo) { 949 list($catchOpLine, $catchBodyLast) = $catchInfo; 950 $catchBodyFirst = $catchOpLine + 1; 951 $this->dasmBasicBlock($EX, array($catchFirst, $catchOpLine)); 952 $catchOp = &$opcodes[$catchOpLine]; 953 echo $indent, 'catch (', str($this->getOpVal($catchOp['op1'], $EX)), ' ', str($this->getOpVal($catchOp['op2'], $EX)), ") {", PHP_EOL; 954 unset($catchOp); 955 956 $EX['lastBlock'] = null; 957 $this->beginScope($EX); 958 $this->recognizeAndDecompileClosedBlocks($EX, array($catchBodyFirst, $catchBodyLast)); 959 $this->endScope($EX); 960 echo $indent, '}', PHP_EOL; 961 } 962 $this->endComplexBlock($EX); 963 return; 964 } 965 // }}} 966 // {{{ switch/case 967 if ($firstOp['opcode'] == XC_SWITCH_FREE && isset($T[$firstOp['op1']['var']])) { 968 // TODO: merge this code to CASE code. use SWITCH_FREE to detect begin of switch by using $Ts if possible 969 $this->beginComplexBlock($EX); 970 echo $indent, 'switch (', str($this->getOpVal($firstOp['op1'], $EX)), ") {", PHP_EOL; 971 echo $indent, '}', PHP_EOL; 972 $this->endComplexBlock($EX); 973 return; 974 } 975 976 if ( 977 ($firstOp['opcode'] == XC_CASE 978 || $firstOp['opcode'] == XC_JMP && !empty($firstOp['jmpouts']) && $opcodes[$firstOp['jmpouts'][0]]['opcode'] == XC_CASE 979 ) 980 && !empty($lastOp['jmpouts']) 981 ) { 982 $cases = array(); 983 $caseDefault = null; 984 $caseOp = null; 985 for ($i = $range[0]; $i <= $range[1]; ) { 986 $op = $opcodes[$i]; 987 if ($op['opcode'] == XC_CASE) { 988 if (!isset($caseOp)) { 989 $caseOp = $op; 990 } 991 $jmpz = $opcodes[$i + 1]; 992 assert('$jmpz["opcode"] == XC_JMPZ'); 993 $caseNext = $jmpz['jmpouts'][0]; 994 $cases[$i] = $caseNext - 1; 995 $i = $caseNext; 996 } 997 else if ($op['opcode'] == XC_JMP && $op['jmpouts'][0] >= $i) { 998 // default 999 $caseNext = $op['jmpouts'][0]; 1000 $caseDefault = $i; 1001 $cases[$i] = $caseNext - 1; 1002 $i = $caseNext; 1003 } 1004 else { 1005 ++$i; 1006 } 1007 } 1008 1009 $this->beginComplexBlock($EX); 1010 1011 echo $indent, 'switch (', str($this->getOpVal($caseOp['op1'], $EX), $EX), ") {", PHP_EOL; 1012 $caseIsOut = false; 1013 foreach ($cases as $caseFirst => $caseLast) { 1014 if ($caseIsOut && empty($lastCaseFall)) { 1015 echo PHP_EOL; 1016 } 1017 1018 $caseOp = $opcodes[$caseFirst]; 1019 1020 echo $indent; 1021 if ($caseOp['opcode'] == XC_CASE) { 1022 echo 'case '; 1023 echo str($this->getOpVal($caseOp['op2'], $EX), $EX); 1024 echo ':', PHP_EOL; 1025 1026 $this->removeJmpInfo($EX, $caseFirst); 1027 ++$caseFirst; 1028 1029 assert('$opcodes[$caseFirst]["opcode"] == XC_JMPZ'); 1030 $this->removeJmpInfo($EX, $caseFirst); 1031 ++$caseFirst; 1032 } 1033 else { 1034 echo 'default'; 1035 echo ':', PHP_EOL; 1036 1037 assert('$opcodes[$caseFirst]["opcode"] == XC_JMP'); 1038 $this->removeJmpInfo($EX, $caseFirst); 1039 ++$caseFirst; 1040 } 1041 1042 assert('$opcodes[$caseLast]["opcode"] == XC_JMP'); 1043 $this->removeJmpInfo($EX, $caseLast); 1044 --$caseLast; 1045 switch ($opcodes[$caseLast]['opcode']) { 1046 case XC_BRK: 1047 case XC_CONT: 1048 case XC_GOTO: 1049 $lastCaseFall = false; 1050 break; 1051 1052 default: 1053 $lastCaseFall = true; 1054 } 1055 1056 $this->beginScope($EX); 1057 $this->recognizeAndDecompileClosedBlocks($EX, array($caseFirst, $caseLast)); 1058 $this->endScope($EX); 1059 $caseIsOut = true; 1060 } 1061 echo $indent, '}', PHP_EOL; 1062 1063 $this->endComplexBlock($EX); 1064 return; 1065 } 1066 // }}} 1067 // {{{ do/while 1068 if ($lastOp['opcode'] == XC_JMPNZ && !empty($lastOp['jmpouts']) 1069 && $lastOp['jmpouts'][0] == $range[0]) { 1070 $this->removeJmpInfo($EX, $range[1]); 1071 $this->beginComplexBlock($EX); 1072 1073 echo $indent, "do {", PHP_EOL; 1074 $this->beginScope($EX); 1075 $this->recognizeAndDecompileClosedBlocks($EX, $range); 1076 $this->endScope($EX); 1077 echo $indent, "} while (", str($this->getOpVal($lastOp['op1'], $EX)), ');', PHP_EOL; 1078 1079 $this->endComplexBlock($EX); 1080 return; 1081 } 1082 // }}} 1083 1084 // {{{ search firstJmpOp 1085 $firstJmp = null; 1086 $firstJmpOp = null; 1087 for ($i = $range[0]; $i <= $range[1]; ++$i) { 1088 if (!empty($opcodes[$i]['jmpouts'])) { 1089 $firstJmp = $i; 1090 $firstJmpOp = &$opcodes[$firstJmp]; 1091 break; 1092 } 1093 } 1094 // }}} 1095 1096 // {{{ while 1097 if (isset($firstJmpOp) 1098 && $firstJmpOp['opcode'] == XC_JMPZ 1099 && $firstJmpOp['jmpouts'][0] > $range[1] 1100 && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts']) 1101 && $lastOp['jmpouts'][0] == $range[0]) { 1102 $this->removeJmpInfo($EX, $firstJmp); 1103 $this->removeJmpInfo($EX, $range[1]); 1104 $this->beginComplexBlock($EX); 1105 1106 ob_start(); 1107 $this->beginScope($EX); 1108 $this->recognizeAndDecompileClosedBlocks($EX, $range); 1109 $this->endScope($EX); 1110 $body = ob_get_clean(); 1111 1112 echo $indent, 'while (', str($this->getOpVal($firstJmpOp['op1'], $EX)), ") {", PHP_EOL; 1113 echo $body; 1114 echo $indent, '}', PHP_EOL; 1115 1116 $this->endComplexBlock($EX); 1117 return; 1118 } 1119 // }}} 1120 // {{{ foreach 1121 if (isset($firstJmpOp) 1122 && $firstJmpOp['opcode'] == XC_FE_FETCH 1123 && $firstJmpOp['jmpouts'][0] > $range[1] 1124 && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts']) 1125 && $lastOp['jmpouts'][0] == $firstJmp) { 1126 $this->removeJmpInfo($EX, $firstJmp); 1127 $this->removeJmpInfo($EX, $range[1]); 1128 $this->beginComplexBlock($EX); 1129 1130 ob_start(); 1131 $this->beginScope($EX); 1132 $this->recognizeAndDecompileClosedBlocks($EX, $range); 1133 $this->endScope($EX); 1134 $body = ob_get_clean(); 1135 1136 $as = foldToCode($firstJmpOp['fe_as'], $EX); 1137 if (isset($firstJmpOp['fe_key'])) { 1138 $as = str($firstJmpOp['fe_key'], $EX) . ' => ' . str($as); 1139 } 1140 1141 echo $indent, 'foreach (', str($firstJmpOp['fe_src'], $EX), " as $as) {", PHP_EOL; 1142 echo $body; 1143 echo $indent, '}', PHP_EOL; 1144 1145 $this->endComplexBlock($EX); 1146 if ($opcodes[$range[1] + 1]['opcode'] == XC_SWITCH_FREE) { 1147 $this->removeJmpInfo($EX, $range[1] + 1); 1148 } 1149 return; 1150 } 1151 // }}} 1152 1153 $this->decompileBasicBlock($EX, $range, true); 1154 } 1155 // }}} 1156 function recognizeAndDecompileClosedBlocks(&$EX, $range) // {{{ decompile in a tree way 1157 { 1158 $opcodes = &$EX['opcodes']; 1159 1160 $starti = $range[0]; 1161 for ($i = $starti; $i <= $range[1]; ) { 1162 if (!empty($opcodes[$i]['jmpins']) || !empty($opcodes[$i]['jmpouts'])) { 1163 $blockFirst = $i; 1164 $blockLast = -1; 1165 $j = $blockFirst; 1166 do { 1167 $op = $opcodes[$j]; 1168 if (!empty($op['jmpins'])) { 1169 // care about jumping from blocks behind, not before 1170 foreach ($op['jmpins'] as $oplineNumber) { 1171 if ($oplineNumber <= $range[1] && $blockLast < $oplineNumber) { 1172 $blockLast = $oplineNumber; 1173 } 1174 } 1175 } 1176 if (!empty($op['jmpouts'])) { 1177 $blockLast = max($blockLast, max($op['jmpouts']) - 1); 1178 } 1179 ++$j; 1180 } while ($j <= $blockLast); 1181 if (!assert('$blockLast <= $range[1]')) { 1182 var_dump($blockLast, $range[1]); 1183 } 1184 1185 if ($blockLast >= $blockFirst) { 1186 if ($blockFirst > $starti) { 1187 $this->decompileBasicBlock($EX, array($starti, $blockFirst - 1)); 1188 } 1189 if ($this->decompileComplexBlock($EX, array($blockFirst, $blockLast)) === false) { 1190 if ($EX['lastBlock'] == 'complex') { 1191 echo PHP_EOL; 1192 } 1193 $EX['lastBlock'] = null; 1194 } 1195 $starti = $blockLast + 1; 1196 $i = $starti; 1197 } 1198 else { 1199 ++$i; 1200 } 1201 } 1202 else { 1203 ++$i; 1204 } 1205 } 1206 if ($starti <= $range[1]) { 1207 $this->decompileBasicBlock($EX, array($starti, $range[1])); 1208 } 1209 } 1210 // }}} 650 1211 function &dop_array($op_array, $indent = '') // {{{ 651 1212 { 652 1213 $op_array['opcodes'] = $this->fixOpcode($op_array['opcodes'], true, $indent == '' ? 1 : null); 653 1214 $opcodes = &$op_array['opcodes']; 654 $ EX['indent'] = '';655 // {{{ build jmp 656 for ($i = 0 , $cnt = count($opcodes); $i < $cnt; $i ++) {1215 $last = count($opcodes) - 1; 1216 // {{{ build jmpins/jmpouts to op_array 1217 for ($i = 0; $i <= $last; $i ++) { 657 1218 $op = &$opcodes[$i]; 658 /*659 if ($op['opcode'] == XC_JMPZ) {660 $this->dumpop($op, $EX);661 var_dump($op);662 }663 continue;664 */665 1219 $op['line'] = $i; 666 1220 switch ($op['opcode']) { … … 671 1225 672 1226 case XC_GOTO: 1227 $target = $op['op1']['var']; 1228 $op['goto'] = $target; 1229 $opcodes[$target]['gofrom'][] = $i; 1230 break; 1231 673 1232 case XC_JMP: 674 1233 $target = $op['op1']['var']; … … 689 1248 case XC_JMPZ_EX: 690 1249 case XC_JMPNZ_EX: 691 case XC_JMP_SET:1250 // case XC_JMP_SET: 692 1251 // case XC_FE_RESET: 693 1252 case XC_FE_FETCH: … … 707 1266 break; 708 1267 */ 709 } 1268 1269 case XC_SWITCH_FREE: 1270 $op['jmpouts'] = array($i + 1); 1271 $opcodes[$i + 1]['jmpins'][] = $i; 1272 break; 1273 1274 case XC_CASE: 1275 // just to link together 1276 $op['jmpouts'] = array($i + 2); 1277 $opcodes[$i + 2]['jmpins'][] = $i; 1278 break; 1279 1280 case XC_CATCH: 1281 $catchNext = $op['extended_value']; 1282 $op['jmpouts'] = array($catchNext); 1283 $opcodes[$catchNext]['jmpins'][] = $i; 1284 break; 1285 } 1286 /* 1287 if (!empty($op['jmpouts']) || !empty($op['jmpins'])) { 1288 echo $i, "\t", xcache_get_opcode($op['opcode']), PHP_EOL; 1289 } 1290 // */ 710 1291 } 711 1292 unset($op); 1293 if ($op_array['try_catch_array']) { 1294 foreach ($op_array['try_catch_array'] as $try_catch_element) { 1295 $catch_op = $try_catch_element['catch_op']; 1296 $try_op = $try_catch_element['try_op']; 1297 $opcodes[$try_op]['jmpins'][] = $catch_op; 1298 $opcodes[$catch_op]['jmpouts'][] = $try_op; 1299 $opcodes[$catch_op]['isCatchBegin'] = true; 1300 } 1301 } 712 1302 // }}} 713 1303 // build semi-basic blocks 714 1304 $nextbbs = array(); 715 1305 $starti = 0; 716 for ($i = 1 , $cnt = count($opcodes); $i < $cnt; $i ++) {1306 for ($i = 1; $i <= $last; $i ++) { 717 1307 if (isset($opcodes[$i]['jmpins']) 718 1308 || isset($opcodes[$i - 1]['jmpouts'])) { … … 721 1311 } 722 1312 } 723 $nextbbs[$starti] = $ cnt;1313 $nextbbs[$starti] = $last + 1; 724 1314 725 1315 $EX = array(); … … 729 1319 $EX['op_array'] = &$op_array; 730 1320 $EX['opcodes'] = &$opcodes; 1321 $EX['range'] = array(0, count($opcodes) - 1); 731 1322 // func call 732 1323 $EX['object'] = null; … … 735 1326 $EX['argstack'] = array(); 736 1327 $EX['arg_types_stack'] = array(); 737 $EX[' last'] = count($opcodes) - 1;1328 $EX['scopeStack'] = array(); 738 1329 $EX['silence'] = 0; 739 1330 $EX['recvs'] = array(); 740 1331 $EX['uses'] = array(); 741 742 for ($next = 0, $last = $EX['last']; 743 $loop = $this->outputCode($EX, $next, $last, $indent, true); 744 list($next, $last) = $loop) { 745 // empty 746 } 1332 $EX['lastBlock'] = null; 1333 1334 /* dump whole array 1335 $this->keepTs = true; 1336 $this->dasmBasicBlock($EX, $range); 1337 for ($i = $range[0]; $i <= $range[1]; ++$i) { 1338 echo $i, "\t", $this->dumpop($opcodes[$i], $EX); 1339 } 1340 // */ 1341 // decompile in a tree way 1342 $this->recognizeAndDecompileClosedBlocks($EX, $EX['range'], $EX['indent']); 747 1343 return $EX; 748 1344 } 749 1345 // }}} 750 function outputCode(&$EX, $opline, $last, $indent, $loop = false) // {{{ 751 { 752 $op = &$EX['opcodes'][$opline]; 753 $next = $EX['nextbbs'][$opline]; 754 755 $end = $next - 1; 756 if ($end > $last) { 757 $end = $last; 758 } 759 760 if (isset($op['jmpins'])) { 761 echo "\nline", $op['line'], ":\n"; 762 } 763 else { 764 // echo ";;;\n"; 765 } 766 $this->dasmBasicBlock($EX, $opline, $end); 767 $this->outputPhp($EX['opcodes'], $opline, $end, $indent); 768 // jmpout op 769 $op = &$EX['opcodes'][$end]; 770 $op1 = $op['op1']; 771 $op2 = $op['op2']; 772 $ext = $op['extended_value']; 773 $line = $op['line']; 774 775 if (isset($EX['opcodes'][$next])) { 776 if (isset($last) && $next > $last) { 777 $next = null; 778 } 779 } 780 else { 781 $next = null; 782 } 783 if ($op['opcode'] == XC_FE_FETCH) { 784 $opline = $next; 785 $next = $op['op2']['opline_num']; 786 $end = $next - 1; 787 788 ob_start(); 789 $this->outputCode($EX, $opline, $end /* - 1 skip last jmp */, $indent . INDENT); 790 $body = ob_get_clean(); 791 792 $as = foldToCode($op['fe_as'], $EX); 793 if (isset($op['fe_key'])) { 794 $as = str($op['fe_key'], $EX) . ' => ' . str($as); 795 } 796 echo "{$indent}foreach (" . str($op['fe_src'], $EX) . " as $as) {\n"; 797 echo $body; 798 echo "{$indent}}"; 799 // $this->outputCode($EX, $next, $last, $indent); 800 // return; 801 } 802 /* 803 if ($op['opcode'] == XC_JMPZ) { 804 $target = $op2['opline_num']; 805 if ($line + 1) { 806 $nextblock = $EX['nextbbs'][$next]; 807 $jmpop = end($nextblock); 808 if ($jmpop['opcode'] == XC_JMP) { 809 $ifendline = $op2['opline_num']; 810 if ($ifendline >= $line) { 811 $cond = $op['cond']; 812 echo "{$indent}if ($cond) {\n"; 813 $this->outputCode($EX, $next, $last, INDENT . $indent); 814 echo "$indent}\n"; 815 $this->outputCode($EX, $target, $last, $indent); 816 return; 817 } 818 } 819 } 820 } 821 */ 822 if (!isset($next)) { 823 return; 824 } 825 if (isset($op['jmpouts']) && isset($op['isjmp'])) { 826 if (isset($op['cond'])) { 827 echo "{$indent}check (" . str($op["cond"]) . ") {\n"; 828 echo INDENT; 829 } 830 switch ($op['opcode']) { 831 case XC_CONT: 832 case XC_BRK: 833 break; 834 835 case XC_GOTO: 836 echo $indent, 'goto', ' line', $op['jmpouts'][0], ';', "\n"; 837 break; 838 839 default: 840 echo $indent; 841 echo xcache_get_opcode($op['opcode']), ' line', $op['jmpouts'][0]; 842 if (isset($op['jmpouts'][1])) { 843 echo ', line', $op['jmpouts'][1]; 844 } 845 echo ";"; 846 // echo ' // <- line', $op['line']; 847 echo "\n"; 848 } 849 if (isset($op['cond'])) echo "$indent}\n"; 850 } 851 852 // proces JMPZ_EX/JMPNZ_EX for AND,OR 853 $op = &$EX['opcodes'][$next]; 854 /* 855 if (isset($op['jmpins'])) { 856 foreach (array_reverse($op['jmpins']) as $fromline) { 857 $fromop = $EX['opcodes'][$fromline]; 858 switch ($fromop['opcode']) { 859 case XC_JMPZ_EX: $opstr = 'and'; break; 860 case XC_JMPNZ_EX: $opstr = 'or'; break; 861 case XC_JMPZNZ: var_dump($fromop); exit; 862 default: continue 2; 863 } 864 865 $var = $fromop['result']['var']; 866 var_dump($EX['Ts'][$var]); 867 $EX['Ts'][$var] = '(' . $fromop['and_or'] . " $opstr " . $EX['Ts'][$var] . ')'; 868 } 869 #$this->outputCode($EX, $next, $last, $indent); 870 #return; 871 } 872 */ 873 if (isset($op['cond_false'])) { 874 // $this->dumpop($op, $EX); 875 // any true comes here, so it's a "or" 876 $cond = implode(' and ', str($op['cond_false'])); 877 // var_dump($op['cond'] = $cond); 878 /* 879 $rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue; 880 unset($op['cond_true']); 881 */ 882 } 883 884 if ($loop) { 885 return array($next, $last); 886 } 887 $this->outputCode($EX, $next, $last, $indent); 888 } 889 // }}} 890 function dasmBasicBlock(&$EX, $opline, $last) // {{{ 1346 function dasmBasicBlock(&$EX, $range) // {{{ 891 1347 { 892 1348 $T = &$EX['Ts']; … … 894 1350 $lastphpop = null; 895 1351 896 for ($i = $ opline, $ic = $last + 1; $i < $ic; $i ++) {1352 for ($i = $range[0]; $i <= $range[1]; $i ++) { 897 1353 // {{{ prepair 898 1354 $op = &$opcodes[$i]; … … 918 1374 919 1375 $resvar = null; 1376 unset($curResVar); 1377 if (array_key_exists($res['var'], $T)) { 1378 $curResVar = &$T[$res['var']]; 1379 } 920 1380 if ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) { 921 1381 $istmpres = false; … … 927 1387 // echo $opname, "\n"; 928 1388 929 $call = array(&$this, $opname); 930 if (is_callable($call)) { 931 $this->usedOps[$opc] = true; 932 $this->{$opname}($op, $EX); 933 } 934 else if (isset($this->binops[$opc])) { // {{{ 935 $this->usedOps[$opc] = true; 936 $op1val = $this->getOpVal($op1, $EX, false); 937 $op2val = $this->getOpVal($op2, $EX, false); 938 $rvalue = new Decompiler_Binop($this, $op1val, $opc, $op2val); 939 $resvar = $rvalue; 940 // }}} 941 } 942 else if (isset($this->unaryops[$opc])) { // {{{ 943 $this->usedOps[$opc] = true; 944 $op1val = $this->getOpVal($op1, $EX); 945 $myop = $this->unaryops[$opc]; 946 $rvalue = $myop . str($op1val); 947 $resvar = $rvalue; 948 // }}} 949 } 950 else { 951 $covered = true; 952 switch ($opc) { 953 case XC_NEW: // {{{ 954 array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope'])); 955 $EX['object'] = (int) $res['var']; 956 $EX['called_scope'] = null; 957 $EX['fbc'] = 'new ' . unquoteName($this->getOpVal($op1, $EX), $EX); 958 if (!ZEND_ENGINE_2) { 959 $resvar = '$new object$'; 960 } 1389 $notHandled = false; 1390 switch ($opc) { 1391 case XC_NEW: // {{{ 1392 array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope'])); 1393 $EX['object'] = (int) $res['var']; 1394 $EX['called_scope'] = null; 1395 $EX['fbc'] = 'new ' . unquoteName($this->getOpVal($op1, $EX), $EX); 1396 if (!ZEND_ENGINE_2) { 1397 $resvar = '$new object$'; 1398 } 1399 break; 1400 // }}} 1401 case XC_THROW: // {{{ 1402 $resvar = 'throw ' . str($this->getOpVal($op1, $EX)); 1403 break; 1404 // }}} 1405 case XC_CLONE: // {{{ 1406 $resvar = 'clone ' . str($this->getOpVal($op1, $EX)); 1407 break; 1408 // }}} 1409 case XC_CATCH: // {{{ 1410 break; 1411 // }}} 1412 case XC_INSTANCEOF: // {{{ 1413 $resvar = str($this->getOpVal($op1, $EX)) . ' instanceof ' . str($this->getOpVal($op2, $EX)); 1414 break; 1415 // }}} 1416 case XC_FETCH_CLASS: // {{{ 1417 if ($op2['op_type'] == XC_IS_UNUSED) { 1418 switch (($ext & (defined('ZEND_FETCH_CLASS_MASK') ? ZEND_FETCH_CLASS_MASK : 0xFF))) { 1419 case ZEND_FETCH_CLASS_SELF: 1420 $class = 'self'; 1421 break; 1422 case ZEND_FETCH_CLASS_PARENT: 1423 $class = 'parent'; 1424 break; 1425 case ZEND_FETCH_CLASS_STATIC: 1426 $class = 'static'; 1427 break; 1428 } 1429 $istmpres = true; 1430 } 1431 else { 1432 $class = $this->getOpVal($op2, $EX); 1433 if (isset($op2['constant'])) { 1434 $class = $this->stripNamespace(unquoteName($class)); 1435 } 1436 } 1437 $resvar = $class; 1438 break; 1439 // }}} 1440 case XC_FETCH_CONSTANT: // {{{ 1441 if ($op1['op_type'] == XC_IS_UNUSED) { 1442 $resvar = $this->stripNamespace($op2['constant']); 961 1443 break; 962 // }}} 963 case XC_THROW: // {{{ 964 $resvar = 'throw ' . str($this->getOpVal($op1, $EX)); 1444 } 1445 1446 if ($op1['op_type'] == XC_IS_CONST) { 1447 $resvar = $this->stripNamespace($op1['constant']); 1448 } 1449 else { 1450 $resvar = $this->getOpVal($op1, $EX); 1451 } 1452 1453 $resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2, $EX)); 1454 break; 1455 // }}} 1456 // {{{ case XC_FETCH_* 1457 case XC_FETCH_R: 1458 case XC_FETCH_W: 1459 case XC_FETCH_RW: 1460 case XC_FETCH_FUNC_ARG: 1461 case XC_FETCH_UNSET: 1462 case XC_FETCH_IS: 1463 case XC_UNSET_VAR: 1464 $rvalue = $this->getOpVal($op1, $EX); 1465 if (defined('ZEND_FETCH_TYPE_MASK')) { 1466 $fetchtype = ($ext & ZEND_FETCH_TYPE_MASK); 1467 } 1468 else { 1469 $fetchtype = $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type']; 1470 } 1471 switch ($fetchtype) { 1472 case ZEND_FETCH_STATIC_MEMBER: 1473 $class = $this->getOpVal($op2, $EX); 1474 $rvalue = str($class) . '::$' . unquoteName($rvalue, $EX); 965 1475 break; 966 // }}} 967 case XC_CLONE: // {{{ 968 $resvar = 'clone ' . str($this->getOpVal($op1, $EX)); 1476 default: 1477 $name = unquoteName($rvalue, $EX); 1478 $globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($rvalue) . "]"; 1479 $rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname); 969 1480 break; 970 // }}} 971 case XC_CATCH: // {{{ 972 $resvar = 'catch (' . str($this->getOpVal($op1, $EX)) . ' ' . str($this->getOpVal($op2, $EX)) . ')'; 1481 } 1482 if ($opc == XC_UNSET_VAR) { 1483 $op['php'] = "unset(" . str($rvalue, $EX) . ")"; 1484 $lastphpop = &$op; 1485 } 1486 else if ($res['op_type'] != XC_IS_UNUSED) { 1487 $resvar = $rvalue; 1488 } 1489 break; 1490 // }}} 1491 // {{{ case XC_FETCH_DIM_* 1492 case XC_FETCH_DIM_TMP_VAR: 1493 case XC_FETCH_DIM_R: 1494 case XC_FETCH_DIM_W: 1495 case XC_FETCH_DIM_RW: 1496 case XC_FETCH_DIM_FUNC_ARG: 1497 case XC_FETCH_DIM_UNSET: 1498 case XC_FETCH_DIM_IS: 1499 case XC_ASSIGN_DIM: 1500 case XC_UNSET_DIM_OBJ: // PHP 4 only 1501 case XC_UNSET_DIM: 1502 case XC_UNSET_OBJ: 1503 $src = $this->getOpVal($op1, $EX); 1504 if (is_a($src, "Decompiler_ForeachBox")) { 1505 $src->iskey = $this->getOpVal($op2, $EX); 1506 $resvar = $src; 973 1507 break; 974 // }}} 975 case XC_INSTANCEOF: // {{{ 976 $resvar = str($this->getOpVal($op1, $EX)) . ' instanceof ' . str($this->getOpVal($op2, $EX)); 1508 } 1509 1510 if (is_a($src, "Decompiler_DimBox")) { 1511 $dimbox = $src; 1512 } 1513 else { 1514 if (!is_a($src, "Decompiler_ListBox")) { 1515 $op1val = $this->getOpVal($op1, $EX); 1516 $list = new Decompiler_List(isset($op1val) ? $op1val : '$this'); 1517 1518 $src = new Decompiler_ListBox($list); 1519 if (!isset($op1['var'])) { 1520 $this->dumpop($op, $EX); 1521 var_dump($op); 1522 die('missing var'); 1523 } 1524 $T[$op1['var']] = $src; 1525 unset($list); 1526 } 1527 $dim = new Decompiler_Dim($src); 1528 $src->obj->dims[] = &$dim; 1529 1530 $dimbox = new Decompiler_DimBox($dim); 1531 } 1532 $dim = &$dimbox->obj; 1533 $dim->offsets[] = $this->getOpVal($op2, $EX); 1534 if ($ext == ZEND_FETCH_ADD_LOCK) { 1535 $src->obj->everLocked = true; 1536 } 1537 else if ($ext == ZEND_FETCH_STANDARD) { 1538 $dim->isLast = true; 1539 } 1540 if ($opc == XC_UNSET_OBJ) { 1541 $dim->isObject = true; 1542 } 1543 unset($dim); 1544 $rvalue = $dimbox; 1545 unset($dimbox); 1546 1547 if ($opc == XC_ASSIGN_DIM) { 1548 $lvalue = $rvalue; 1549 ++ $i; 1550 $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX); 1551 $resvar = str($lvalue, $EX) . ' = ' . str($rvalue); 1552 } 1553 else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ) { 1554 $op['php'] = "unset(" . str($rvalue, $EX) . ")"; 1555 $lastphpop = &$op; 1556 } 1557 else if ($res['op_type'] != XC_IS_UNUSED) { 1558 $resvar = $rvalue; 1559 } 1560 break; 1561 // }}} 1562 case XC_ASSIGN: // {{{ 1563 $lvalue = $this->getOpVal($op1, $EX); 1564 $rvalue = $this->getOpVal($op2, $EX); 1565 if (is_a($rvalue, 'Decompiler_ForeachBox')) { 1566 $type = $rvalue->iskey ? 'fe_key' : 'fe_as'; 1567 $rvalue->obj[$type] = $lvalue; 1568 unset($T[$op2['var']]); 977 1569 break; 978 // }}} 979 case XC_FETCH_CLASS: // {{{ 980 if ($op2['op_type'] == XC_IS_UNUSED) { 981 switch (($ext & (defined('ZEND_FETCH_CLASS_MASK') ? ZEND_FETCH_CLASS_MASK : 0xFF))) { 982 case ZEND_FETCH_CLASS_SELF: 983 $class = 'self'; 984 break; 985 case ZEND_FETCH_CLASS_PARENT: 986 $class = 'parent'; 987 break; 988 case ZEND_FETCH_CLASS_STATIC: 989 $class = 'static'; 990 break; 991 } 992 $istmpres = true; 993 } 994 else { 995 $class = $this->getOpVal($op2, $EX); 996 if (isset($op2['constant'])) { 997 $class = $this->stripNamespace(unquoteName($class)); 998 } 999 } 1000 $resvar = $class; 1570 } 1571 if (is_a($rvalue, "Decompiler_DimBox")) { 1572 $dim = &$rvalue->obj; 1573 $dim->assign = $lvalue; 1574 if ($dim->isLast) { 1575 $resvar = foldToCode($dim->value, $EX); 1576 } 1577 unset($dim); 1001 1578 break; 1002 // }}} 1003 case XC_FETCH_CONSTANT: // {{{ 1004 if ($op1['op_type'] == XC_IS_UNUSED) { 1005 $resvar = $this->stripNamespace($op2['constant']); 1006 break; 1007 } 1008 1009 if ($op1['op_type'] == XC_IS_CONST) { 1010 $resvar = $this->stripNamespace($op1['constant']); 1011 } 1012 else { 1013 $resvar = $this->getOpVal($op1, $EX); 1014 } 1015 1016 $resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2, $EX)); 1017 break; 1018 // }}} 1019 // {{{ case XC_FETCH_* 1020 case XC_FETCH_R: 1021 case XC_FETCH_W: 1022 case XC_FETCH_RW: 1023 case XC_FETCH_FUNC_ARG: 1024 case XC_FETCH_UNSET: 1025 case XC_FETCH_IS: 1026 case XC_UNSET_VAR: 1027 $rvalue = $this->getOpVal($op1, $EX); 1028 if (defined('ZEND_FETCH_TYPE_MASK')) { 1029 $fetchtype = ($ext & ZEND_FETCH_TYPE_MASK); 1030 } 1031 else { 1032 $fetchtype = $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type']; 1033 } 1034 switch ($fetchtype) { 1035 case ZEND_FETCH_STATIC_MEMBER: 1036 $class = $this->getOpVal($op2, $EX); 1037 $rvalue = str($class) . '::$' . unquoteName($rvalue, $EX); 1038 break; 1039 default: 1040 $name = unquoteName($rvalue, $EX); 1041 $globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($rvalue) . "]"; 1042 $rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname); 1043 break; 1044 } 1045 if ($opc == XC_UNSET_VAR) { 1046 $op['php'] = "unset(" . str($rvalue, $EX) . ")"; 1047 $lastphpop = &$op; 1048 } 1049 else if ($res['op_type'] != XC_IS_UNUSED) { 1050 $resvar = $rvalue; 1051 } 1052 break; 1053 // }}} 1054 // {{{ case XC_FETCH_DIM_* 1055 case XC_FETCH_DIM_TMP_VAR: 1056 case XC_FETCH_DIM_R: 1057 case XC_FETCH_DIM_W: 1058 case XC_FETCH_DIM_RW: 1059 case XC_FETCH_DIM_FUNC_ARG: 1060 case XC_FETCH_DIM_UNSET: 1061 case XC_FETCH_DIM_IS: 1062 case XC_ASSIGN_DIM: 1063 case XC_UNSET_DIM_OBJ: // PHP 4 only 1064 case XC_UNSET_DIM: 1065 case XC_UNSET_OBJ: 1066 $src = $this->getOpVal($op1, $EX, false); 1067 if (is_a($src, "Decompiler_ForeachBox")) { 1068 $src->iskey = $this->getOpVal($op2, $EX); 1069 $resvar = $src; 1070 break; 1071 } 1072 1073 if (is_a($src, "Decompiler_DimBox")) { 1074 $dimbox = $src; 1075 } 1076 else { 1077 if (!is_a($src, "Decompiler_ListBox")) { 1078 $op1val = $this->getOpVal($op1, $EX, false); 1079 $list = new Decompiler_List(isset($op1val) ? $op1val : '$this'); 1080 1081 $src = new Decompiler_ListBox($list); 1082 if (!isset($op1['var'])) { 1083 $this->dumpop($op, $EX); 1084 var_dump($op); 1085 die('missing var'); 1086 } 1087 $T[$op1['var']] = $src; 1088 unset($list); 1089 } 1090 $dim = new Decompiler_Dim($src); 1091 $src->obj->dims[] = &$dim; 1092 1093 $dimbox = new Decompiler_DimBox($dim); 1094 } 1095 $dim = &$dimbox->obj; 1096 $dim->offsets[] = $this->getOpVal($op2, $EX); 1097 if ($ext == ZEND_FETCH_ADD_LOCK) { 1098 $src->obj->everLocked = true; 1099 } 1100 else if ($ext == ZEND_FETCH_STANDARD) { 1101 $dim->isLast = true; 1102 } 1103 if ($opc == XC_UNSET_OBJ) { 1104 $dim->isObject = true; 1105 } 1106 unset($dim); 1107 $rvalue = $dimbox; 1108 unset($dimbox); 1109 1110 if ($opc == XC_ASSIGN_DIM) { 1111 $lvalue = $rvalue; 1112 ++ $i; 1113 $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX); 1114 $resvar = str($lvalue, $EX) . ' = ' . str($rvalue); 1115 } 1116 else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ) { 1117 $op['php'] = "unset(" . str($rvalue, $EX) . ")"; 1118 $lastphpop = &$op; 1119 } 1120 else if ($res['op_type'] != XC_IS_UNUSED) { 1121 $resvar = $rvalue; 1122 } 1123 break; 1124 // }}} 1125 case XC_ASSIGN: // {{{ 1126 $lvalue = $this->getOpVal($op1, $EX); 1127 $rvalue = $this->getOpVal($op2, $EX, false); 1128 if (is_a($rvalue, 'Decompiler_ForeachBox')) { 1129 $type = $rvalue->iskey ? 'fe_key' : 'fe_as'; 1130 $rvalue->obj[$type] = $lvalue; 1131 unset($T[$op2['var']]); 1132 break; 1133 } 1134 if (is_a($rvalue, "Decompiler_DimBox")) { 1135 $dim = &$rvalue->obj; 1136 $dim->assign = $lvalue; 1137 if ($dim->isLast) { 1138 $resvar = foldToCode($dim->value, $EX); 1139 } 1140 unset($dim); 1141 break; 1142 } 1143 if (is_a($rvalue, 'Decompiler_Fetch')) { 1144 $src = str($rvalue->src, $EX); 1145 if ('$' . unquoteName($src) == $lvalue) { 1146 switch ($rvalue->fetchType) { 1147 case ZEND_FETCH_STATIC: 1148 $statics = &$EX['op_array']['static_variables']; 1149 if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) { 1150 $EX['uses'][] = str($lvalue); 1151 unset($statics); 1152 break 2; 1153 } 1154 unset($statics); 1155 } 1156 } 1157 } 1158 $resvar = "$lvalue = " . str($rvalue, $EX); 1159 break; 1160 // }}} 1161 case XC_ASSIGN_REF: // {{{ 1162 $lvalue = $this->getOpVal($op1, $EX); 1163 $rvalue = $this->getOpVal($op2, $EX, false); 1164 if (is_a($rvalue, 'Decompiler_Fetch')) { 1165 $src = str($rvalue->src, $EX); 1166 if ('$' . unquoteName($src) == $lvalue) { 1167 switch ($rvalue->fetchType) { 1168 case ZEND_FETCH_GLOBAL: 1169 case ZEND_FETCH_GLOBAL_LOCK: 1170 $resvar = 'global ' . $lvalue; 1171 break 2; 1172 case ZEND_FETCH_STATIC: 1173 $statics = &$EX['op_array']['static_variables']; 1174 if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) { 1175 $EX['uses'][] = '&' . str($lvalue); 1176 unset($statics); 1177 break 2; 1178 } 1179 1180 $resvar = 'static ' . $lvalue; 1181 $name = unquoteName($src); 1182 if (isset($statics[$name])) { 1183 $var = $statics[$name]; 1184 $resvar .= ' = '; 1185 $resvar .= str(value($var), $EX); 1186 } 1579 } 1580 if (is_a($rvalue, 'Decompiler_Fetch')) { 1581 $src = str($rvalue->src, $EX); 1582 if ('$' . unquoteName($src) == $lvalue) { 1583 switch ($rvalue->fetchType) { 1584 case ZEND_FETCH_STATIC: 1585 $statics = &$EX['op_array']['static_variables']; 1586 if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) { 1587 $EX['uses'][] = str($lvalue); 1187 1588 unset($statics); 1188 1589 break 2; 1189 default:1190 1590 } 1591 unset($statics); 1191 1592 } 1192 1593 } 1193 // TODO: PHP_6 global 1194 $rvalue = str($rvalue, $EX); 1195 $resvar = "$lvalue = &$rvalue"; 1594 } 1595 $resvar = new Decompiler_Binop($this, $lvalue, XC_ASSIGN, $rvalue); 1596 break; 1597 // }}} 1598 case XC_ASSIGN_REF: // {{{ 1599 $lvalue = $this->getOpVal($op1, $EX); 1600 $rvalue = $this->getOpVal($op2, $EX); 1601 if (is_a($rvalue, 'Decompiler_Fetch')) { 1602 $src = str($rvalue->src, $EX); 1603 if ('$' . unquoteName($src) == $lvalue) { 1604 switch ($rvalue->fetchType) { 1605 case ZEND_FETCH_GLOBAL: 1606 case ZEND_FETCH_GLOBAL_LOCK: 1607 $resvar = 'global ' . $lvalue; 1608 break 2; 1609 case ZEND_FETCH_STATIC: 1610 $statics = &$EX['op_array']['static_variables']; 1611 if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) { 1612 $EX['uses'][] = '&' . str($lvalue); 1613 unset($statics); 1614 break 2; 1615 } 1616 1617 $resvar = 'static ' . $lvalue; 1618 $name = unquoteName($src); 1619 if (isset($statics[$name])) { 1620 $var = $statics[$name]; 1621 $resvar .= ' = '; 1622 $resvar .= str(value($var), $EX); 1623 } 1624 unset($statics); 1625 break 2; 1626 default: 1627 } 1628 } 1629 } 1630 // TODO: PHP_6 global 1631 $resvar = new Decompiler_Binop($this, $lvalue, XC_ASSIGN_REF, $rvalue); 1632 break; 1633 // }}} 1634 // {{{ case XC_FETCH_OBJ_* 1635 case XC_FETCH_OBJ_R: 1636 case XC_FETCH_OBJ_W: 1637 case XC_FETCH_OBJ_RW: 1638 case XC_FETCH_OBJ_FUNC_ARG: 1639 case XC_FETCH_OBJ_UNSET: 1640 case XC_FETCH_OBJ_IS: 1641 case XC_ASSIGN_OBJ: 1642 $obj = $this->getOpVal($op1, $EX); 1643 if (!isset($obj)) { 1644 $obj = '$this'; 1645 } 1646 $rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2, $EX), $EX); 1647 if ($res['op_type'] != XC_IS_UNUSED) { 1648 $resvar = $rvalue; 1649 } 1650 if ($opc == XC_ASSIGN_OBJ) { 1651 ++ $i; 1652 $lvalue = $rvalue; 1653 $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX); 1654 $resvar = "$lvalue = " . str($rvalue); 1655 } 1656 break; 1657 // }}} 1658 case XC_ISSET_ISEMPTY_DIM_OBJ: 1659 case XC_ISSET_ISEMPTY_PROP_OBJ: 1660 case XC_ISSET_ISEMPTY: 1661 case XC_ISSET_ISEMPTY_VAR: // {{{ 1662 if ($opc == XC_ISSET_ISEMPTY_VAR) { 1663 $rvalue = $this->getOpVal($op1, $EX); 1664 // for < PHP_5_3 1665 if ($op1['op_type'] == XC_IS_CONST) { 1666 $rvalue = '$' . unquoteVariableName($this->getOpVal($op1, $EX)); 1667 } 1668 if ($op2['EA.type'] == ZEND_FETCH_STATIC_MEMBER) { 1669 $class = $this->getOpVal($op2, $EX); 1670 $rvalue = $class . '::' . $rvalue; 1671 } 1672 } 1673 else if ($opc == XC_ISSET_ISEMPTY) { 1674 $rvalue = $this->getOpVal($op1, $EX); 1675 } 1676 else { 1677 $container = $this->getOpVal($op1, $EX); 1678 $dim = $this->getOpVal($op2, $EX); 1679 if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) { 1680 if (!isset($container)) { 1681 $container = '$this'; 1682 } 1683 $rvalue = $container . "->" . unquoteVariableName($dim); 1684 } 1685 else { 1686 $rvalue = $container . '[' . str($dim) .']'; 1687 } 1688 } 1689 1690 switch ((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & (ZEND_ISSET|ZEND_ISEMPTY)) { 1691 case ZEND_ISSET: 1692 $rvalue = "isset(" . str($rvalue) . ")"; 1196 1693 break; 1197 // }}} 1198 // {{{ case XC_FETCH_OBJ_* 1199 case XC_FETCH_OBJ_R: 1200 case XC_FETCH_OBJ_W: 1201 case XC_FETCH_OBJ_RW: 1202 case XC_FETCH_OBJ_FUNC_ARG: 1203 case XC_FETCH_OBJ_UNSET: 1204 case XC_FETCH_OBJ_IS: 1205 case XC_ASSIGN_OBJ: 1694 case ZEND_ISEMPTY: 1695 $rvalue = "empty(" . str($rvalue) . ")"; 1696 break; 1697 } 1698 $resvar = $rvalue; 1699 break; 1700 // }}} 1701 case XC_SEND_VAR_NO_REF: 1702 case XC_SEND_VAL: 1703 case XC_SEND_REF: 1704 case XC_SEND_VAR: // {{{ 1705 $ref = ($opc == XC_SEND_REF ? '&' : ''); 1706 $EX['argstack'][] = $ref . str($this->getOpVal($op1, $EX)); 1707 break; 1708 // }}} 1709 case XC_INIT_STATIC_METHOD_CALL: 1710 case XC_INIT_METHOD_CALL: // {{{ 1711 array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope'])); 1712 if ($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) { 1206 1713 $obj = $this->getOpVal($op1, $EX); 1207 1714 if (!isset($obj)) { 1208 1715 $obj = '$this'; 1209 1716 } 1210 $rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2, $EX), $EX); 1717 if ($opc == XC_INIT_STATIC_METHOD_CALL || /* PHP4 */ isset($op1['constant'])) { 1718 $EX['object'] = null; 1719 $EX['called_scope'] = $this->stripNamespace(unquoteName($obj, $EX)); 1720 } 1721 else { 1722 $EX['object'] = $obj; 1723 $EX['called_scope'] = null; 1724 } 1211 1725 if ($res['op_type'] != XC_IS_UNUSED) { 1212 $resvar = $rvalue; 1213 } 1214 if ($opc == XC_ASSIGN_OBJ) { 1215 ++ $i; 1216 $lvalue = $rvalue; 1217 $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX); 1218 $resvar = "$lvalue = " . str($rvalue); 1219 } 1220 break; 1221 // }}} 1222 case XC_ISSET_ISEMPTY_DIM_OBJ: 1223 case XC_ISSET_ISEMPTY_PROP_OBJ: 1224 case XC_ISSET_ISEMPTY: 1225 case XC_ISSET_ISEMPTY_VAR: // {{{ 1226 if ($opc == XC_ISSET_ISEMPTY_VAR) { 1227 $rvalue = $this->getOpVal($op1, $EX); 1228 // for < PHP_5_3 1229 if ($op1['op_type'] == XC_IS_CONST) { 1230 $rvalue = '$' . unquoteVariableName($this->getOpVal($op1, $EX)); 1231 } 1232 if ($op2['EA.type'] == ZEND_FETCH_STATIC_MEMBER) { 1233 $class = $this->getOpVal($op2, $EX); 1234 $rvalue = $class . '::' . $rvalue; 1235 } 1236 } 1237 else if ($opc == XC_ISSET_ISEMPTY) { 1238 $rvalue = $this->getOpVal($op1, $EX); 1239 } 1240 else { 1241 $container = $this->getOpVal($op1, $EX); 1242 $dim = $this->getOpVal($op2, $EX); 1243 if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) { 1244 if (!isset($container)) { 1245 $container = '$this'; 1246 } 1247 $rvalue = $container . "->" . unquoteVariableName($dim); 1248 } 1249 else { 1250 $rvalue = $container . '[' . str($dim) .']'; 1251 } 1252 } 1253 1254 switch ((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & (ZEND_ISSET|ZEND_ISEMPTY)) { 1255 case ZEND_ISSET: 1256 $rvalue = "isset(" . str($rvalue) . ")"; 1257 break; 1258 case ZEND_ISEMPTY: 1259 $rvalue = "empty(" . str($rvalue) . ")"; 1260 break; 1261 } 1262 $resvar = $rvalue; 1263 break; 1264 // }}} 1265 case XC_SEND_VAR_NO_REF: 1266 case XC_SEND_VAL: 1267 case XC_SEND_REF: 1268 case XC_SEND_VAR: // {{{ 1269 $ref = ($opc == XC_SEND_REF ? '&' : ''); 1270 $EX['argstack'][] = $ref . str($this->getOpVal($op1, $EX)); 1271 break; 1272 // }}} 1273 case XC_INIT_STATIC_METHOD_CALL: 1274 case XC_INIT_METHOD_CALL: // {{{ 1275 array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope'])); 1276 if ($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) { 1277 $obj = $this->getOpVal($op1, $EX); 1278 if (!isset($obj)) { 1279 $obj = '$this'; 1280 } 1281 if ($opc == XC_INIT_STATIC_METHOD_CALL || /* PHP4 */ isset($op1['constant'])) { 1282 $EX['object'] = null; 1283 $EX['called_scope'] = $this->stripNamespace(unquoteName($obj, $EX)); 1284 } 1285 else { 1286 $EX['object'] = $obj; 1287 $EX['called_scope'] = null; 1288 } 1289 if ($res['op_type'] != XC_IS_UNUSED) { 1290 $resvar = '$obj call$'; 1291 } 1292 } 1293 else { 1294 $EX['object'] = null; 1295 $EX['called_scope'] = null; 1296 } 1297 1298 $EX['fbc'] = $this->getOpVal($op2, $EX, false); 1299 if (($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL) && !isset($EX['fbc'])) { 1300 $EX['fbc'] = '__construct'; 1301 } 1302 break; 1303 // }}} 1304 case XC_INIT_NS_FCALL_BY_NAME: 1305 case XC_INIT_FCALL_BY_NAME: // {{{ 1306 array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope'])); 1307 if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) { 1308 break; 1309 } 1726 $resvar = '$obj call$'; 1727 } 1728 } 1729 else { 1310 1730 $EX['object'] = null; 1311 1731 $EX['called_scope'] = null; 1312 $EX['fbc'] = $this->getOpVal($op2, $EX); 1732 } 1733 1734 $EX['fbc'] = $this->getOpVal($op2, $EX); 1735 if (($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL) && !isset($EX['fbc'])) { 1736 $EX['fbc'] = '__construct'; 1737 } 1738 break; 1739 // }}} 1740 case XC_INIT_NS_FCALL_BY_NAME: 1741 case XC_INIT_FCALL_BY_NAME: // {{{ 1742 array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope'])); 1743 if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) { 1313 1744 break; 1314 // }}} 1315 case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4? 1316 $EX['object'] = null; 1317 $EX['called_scope'] = null; 1318 $which = $op1['var']; 1319 $EX['fbc'] = $EX['op_array']['funcs'][$which]['name']; 1745 } 1746 $EX['object'] = null; 1747 $EX['called_scope'] = null; 1748 $EX['fbc'] = $this->getOpVal($op2, $EX); 1749 break; 1750 // }}} 1751 case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4? 1752 $EX['object'] = null; 1753 $EX['called_scope'] = null; 1754 $which = $op1['var']; 1755 $EX['fbc'] = $EX['op_array']['funcs'][$which]['name']; 1756 break; 1757 // }}} 1758 case XC_DO_FCALL_BY_FUNC: 1759 $which = $op1['var']; 1760 $fname = $EX['op_array']['funcs'][$which]['name']; 1761 $args = $this->popargs($EX, $ext); 1762 $resvar = $fname . "($args)"; 1763 break; 1764 case XC_DO_FCALL: 1765 $fname = unquoteName($this->getOpVal($op1, $EX), $EX); 1766 $args = $this->popargs($EX, $ext); 1767 $resvar = $fname . "($args)"; 1768 break; 1769 case XC_DO_FCALL_BY_NAME: // {{{ 1770 $object = null; 1771 1772 $fname = unquoteName($EX['fbc'], $EX); 1773 if (!is_int($EX['object'])) { 1774 $object = $EX['object']; 1775 } 1776 1777 $args = $this->popargs($EX, $ext); 1778 1779 $prefix = (isset($object) ? $object . '->' : '' ) 1780 . (isset($EX['called_scope']) ? $EX['called_scope'] . '::' : '' ); 1781 $resvar = $prefix 1782 . (!$prefix ? $this->stripNamespace($fname) : $fname) 1783 . "($args)"; 1784 unset($args); 1785 1786 if (is_int($EX['object'])) { 1787 $T[$EX['object']] = $resvar; 1788 $resvar = null; 1789 } 1790 list($EX['fbc'], $EX['object'], $EX['called_scope']) = array_pop($EX['arg_types_stack']); 1791 break; 1792 // }}} 1793 case XC_VERIFY_ABSTRACT_CLASS: // {{{ 1794 //unset($T[$op1['var']]); 1795 break; 1796 // }}} 1797 case XC_DECLARE_CLASS: 1798 case XC_DECLARE_INHERITED_CLASS: 1799 case XC_DECLARE_INHERITED_CLASS_DELAYED: // {{{ 1800 $key = $op1['constant']; 1801 if (!isset($this->dc['class_table'][$key])) { 1802 echo 'class not found: ', $key, 'existing classes are:', "\n"; 1803 var_dump(array_keys($this->dc['class_table'])); 1804 exit; 1805 } 1806 $class = &$this->dc['class_table'][$key]; 1807 if (!isset($class['name'])) { 1808 $class['name'] = unquoteName($this->getOpVal($op2, $EX), $EX); 1809 } 1810 if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) { 1811 $ext /= XC_SIZEOF_TEMP_VARIABLE; 1812 $class['parent'] = $T[$ext]; 1813 unset($T[$ext]); 1814 } 1815 else { 1816 $class['parent'] = null; 1817 } 1818 1819 for (;;) { 1820 if ($i + 1 <= $range[1] 1821 && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE 1822 && $opcodes[$i + 1]['op1']['var'] == $res['var']) { 1823 // continue 1824 } 1825 else if ($i + 2 <= $range[1] 1826 && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE 1827 && $opcodes[$i + 2]['op1']['var'] == $res['var'] 1828 && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) { 1829 // continue 1830 } 1831 else { 1832 break; 1833 } 1834 $this->usedOps[XC_ADD_INTERFACE] = true; 1835 1836 $fetchop = &$opcodes[$i + 1]; 1837 $interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2'], $EX), $EX)); 1838 $addop = &$opcodes[$i + 2]; 1839 $class['interfaces'][$addop['extended_value']] = $interface; 1840 unset($fetchop, $addop); 1841 $i += 2; 1842 } 1843 $this->dclass($class, $EX['indent']); 1844 echo "\n"; 1845 unset($class); 1846 break; 1847 // }}} 1848 case XC_INIT_STRING: // {{{ 1849 $resvar = "''"; 1850 break; 1851 // }}} 1852 case XC_ADD_CHAR: 1853 case XC_ADD_STRING: 1854 case XC_ADD_VAR: // {{{ 1855 $op1val = $this->getOpVal($op1, $EX); 1856 $op2val = $this->getOpVal($op2, $EX); 1857 switch ($opc) { 1858 case XC_ADD_CHAR: 1859 $op2val = value(chr(str($op2val))); 1320 1860 break; 1321 // }}} 1322 case XC_DO_FCALL_BY_FUNC: 1323 $which = $op1['var']; 1324 $fname = $EX['op_array']['funcs'][$which]['name']; 1325 $args = $this->popargs($EX, $ext); 1326 $resvar = $fname . "($args)"; 1861 case XC_ADD_STRING: 1327 1862 break; 1328 case XC_DO_FCALL: 1329 $fname = unquoteName($this->getOpVal($op1, $EX, false), $EX); 1330 $args = $this->popargs($EX, $ext); 1331 $resvar = $fname . "($args)"; 1863 case XC_ADD_VAR: 1332 1864 break; 1333 case XC_DO_FCALL_BY_NAME: // {{{ 1334 $object = null; 1335 1336 $fname = unquoteName($EX['fbc'], $EX); 1337 if (!is_int($EX['object'])) { 1338 $object = $EX['object']; 1339 } 1340 1341 $args = $this->popargs($EX, $ext); 1342 1343 $prefix = (isset($object) ? $object . '->' : '' ) 1344 . (isset($EX['called_scope']) ? $EX['called_scope'] . '::' : '' ); 1345 $resvar = $prefix 1346 . (!$prefix ? $this->stripNamespace($fname) : $fname) 1347 . "($args)"; 1348 unset($args); 1349 1350 if (is_int($EX['object'])) { 1351 $T[$EX['object']] = $resvar; 1352 $resvar = null; 1353 } 1354 list($EX['fbc'], $EX['object'], $EX['called_scope']) = array_pop($EX['arg_types_stack']); 1355 break; 1356 // }}} 1357 case XC_VERIFY_ABSTRACT_CLASS: // {{{ 1358 //unset($T[$op1['var']]); 1359 break; 1360 // }}} 1361 case XC_DECLARE_CLASS: 1362 case XC_DECLARE_INHERITED_CLASS: 1363 case XC_DECLARE_INHERITED_CLASS_DELAYED: // {{{ 1364 $key = $op1['constant']; 1365 if (!isset($this->dc['class_table'][$key])) { 1366 echo 'class not found: ', $key, 'existing classes are:', "\n"; 1367 var_dump(array_keys($this->dc['class_table'])); 1368 exit; 1369 } 1370 $class = &$this->dc['class_table'][$key]; 1371 if (!isset($class['name'])) { 1372 $class['name'] = unquoteName($this->getOpVal($op2, $EX), $EX); 1373 } 1374 if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) { 1375 $ext /= XC_SIZEOF_TEMP_VARIABLE; 1376 $class['parent'] = $T[$ext]; 1377 unset($T[$ext]); 1865 } 1866 if (str($op1val) == "''") { 1867 $rvalue = $op2val; 1868 } 1869 else if (str($op2val) == "''") { 1870 $rvalue = $op1val; 1871 } 1872 else { 1873 $rvalue = str($op1val) . ' . ' . str($op2val); 1874 } 1875 $resvar = $rvalue; 1876 // }}} 1877 break; 1878 case XC_PRINT: // {{{ 1879 $op1val = $this->getOpVal($op1, $EX); 1880 $resvar = "print(" . str($op1val) . ")"; 1881 break; 1882 // }}} 1883 case XC_ECHO: // {{{ 1884 $op1val = $this->getOpVal($op1, $EX); 1885 $resvar = "echo " . str($op1val); 1886 break; 1887 // }}} 1888 case XC_EXIT: // {{{ 1889 $op1val = $this->getOpVal($op1, $EX); 1890 $resvar = "exit($op1val)"; 1891 break; 1892 // }}} 1893 case XC_INIT_ARRAY: 1894 case XC_ADD_ARRAY_ELEMENT: // {{{ 1895 $rvalue = $this->getOpVal($op1, $EX, true); 1896 1897 if ($opc == XC_ADD_ARRAY_ELEMENT) { 1898 $assoc = $this->getOpVal($op2, $EX); 1899 if (isset($assoc)) { 1900 $curResVar->value[] = array($assoc, $rvalue); 1378 1901 } 1379 1902 else { 1380 $c lass['parent'] = null;1381 } 1382 1383 for (;;){1384 if ($i + 1 < $ic1385 && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE1386 && $opcodes[$i + 1]['op1']['var'] == $res['var']) {1387 // continue1903 $curResVar->value[] = array(null, $rvalue); 1904 } 1905 } 1906 else { 1907 if ($opc == XC_INIT_ARRAY) { 1908 $resvar = new Decompiler_Array(); 1909 if (!isset($rvalue)) { 1910 continue; 1388 1911 } 1389 else if ($i + 2 < $ic 1390 && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE 1391 && $opcodes[$i + 2]['op1']['var'] == $res['var'] 1392 && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) { 1393 // continue 1394 } 1395 else { 1396 break; 1397 } 1398 $this->usedOps[XC_ADD_INTERFACE] = true; 1399 1400 $fetchop = &$opcodes[$i + 1]; 1401 $interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2'], $EX), $EX)); 1402 $addop = &$opcodes[$i + 2]; 1403 $class['interfaces'][$addop['extended_value']] = $interface; 1404 unset($fetchop, $addop); 1405 $i += 2; 1406 } 1407 $this->dclass($class); 1408 echo "\n"; 1409 unset($class); 1410 break; 1411 // }}} 1412 case XC_INIT_STRING: // {{{ 1413 $resvar = "''"; 1414 break; 1415 // }}} 1416 case XC_ADD_CHAR: 1417 case XC_ADD_STRING: 1418 case XC_ADD_VAR: // {{{ 1912 } 1913 1914 $assoc = $this->getOpVal($op2, $EX); 1915 if (isset($assoc)) { 1916 $resvar->value[] = array($assoc, $rvalue); 1917 } 1918 else { 1919 $resvar->value[] = array(null, $rvalue); 1920 } 1921 } 1922 break; 1923 // }}} 1924 case XC_QM_ASSIGN: // {{{ 1925 if (isset($curResVar) && is_a($curResVar, 'Decompiler_Binop')) { 1926 $curResVar->op2 = $this->getOpVal($op1, $EX); 1927 } 1928 else { 1929 $resvar = $this->getOpVal($op1, $EX); 1930 } 1931 break; 1932 // }}} 1933 case XC_BOOL: // {{{ 1934 $resvar = /*'(bool) ' .*/ $this->getOpVal($op1, $EX); 1935 break; 1936 // }}} 1937 case XC_RETURN: // {{{ 1938 $resvar = "return " . str($this->getOpVal($op1, $EX)); 1939 break; 1940 // }}} 1941 case XC_INCLUDE_OR_EVAL: // {{{ 1942 $type = $op2['var']; // hack 1943 $keyword = $this->includeTypes[$type]; 1944 $resvar = "$keyword " . str($this->getOpVal($op1, $EX)); 1945 break; 1946 // }}} 1947 case XC_FE_RESET: // {{{ 1948 $resvar = $this->getOpVal($op1, $EX); 1949 break; 1950 // }}} 1951 case XC_FE_FETCH: // {{{ 1952 $op['fe_src'] = $this->getOpVal($op1, $EX, true); 1953 $fe = new Decompiler_ForeachBox($op); 1954 $fe->iskey = false; 1955 $T[$res['var']] = $fe; 1956 1957 ++ $i; 1958 if (($ext & ZEND_FE_FETCH_WITH_KEY)) { 1959 $fe = new Decompiler_ForeachBox($op); 1960 $fe->iskey = true; 1961 1962 $res = $opcodes[$i]['result']; 1963 $T[$res['var']] = $fe; 1964 } 1965 break; 1966 // }}} 1967 case XC_SWITCH_FREE: // {{{ 1968 break; 1969 // }}} 1970 case XC_FREE: // {{{ 1971 $free = $T[$op1['var']]; 1972 if (!is_a($free, 'Decompiler_Array') && !is_a($free, 'Decompiler_Box')) { 1973 $op['php'] = is_object($free) ? $free : $this->unquote($free, '(', ')'); 1974 $lastphpop = &$op; 1975 } 1976 unset($T[$op1['var']], $free); 1977 break; 1978 // }}} 1979 case XC_JMP_NO_CTOR: 1980 break; 1981 case XC_JMP_SET: // ?: 1982 $resvar = new Decompiler_Binop($this, $this->getOpVal($op1, $EX), XC_JMP_SET, null); 1983 break; 1984 case XC_JMPZ_EX: // and 1985 case XC_JMPNZ_EX: // or 1986 $resvar = $this->getOpVal($op1, $EX); 1987 break; 1988 1989 case XC_JMPNZ: // while 1990 case XC_JMPZNZ: // for 1991 case XC_JMPZ: // {{{ 1992 break; 1993 // }}} 1994 case XC_CONT: 1995 case XC_BRK: 1996 $resvar = $opc == XC_CONT ? 'continue' : 'break'; 1997 $count = str($this->getOpVal($op2, $EX)); 1998 if ($count != '1') { 1999 $resvar .= ' ' . $count; 2000 } 2001 break; 2002 case XC_GOTO: 2003 $resvar = 'goto label' . $op['op1']['var']; 2004 $istmpres = false; 2005 break; 2006 2007 case XC_JMP: // {{{ 2008 break; 2009 // }}} 2010 case XC_CASE: 2011 // $switchValue = $this->getOpVal($op1, $EX); 2012 $caseValue = $this->getOpVal($op2, $EX); 2013 $resvar = $caseValue; 2014 break; 2015 case XC_RECV_INIT: 2016 case XC_RECV: 2017 $offset = $this->getOpVal($op1, $EX); 2018 $lvalue = $this->getOpVal($op['result'], $EX); 2019 if ($opc == XC_RECV_INIT) { 2020 $default = value($op['op2']['constant']); 2021 } 2022 else { 2023 $default = null; 2024 } 2025 $EX['recvs'][str($offset)] = array($lvalue, $default); 2026 break; 2027 case XC_POST_DEC: 2028 case XC_POST_INC: 2029 case XC_POST_DEC_OBJ: 2030 case XC_POST_INC_OBJ: 2031 case XC_PRE_DEC: 2032 case XC_PRE_INC: 2033 case XC_PRE_DEC_OBJ: 2034 case XC_PRE_INC_OBJ: // {{{ 2035 $flags = array_flip(explode('_', $opname)); 2036 if (isset($flags['OBJ'])) { 2037 $resvar = $this->getOpVal($op1, $EX) . '->' . unquoteVariableName($this->getOpVal($op2, $EX), $EX); 2038 } 2039 else { 2040 $resvar = $this->getOpVal($op1, $EX); 2041 } 2042 $opstr = isset($flags['DEC']) ? '--' : '++'; 2043 if (isset($flags['POST'])) { 2044 $resvar .= $opstr; 2045 } 2046 else { 2047 $resvar = "$opstr$resvar"; 2048 } 2049 break; 2050 // }}} 2051 2052 case XC_BEGIN_SILENCE: // {{{ 2053 $EX['silence'] ++; 2054 break; 2055 // }}} 2056 case XC_END_SILENCE: // {{{ 2057 $EX['silence'] --; 2058 $lastresvar = '@' . str($lastresvar, $EX); 2059 break; 2060 // }}} 2061 case XC_CAST: // {{{ 2062 $type = $ext; 2063 static $type2cast = array( 2064 IS_LONG => '(int)', 2065 IS_DOUBLE => '(double)', 2066 IS_STRING => '(string)', 2067 IS_ARRAY => '(array)', 2068 IS_OBJECT => '(object)', 2069 IS_BOOL => '(bool)', 2070 IS_NULL => '(unset)', 2071 ); 2072 assert(isset($type2cast[$type])); 2073 $cast = $type2cast[$type]; 2074 $resvar = $cast . ' ' . $this->getOpVal($op1, $EX); 2075 break; 2076 // }}} 2077 case XC_EXT_STMT: 2078 case XC_EXT_FCALL_BEGIN: 2079 case XC_EXT_FCALL_END: 2080 case XC_EXT_NOP: 2081 break; 2082 case XC_DECLARE_FUNCTION: 2083 $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']); 2084 break; 2085 case XC_DECLARE_LAMBDA_FUNCTION: // {{{ 2086 ob_start(); 2087 $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']); 2088 $resvar = ob_get_clean(); 2089 $istmpres = true; 2090 break; 2091 // }}} 2092 case XC_DECLARE_CONST: 2093 $name = $this->stripNamespace(unquoteName($this->getOpVal($op1, $EX), $EX)); 2094 $value = str($this->getOpVal($op2, $EX)); 2095 $resvar = 'const ' . $name . ' = ' . $value; 2096 break; 2097 case XC_DECLARE_FUNCTION_OR_CLASS: 2098 /* always removed by compiler */ 2099 break; 2100 case XC_TICKS: 2101 $lastphpop['ticks'] = $this->getOpVal($op1, $EX); 2102 // $EX['tickschanged'] = true; 2103 break; 2104 case XC_RAISE_ABSTRACT_ERROR: 2105 // abstract function body is empty, don't need this code 2106 break; 2107 case XC_USER_OPCODE: 2108 echo '// ZEND_USER_OPCODE, impossible to decompile'; 2109 break; 2110 case XC_OP_DATA: 2111 break; 2112 default: // {{{ 2113 $call = array(&$this, $opname); 2114 if (is_callable($call)) { 2115 $this->usedOps[$opc] = true; 2116 $this->{$opname}($op, $EX); 2117 } 2118 else if (isset($this->binops[$opc])) { // {{{ 2119 $this->usedOps[$opc] = true; 1419 2120 $op1val = $this->getOpVal($op1, $EX); 1420 2121 $op2val = $this->getOpVal($op2, $EX); 1421 switch ($opc) { 1422 case XC_ADD_CHAR: 1423 $op2val = value(chr(str($op2val))); 1424 break; 1425 case XC_ADD_STRING: 1426 break; 1427 case XC_ADD_VAR: 1428 break; 1429 } 1430 if (str($op1val) == "''") { 1431 $rvalue = $op2val; 1432 } 1433 else if (str($op2val) == "''") { 1434 $rvalue = $op1val; 1435 } 1436 else { 1437 $rvalue = str($op1val) . ' . ' . str($op2val); 1438 } 2122 $rvalue = new Decompiler_Binop($this, $op1val, $opc, $op2val); 1439 2123 $resvar = $rvalue; 1440 2124 // }}} 1441 break; 1442 case XC_PRINT: // {{{ 2125 } 2126 else if (isset($this->unaryops[$opc])) { // {{{ 2127 $this->usedOps[$opc] = true; 1443 2128 $op1val = $this->getOpVal($op1, $EX); 1444 $resvar = "print(" . str($op1val) . ")"; 1445 break; 2129 $myop = $this->unaryops[$opc]; 2130 $rvalue = $myop . str($op1val); 2131 $resvar = $rvalue; 1446 2132 // }}} 1447 case XC_ECHO: // {{{ 1448 $op1val = $this->getOpVal($op1, $EX); 1449 $resvar = "echo " . str($op1val); 1450 break; 1451 // }}} 1452 case XC_EXIT: // {{{ 1453 $op1val = $this->getOpVal($op1, $EX); 1454 $resvar = "exit($op1val)"; 1455 break; 1456 // }}} 1457 case XC_INIT_ARRAY: 1458 case XC_ADD_ARRAY_ELEMENT: // {{{ 1459 $rvalue = $this->getOpVal($op1, $EX, false, true); 1460 1461 if ($opc == XC_ADD_ARRAY_ELEMENT) { 1462 $assoc = $this->getOpVal($op2, $EX); 1463 if (isset($assoc)) { 1464 $T[$res['var']]->value[] = array($assoc, $rvalue); 1465 } 1466 else { 1467 $T[$res['var']]->value[] = array(null, $rvalue); 1468 } 1469 } 1470 else { 1471 if ($opc == XC_INIT_ARRAY) { 1472 $resvar = new Decompiler_Array(); 1473 if (!isset($rvalue)) { 1474 continue; 1475 } 1476 } 1477 1478 $assoc = $this->getOpVal($op2, $EX); 1479 if (isset($assoc)) { 1480 $resvar->value[] = array($assoc, $rvalue); 1481 } 1482 else { 1483 $resvar->value[] = array(null, $rvalue); 1484 } 1485 } 1486 break; 1487 // }}} 1488 case XC_QM_ASSIGN: // {{{ 1489 $resvar = $this->getOpVal($op1, $EX); 1490 break; 1491 // }}} 1492 case XC_BOOL: // {{{ 1493 $resvar = /*'(bool) ' .*/ $this->getOpVal($op1, $EX); 1494 break; 1495 // }}} 1496 case XC_RETURN: // {{{ 1497 $resvar = "return " . str($this->getOpVal($op1, $EX)); 1498 break; 1499 // }}} 1500 case XC_INCLUDE_OR_EVAL: // {{{ 1501 $type = $op2['var']; // hack 1502 $keyword = $this->includeTypes[$type]; 1503 $resvar = "$keyword " . str($this->getOpVal($op1, $EX)); 1504 break; 1505 // }}} 1506 case XC_FE_RESET: // {{{ 1507 $resvar = $this->getOpVal($op1, $EX); 1508 break; 1509 // }}} 1510 case XC_FE_FETCH: // {{{ 1511 $op['fe_src'] = $this->getOpVal($op1, $EX); 1512 $fe = new Decompiler_ForeachBox($op); 1513 $fe->iskey = false; 1514 $T[$res['var']] = $fe; 1515 1516 ++ $i; 1517 if (($ext & ZEND_FE_FETCH_WITH_KEY)) { 1518 $fe = new Decompiler_ForeachBox($op); 1519 $fe->iskey = true; 1520 1521 $res = $opcodes[$i]['result']; 1522 $T[$res['var']] = $fe; 1523 } 1524 break; 1525 // }}} 1526 case XC_SWITCH_FREE: // {{{ 1527 // unset($T[$op1['var']]); 1528 break; 1529 // }}} 1530 case XC_FREE: // {{{ 1531 $free = $T[$op1['var']]; 1532 if (!is_a($free, 'Decompiler_Array') && !is_a($free, 'Decompiler_Box')) { 1533 $op['php'] = is_object($free) ? $free : $this->unquote($free, '(', ')'); 1534 $lastphpop = &$op; 1535 } 1536 unset($T[$op1['var']], $free); 1537 break; 1538 // }}} 1539 case XC_JMP_NO_CTOR: 1540 break; 1541 case XC_JMP_SET: // ?: 1542 $resvar = $this->getOpVal($op1, $EX); 1543 $op['cond'] = $resvar; 1544 $op['isjmp'] = true; 1545 break; 1546 case XC_JMPNZ: // while 1547 case XC_JMPZNZ: // for 1548 case XC_JMPZ_EX: // and 1549 case XC_JMPNZ_EX: // or 1550 case XC_JMPZ: // {{{ 1551 if ($opc == XC_JMP_NO_CTOR && $EX['object']) { 1552 $rvalue = $EX['object']; 1553 } 1554 else { 1555 $rvalue = $this->getOpVal($op1, $EX); 1556 } 1557 1558 if (isset($op['cond_true'])) { 1559 // any true comes here, so it's a "or" 1560 $rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue; 1561 unset($op['cond_true']); 1562 } 1563 if (isset($op['cond_false'])) { 1564 echo "TODO(cond_false):\n"; 1565 var_dump($op);// exit; 1566 } 1567 if ($opc == XC_JMPZ_EX || $opc == XC_JMPNZ_EX) { 1568 $targetop = &$EX['opcodes'][$op2['opline_num']]; 1569 if ($opc == XC_JMPNZ_EX) { 1570 $targetop['cond_true'][] = foldToCode($rvalue, $EX); 1571 } 1572 else { 1573 $targetop['cond_false'][] = foldToCode($rvalue, $EX); 1574 } 1575 unset($targetop); 1576 } 1577 else { 1578 $op['cond'] = $rvalue; 1579 $op['isjmp'] = true; 1580 } 1581 break; 1582 // }}} 1583 case XC_CONT: 1584 case XC_BRK: 1585 $op['cond'] = null; 1586 $op['isjmp'] = true; 1587 $resvar = $opc == XC_CONT ? 'continue' : 'break'; 1588 $count = str($this->getOpVal($op2, $EX)); 1589 if ($count != '1') { 1590 $resvar .= ' ' . $count; 1591 } 1592 break; 1593 case XC_GOTO: 1594 case XC_JMP: // {{{ 1595 $op['cond'] = null; 1596 $op['isjmp'] = true; 1597 break; 1598 // }}} 1599 case XC_CASE: 1600 $switchValue = $this->getOpVal($op1, $EX); 1601 $caseValue = $this->getOpVal($op2, $EX); 1602 $resvar = str($switchValue) . ' == ' . str($caseValue); 1603 break; 1604 case XC_RECV_INIT: 1605 case XC_RECV: 1606 $offset = $this->getOpVal($op1, $EX); 1607 $lvalue = $this->getOpVal($op['result'], $EX); 1608 if ($opc == XC_RECV_INIT) { 1609 $default = value($op['op2']['constant']); 1610 } 1611 else { 1612 $default = null; 1613 } 1614 $EX['recvs'][str($offset)] = array($lvalue, $default); 1615 break; 1616 case XC_POST_DEC: 1617 case XC_POST_INC: 1618 case XC_POST_DEC_OBJ: 1619 case XC_POST_INC_OBJ: 1620 case XC_PRE_DEC: 1621 case XC_PRE_INC: 1622 case XC_PRE_DEC_OBJ: 1623 case XC_PRE_INC_OBJ: // {{{ 1624 $flags = array_flip(explode('_', $opname)); 1625 if (isset($flags['OBJ'])) { 1626 $resvar = $this->getOpVal($op1, $EX) . '->' . unquoteVariableName($this->getOpVal($op2, $EX), $EX); 1627 } 1628 else { 1629 $resvar = $this->getOpVal($op1, $EX); 1630 } 1631 $opstr = isset($flags['DEC']) ? '--' : '++'; 1632 if (isset($flags['POST'])) { 1633 $resvar .= $opstr; 1634 } 1635 else { 1636 $resvar = "$opstr$resvar"; 1637 } 1638 break; 1639 // }}} 1640 1641 case XC_BEGIN_SILENCE: // {{{ 1642 $EX['silence'] ++; 1643 break; 1644 // }}} 1645 case XC_END_SILENCE: // {{{ 1646 $EX['silence'] --; 1647 $lastresvar = '@' . str($lastresvar, $EX); 1648 break; 1649 // }}} 1650 case XC_CAST: // {{{ 1651 $type = $ext; 1652 static $type2cast = array( 1653 IS_LONG => '(int)', 1654 IS_DOUBLE => '(double)', 1655 IS_STRING => '(string)', 1656 IS_ARRAY => '(array)', 1657 IS_OBJECT => '(object)', 1658 IS_BOOL => '(bool)', 1659 IS_NULL => '(unset)', 1660 ); 1661 assert(isset($type2cast[$type])); 1662 $cast = $type2cast[$type]; 1663 $resvar = $cast . ' ' . $this->getOpVal($op1, $EX); 1664 break; 1665 // }}} 1666 case XC_EXT_STMT: 1667 case XC_EXT_FCALL_BEGIN: 1668 case XC_EXT_FCALL_END: 1669 case XC_EXT_NOP: 1670 break; 1671 case XC_DECLARE_FUNCTION: 1672 $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']); 1673 break; 1674 case XC_DECLARE_LAMBDA_FUNCTION: // {{{ 1675 ob_start(); 1676 $this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']); 1677 $resvar = ob_get_clean(); 1678 $istmpres = true; 1679 break; 1680 // }}} 1681 case XC_DECLARE_CONST: 1682 $name = $this->stripNamespace(unquoteName($this->getOpVal($op1, $EX), $EX)); 1683 $value = str($this->getOpVal($op2, $EX)); 1684 $resvar = 'const ' . $name . ' = ' . $value; 1685 break; 1686 case XC_DECLARE_FUNCTION_OR_CLASS: 1687 /* always removed by compiler */ 1688 break; 1689 case XC_TICKS: 1690 $lastphpop['ticks'] = $this->getOpVal($op1, $EX); 1691 // $EX['tickschanged'] = true; 1692 break; 1693 case XC_RAISE_ABSTRACT_ERROR: 1694 // abstract function body is empty, don't need this code 1695 break; 1696 case XC_USER_OPCODE: 1697 echo '// ZEND_USER_OPCODE, impossible to decompile'; 1698 break; 1699 case XC_OP_DATA: 1700 break; 1701 default: // {{{ 1702 echo "\x1B[31m * TODO ", $opname, "\x1B[0m\n"; 1703 $covered = false; 1704 // }}} 1705 } 1706 if ($covered) { 1707 $this->usedOps[$opc] = true; 1708 } 1709 } 2133 } 2134 else { 2135 $notHandled = true; 2136 } 2137 // }}} 2138 } 2139 if ($notHandled) { 2140 echo "\x1B[31m * TODO ", $opname, "\x1B[0m\n"; 2141 } 2142 else { 2143 $this->usedOps[$opc] = true; 2144 } 2145 1710 2146 if (isset($resvar)) { 1711 2147 if ($istmpres) { … … 1750 2186 function dumpop($op, &$EX) // {{{ 1751 2187 { 2188 assert('isset($op)'); 1752 2189 $op1 = $op['op1']; 1753 2190 $op2 = $op['op2']; … … 1781 2218 if ($k == 'result') { 1782 2219 var_dump($op); 2220 assert(0); 1783 2221 exit; 1784 assert(0);1785 2222 } 1786 2223 else { … … 1790 2227 } 1791 2228 $d[';'] = $op['extended_value']; 2229 if (!empty($op['jmpouts'])) { 2230 $d['>>'] = implode(',', $op['jmpouts']); 2231 } 2232 if (!empty($op['jmpins'])) { 2233 $d['<<'] = implode(',', $op['jmpins']); 2234 } 1792 2235 1793 2236 foreach ($d as $k => $v) { … … 1797 2240 } 1798 2241 // }}} 1799 function dargs(&$EX, $indent) // {{{ 2242 function dumpRange(&$EX, $range) // {{{ 2243 { 2244 for ($i = $range[0]; $i <= $range[1]; ++$i) { 2245 echo $EX['indent'], $i, "\t"; $this->dumpop($EX['opcodes'][$i], $EX); 2246 } 2247 echo $EX['indent'], "==", PHP_EOL; 2248 } 2249 // }}} 2250 function dargs(&$EX) // {{{ 1800 2251 { 1801 2252 $op_array = &$EX['op_array']; … … 1857 2308 } 1858 2309 } 1859 echo str($arg[0], $ indent);2310 echo str($arg[0], $EX); 1860 2311 } 1861 2312 if (isset($arg[1])) { 1862 echo ' = ', str($arg[1], $ indent);1863 } 1864 } 1865 } 1866 // }}} 1867 function duses(&$EX , $indent) // {{{2313 echo ' = ', str($arg[1], $EX); 2314 } 2315 } 2316 } 2317 // }}} 2318 function duses(&$EX) // {{{ 1868 2319 { 1869 2320 if ($EX['uses']) { … … 1872 2323 } 1873 2324 // }}} 1874 function dfunction($func, $indent = '', $ nobody = false) // {{{2325 function dfunction($func, $indent = '', $decorations = array(), $nobody = false) // {{{ 1875 2326 { 1876 2327 $this->detectNamespace($func['op_array']['function_name']); … … 1884 2335 else { 1885 2336 ob_start(); 1886 $newindent = INDENT . $indent; 1887 $EX = &$this->dop_array($func['op_array'], $newindent); 2337 $EX = &$this->dop_array($func['op_array'], $indent . INDENT); 1888 2338 $body = ob_get_clean(); 1889 2339 } 1890 2340 1891 2341 $functionName = $this->stripNamespace($func['op_array']['function_name']); 2342 $isExpression = false; 1892 2343 if ($functionName == '{closure}') { 1893 2344 $functionName = ''; 2345 $isExpression = true; 2346 } 2347 echo $isExpression ? '' : $indent; 2348 if ($decorations) { 2349 echo implode(' ', $decorations), ' '; 1894 2350 } 1895 2351 echo 'function', $functionName ? ' ' . $functionName : '', '('; 1896 $this->dargs($EX , $indent);2352 $this->dargs($EX); 1897 2353 echo ")"; 1898 $this->duses($EX , $indent);2354 $this->duses($EX); 1899 2355 if ($nobody) { 1900 2356 echo ";\n"; 1901 2357 } 1902 2358 else { 1903 if ( $functionName !== '') {2359 if (!$isExpression) { 1904 2360 echo "\n"; 1905 2361 echo $indent, "{\n"; … … 1911 2367 echo $body; 1912 2368 echo "$indent}"; 1913 if ( $functionName !== '') {2369 if (!$isExpression) { 1914 2370 echo "\n"; 1915 2371 } … … 1927 2383 echo "\n"; 1928 2384 } 1929 $isinterface = false; 2385 $isInterface = false; 2386 $decorations = array(); 1930 2387 if (!empty($class['ce_flags'])) { 1931 2388 if ($class['ce_flags'] & ZEND_ACC_INTERFACE) { 1932 $is interface = true;2389 $isInterface = true; 1933 2390 } 1934 2391 else { 1935 2392 if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { 1936 echo "abstract";2393 $decorations[] = "abstract"; 1937 2394 } 1938 2395 if ($class['ce_flags'] & ZEND_ACC_FINAL_CLASS) { 1939 echo "final "; 1940 } 1941 } 1942 } 1943 echo $isinterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']); 2396 $decorations[] = "final"; 2397 } 2398 } 2399 } 2400 2401 echo $indent; 2402 if ($decorations) { 2403 echo implode(' ', $decorations), ' '; 2404 } 2405 echo $isInterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']); 1944 2406 if ($class['parent']) { 1945 2407 echo ' extends ', $class['parent']; … … 2055 2517 echo "\n"; 2056 2518 } 2057 echo $newindent;2058 2519 $isAbstractMethod = false; 2520 $decorations = array(); 2059 2521 if (isset($opa['fn_flags'])) { 2060 if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$is interface) {2061 echo "abstract";2522 if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isInterface) { 2523 $decorations[] = "abstract"; 2062 2524 $isAbstractMethod = true; 2063 2525 } 2064 2526 if ($opa['fn_flags'] & ZEND_ACC_FINAL) { 2065 echo "final";2527 $decorations[] = "final"; 2066 2528 } 2067 2529 if ($opa['fn_flags'] & ZEND_ACC_STATIC) { 2068 echo "static";2530 $decorations[] = "static"; 2069 2531 } 2070 2532 2071 2533 switch ($opa['fn_flags'] & ZEND_ACC_PPP_MASK) { 2072 2534 case ZEND_ACC_PUBLIC: 2073 echo "public";2535 $decorations[] = "public"; 2074 2536 break; 2075 2537 case ZEND_ACC_PRIVATE: 2076 echo "private";2538 $decorations[] = "private"; 2077 2539 break; 2078 2540 case ZEND_ACC_PROTECTED: 2079 echo "protected";2541 $decorations[] = "protected"; 2080 2542 break; 2081 2543 default: 2082 echo "<visibility error>";2544 $decorations[] = "<visibility error>"; 2083 2545 break; 2084 2546 } 2085 2547 } 2086 $this->dfunction($func, $newindent, $ isinterface || $isAbstractMethod);2548 $this->dfunction($func, $newindent, $decorations, $isInterface || $isAbstractMethod); 2087 2549 if ($opa['function_name'] == 'Decompiler') { 2088 2550 //exit; -
branches/1.3/decompilesample.php
r784 r817 65 65 /** doc */ 66 66 protected function protectedMethod(ClassName $a, $b = array( 67 array('array')68 ))67 array('array') 68 )) 69 69 { 70 70 $runtimeArray = array('1'); … … 155 155 156 156 if ($late) { 157 class LateBindingClass 158 {} 159 160 function lateBindingFunction($arg) 161 { 162 echo 'lateFunction'; 163 } 157 class LateBindingClass 158 { 159 public function __construct() 160 { 161 } 162 } 163 164 function lateBindingFunction($arg) 165 { 166 echo 'lateFunction'; 167 } 164 168 } 165 169 … … 204 208 $a = $obj->b--; 205 209 $a = --$obj->b; 206 $a = $b xor $c;207 210 $a = !$b; 208 211 $a = $b === $c; … … 250 253 $a = (object) $b; 251 254 // PHP6+ $a = (scalar) $b; 252 $a = $b ? $c : $d; 253 $a = f1() ? f2() : f3(); 254 $a = $b and $c; 255 $a = $b or $c; 255 $a = ($b ? $c : $d); 256 $a = (f1() ? f2() : f3()); 257 ($a = $b) xor $c; 258 ($a = $b) and $c; 259 ($a = $b) or $c; 256 260 $a = $b && $c; 257 261 $a = $b || $c; 258 262 259 try { 260 echo 'outer try 1'; 263 do { 261 264 try { 262 echo 'inner try'; 263 } 264 catch (InnerException $e) { 265 echo 'outer try 1'; 266 267 try { 268 echo 'inner try'; 269 } 270 catch (InnerException $e) { 271 echo $e; 272 } 273 274 echo 'outer try 2'; 275 } 276 catch (OuterException $e) { 265 277 echo $e; 266 278 } 267 echo 'outer try 2'; 268 } 269 catch (OuterException $e) { 270 echo $e; 271 } 272 273 if ($a) { 274 echo 'if ($a)'; 275 } 276 else if ($b) { 277 echo 'else if ($b)'; 279 } while (0); 280 281 if (if_()) { 282 echo 'if'; 283 284 if (innerIf_()) { 285 echo 'if innerIf'; 286 } 287 } 288 else if (elseif_()) { 289 echo 'else if'; 290 291 if (innerIf_()) { 292 echo 'if innerIf'; 293 } 278 294 } 279 295 else { 296 if (innerIf_()) { 297 echo 'if innerIf'; 298 } 299 280 300 echo 'else'; 281 301 } … … 289 309 } while (false); 290 310 291 for ($i = 1; $i < 10; ++$i) { 311 $i = 1; 312 313 for (; $i < 10; ++$i) { 292 314 echo $i; 293 315 break; … … 295 317 296 318 foreach ($array as $key => $value) { 297 foreach ($ arrayas $key => $value) {319 foreach ($value as $key => $value) { 298 320 echo $key . ' = ' . $value . "\n"; 299 321 break 2; … … 302 324 } 303 325 304 switch ($ switch) {326 switch ($normalSwitch) { 305 327 case 'case1': 306 328 echo 'case1'; 329 330 switch ($nestedSwitch) { 331 case 1: 332 } 333 307 334 break; 308 335 … … 312 339 313 340 default: 341 switch ($nestedSwitch) { 342 case 1: 343 } 344 314 345 echo 'default'; 315 346 break; 347 } 348 349 switch ($switchWithoutDefault) { 350 case 'case1': 351 echo 'case1'; 352 break; 353 354 case 'case2': 355 echo 'case2'; 356 break; 357 } 358 359 switch ($switchWithMiddleDefault) { 360 case 'case1': 361 echo 'case1'; 362 break; 363 364 default: 365 echo 'default'; 366 break; 367 368 case 'case2': 369 echo 'case2'; 370 break; 371 } 372 373 switch ($switchWithInitialDefault) { 374 default: 375 echo 'default'; 376 break; 377 378 case 'case1': 379 echo 'case1'; 380 break; 381 382 case 'case2': 383 echo 'case2'; 384 break; 385 } 386 387 switch (emptySwitch()) { 388 } 389 390 switch (emptySwitch()) { 391 default: 316 392 } 317 393 318 394 declare (ticks=1) { 319 395 echo 1; 320 echo 2; 396 while (1) { 397 echo 2; 398 } 321 399 } 322 400 … … 336 414 $this::__construct(); 337 415 $obj::__construct(); 338 339 416 $a = $b ?: $d; 340 417 $a = ($b ?: $d) + $c; 341 418 $a = f1() ?: f2(); 419 $a = ($b ? $c : $d); 420 $a = ($b ? $c : $d) + $c; 421 $a = (f1() ? f3() : f2()); 422 423 if ($b ?: $d) { 424 echo 'if ($b ?: $d)'; 425 } 426 427 if (($b ?: $d) + $c) { 428 echo 'if (($b ?: $d) + $c)'; 429 } 430 431 if (f1() ?: f2()) { 432 echo 'if (f1() ?: f2())'; 433 } 342 434 343 435 echo 'goto a'; 344 436 goto a; 345 437 346 for ($i = 1; $i <= 2; ++$i) { 438 $i = 1; 439 440 for (; $i <= 2; ++$i) { 347 441 goto a; 348 442 } … … 350 444 a: 351 445 echo 'label a'; 352 echo preg_replace_callback('~-([a-z])~', function 446 echo preg_replace_callback('~-([a-z])~', function($match) { 353 447 return strtoupper($match[1]); 354 448 }, 'hello-world'); 355 $greet = function 449 $greet = function($name) { 356 450 printf("Hello %s\r\n", $name); 357 451 }; -
branches/1.3/processor/main.m4
r726 r817 165 165 dnl {{{ COPYNULL(1:elm) 166 166 define(`COPYNULL', ` 167 COPYNULL_EX(`dst->$1', `$ 2')DONE(`$1')167 COPYNULL_EX(`dst->$1', `$1')DONE(`$1') 168 168 ') 169 169 dnl }}} -
branches/1.3/utils.c
r784 r817 150 150 #endif 151 151 case ZEND_JMP: 152 assert(Z_OP(opline->op1).jmp_addr - op_array->opcodes < op_array->last);152 assert(Z_OP(opline->op1).jmp_addr >= op_array->opcodes && Z_OP(opline->op1).jmp_addr - op_array->opcodes < op_array->last); 153 153 Z_OP(opline->op1).opline_num = Z_OP(opline->op1).jmp_addr - op_array->opcodes; 154 154 break; … … 160 160 case ZEND_JMP_SET: 161 161 #endif 162 assert(Z_OP(opline->op2).jmp_addr - op_array->opcodes < op_array->last);162 assert(Z_OP(opline->op2).jmp_addr >= op_array->opcodes && Z_OP(opline->op2).jmp_addr - op_array->opcodes < op_array->last); 163 163 Z_OP(opline->op2).opline_num = Z_OP(opline->op2).jmp_addr - op_array->opcodes; 164 164 break;
Note: See TracChangeset
for help on using the changeset viewer.