diff --git a/Decompiler.class.php b/Decompiler.class.php index db5d332..0ee67ea 100644 --- a/Decompiler.class.php +++ b/Decompiler.class.php @@ -558,10 +558,10 @@ class Decompiler } } // }}} - function outputPhp(&$EX, $range, $indent) // {{{ + function outputPhp(&$EX, $range) // {{{ { $needBlankline = isset($EX['lastBlock']); - $origindent = $indent; + $indent = $EX['indent']; $curticks = 0; for ($i = $range[0]; $i <= $range[1]; $i ++) { $op = $EX['opcodes'][$i]; @@ -578,12 +578,12 @@ class Decompiler $oldticks = $curticks; $curticks = $toticks; if (!$curticks) { - echo $origindent, "}\n\n"; - $indent = $origindent; + echo $EX['indent'], "}\n\n"; + $indent = $EX['indent']; } else { if ($oldticks) { - echo $origindent, "}\n\n"; + echo $EX['indent'], "}\n\n"; } else if (!$oldticks) { $indent .= INDENT; @@ -592,7 +592,7 @@ class Decompiler $needBlankline = false; echo PHP_EOL; } - echo $origindent, "declare (ticks=$curticks) {\n"; + echo $EX['indent'], "declare (ticks=$curticks) {\n"; } } if ($needBlankline) { @@ -604,7 +604,7 @@ class Decompiler } } if ($curticks) { - echo $origindent, "}\n"; + echo $EX['indent'], "}\n"; } } // }}} @@ -699,13 +699,13 @@ class Decompiler return $opcodes; } // }}} - function decompileBasicBlock(&$EX, $range, $indent, $unhandled = false) // {{{ + function decompileBasicBlock(&$EX, $range, $unhandled = false) // {{{ { $this->dasmBasicBlock($EX, $range); if ($unhandled) { - $this->dumpRange($EX, $range, $indent); + $this->dumpRange($EX, $range); } - $this->outputPhp($EX, $range, $indent); + $this->outputPhp($EX, $range); } // }}} function isIfCondition(&$EX, $range) // {{{ @@ -730,6 +730,20 @@ class Decompiler unset($opcodes[$line]['jmpouts']); } // }}} + function beginScope(&$EX, $doIndent = true) // {{{ + { + array_push($EX['scopeStack'], array($EX['lastBlock'], $EX['indent'])); + if ($doIndent) { + $EX['indent'] .= INDENT; + } + $EX['lastBlock'] = null; + } + // }}} + function endScope(&$EX) // {{{ + { + list($EX['lastBlock'], $EX['indent']) = array_pop($EX['scopeStack']); + } + // }}} function beginComplexBlock(&$EX) // {{{ { if (isset($EX['lastBlock'])) { @@ -743,10 +757,11 @@ class Decompiler $EX['lastBlock'] = 'complex'; } // }}} - function decompileComplexBlock(&$EX, $range, $indent) // {{{ + function decompileComplexBlock(&$EX, $range) // {{{ { $T = &$EX['Ts']; $opcodes = &$EX['opcodes']; + $indent = $EX['indent']; $firstOp = &$opcodes[$range[0]]; $lastOp = &$opcodes[$range[1]]; @@ -759,10 +774,10 @@ class Decompiler ) { $this->removeJmpInfo($EX, $range[0]); - $this->recognizeAndDecompileClosedBlocks($EX, array($range[0], $range[0]), $indent . INDENT); + $this->recognizeAndDecompileClosedBlocks($EX, array($range[0], $range[0])); $op1 = $this->getOpVal($firstOp['result'], $EX, true); - $this->recognizeAndDecompileClosedBlocks($EX, array($range[0] + 1, $range[1]), $indent . INDENT); + $this->recognizeAndDecompileClosedBlocks($EX, array($range[0] + 1, $range[1])); $op2 = $this->getOpVal($lastOp['result'], $EX, true); $T[$firstOp['result']['var']] = new Decompiler_Binop($this, $op1, $firstOp['opcode'], $op2); @@ -781,9 +796,9 @@ class Decompiler $this->removeJmpInfo($EX, $range[0]); $condition = $this->getOpVal($firstOp['op1'], $EX); - $this->recognizeAndDecompileClosedBlocks($EX, $trueRange, $indent . INDENT); + $this->recognizeAndDecompileClosedBlocks($EX, $trueRange); $trueValue = $this->getOpVal($opcodes[$trueRange[1]]['result'], $EX, true); - $this->recognizeAndDecompileClosedBlocks($EX, $falseRange, $indent . INDENT); + $this->recognizeAndDecompileClosedBlocks($EX, $falseRange); $falseValue = $this->getOpVal($opcodes[$falseRange[1]]['result'], $EX, true); $T[$opcodes[$trueRange[1]]['result']['var']] = new Decompiler_TriOp($condition, $trueValue, $falseValue); return false; @@ -797,7 +812,7 @@ class Decompiler $firstOp['goto'] = $target; $opcodes[$target]['gofrom'][] = $range[0]; - $this->recognizeAndDecompileClosedBlocks($EX, $range, $indent); + $this->recognizeAndDecompileClosedBlocks($EX, $range); return false; } // }}} @@ -813,7 +828,8 @@ class Decompiler $this->removeJmpInfo($EX, $bodyRange[1]); $initial = ''; - $this->dasmBasicBlock($EX, $conditionRange, $indent . INDENT); + $this->beginScope($EX); + $this->dasmBasicBlock($EX, $conditionRange); $conditionCodes = array(); for ($i = $conditionRange[0]; $i <= $conditionRange[1]; ++$i) { if (isset($opcodes[$i]['php'])) { @@ -824,17 +840,23 @@ class Decompiler if (implode(',', $conditionCodes) == 'true') { $conditionCodes = array(); } + $this->endScope($EX); - $this->dasmBasicBlock($EX, $nextRange, $indent . INDENT); + $this->beginScope($EX); + $this->dasmBasicBlock($EX, $nextRange); $nextCodes = array(); for ($i = $nextRange[0]; $i <= $nextRange[1]; ++$i) { if (isset($opcodes[$i]['php'])) { $nextCodes[] = str($opcodes[$i]['php'], $EX); } } + $this->endScope($EX); + $this->beginComplexBlock($EX); echo $indent, 'for (', str($initial, $EX), '; ', implode(', ', $conditionCodes), '; ', implode(', ', $nextCodes), ') ', '{', PHP_EOL; - $this->recognizeAndDecompileClosedBlocks($EX, $bodyRange, $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, $bodyRange); + $this->endScope($EX); echo $indent, '}', PHP_EOL; $this->endComplexBlock($EX); return; @@ -851,7 +873,9 @@ class Decompiler $condition = $this->getOpVal($opcodes[$ifRange[0]]['op1'], $EX); echo $indent, $isElseIf ? 'else if' : 'if', ' (', str($condition, $EX), ') ', '{', PHP_EOL; - $this->recognizeAndDecompileClosedBlocks($EX, $ifRange, $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, $ifRange); + $this->endScope($EX); $EX['lastBlock'] = null; echo $indent, '}', PHP_EOL; @@ -872,7 +896,9 @@ class Decompiler if ($ifRange[1] < $range[1]) { $elseRange = array($ifRange[1], $range[1]); echo $indent, 'else ', '{', PHP_EOL; - $this->recognizeAndDecompileClosedBlocks($EX, $elseRange, $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, $elseRange); + $this->endScope($EX); $EX['lastBlock'] = null; echo $indent, '}', PHP_EOL; } @@ -915,18 +941,22 @@ class Decompiler $this->beginComplexBlock($EX); echo $indent, "try {", PHP_EOL; - $this->recognizeAndDecompileClosedBlocks($EX, $tryRange, $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, $tryRange); + $this->endScope($EX); echo $indent, '}', PHP_EOL; foreach ($catchBlocks as $catchFirst => $catchInfo) { list($catchOpLine, $catchBodyLast) = $catchInfo; $catchBodyFirst = $catchOpLine + 1; - $this->recognizeAndDecompileClosedBlocks($EX, array($catchFirst, $catchOpLine), $indent); + $this->dasmBasicBlock($EX, array($catchFirst, $catchOpLine)); $catchOp = &$opcodes[$catchOpLine]; echo $indent, 'catch (', str($this->getOpVal($catchOp['op1'], $EX)), ' ', str($this->getOpVal($catchOp['op2'], $EX)), ") {", PHP_EOL; unset($catchOp); $EX['lastBlock'] = null; - $this->recognizeAndDecompileClosedBlocks($EX, array($catchBodyFirst, $catchBodyLast), $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, array($catchBodyFirst, $catchBodyLast)); + $this->endScope($EX); echo $indent, '}', PHP_EOL; } $this->endComplexBlock($EX); @@ -981,10 +1011,9 @@ class Decompiler echo $indent, 'switch (', str($this->getOpVal($caseOp['op1'], $EX), $EX), ") {", PHP_EOL; $caseIsOut = false; foreach ($cases as $caseFirst => $caseLast) { - if ($caseIsOut && !empty($EX['lastBlock']) && empty($lastCaseFall)) { + if ($caseIsOut && empty($lastCaseFall)) { echo PHP_EOL; } - unset($EX['lastBlock']); $caseOp = $opcodes[$caseFirst]; @@ -1024,7 +1053,9 @@ class Decompiler $lastCaseFall = true; } - $this->recognizeAndDecompileClosedBlocks($EX, array($caseFirst, $caseLast), $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, array($caseFirst, $caseLast)); + $this->endScope($EX); $caseIsOut = true; } echo $indent, '}', PHP_EOL; @@ -1040,7 +1071,9 @@ class Decompiler $this->beginComplexBlock($EX); echo $indent, "do {", PHP_EOL; - $this->recognizeAndDecompileClosedBlocks($EX, $range, $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, $range); + $this->endScope($EX); echo $indent, "} while (", str($this->getOpVal($lastOp['op1'], $EX)), ');', PHP_EOL; $this->endComplexBlock($EX); @@ -1071,7 +1104,9 @@ class Decompiler $this->beginComplexBlock($EX); ob_start(); - $this->recognizeAndDecompileClosedBlocks($EX, $range, $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, $range); + $this->endScope($EX); $body = ob_get_clean(); echo $indent, 'while (', str($this->getOpVal($firstJmpOp['op1'], $EX)), ") {", PHP_EOL; @@ -1093,7 +1128,9 @@ class Decompiler $this->beginComplexBlock($EX); ob_start(); - $this->recognizeAndDecompileClosedBlocks($EX, $range, $indent . INDENT); + $this->beginScope($EX); + $this->recognizeAndDecompileClosedBlocks($EX, $range); + $this->endScope($EX); $body = ob_get_clean(); $as = foldToCode($firstJmpOp['fe_as'], $EX); @@ -1113,10 +1150,10 @@ class Decompiler } // }}} - $this->decompileBasicBlock($EX, $range, $indent, true); + $this->decompileBasicBlock($EX, $range, true); } // }}} - function recognizeAndDecompileClosedBlocks(&$EX, $range, $indent) // {{{ decompile in a tree way + function recognizeAndDecompileClosedBlocks(&$EX, $range) // {{{ decompile in a tree way { $opcodes = &$EX['opcodes']; @@ -1147,9 +1184,9 @@ class Decompiler if ($blockLast >= $blockFirst) { if ($blockFirst > $starti) { - $this->decompileBasicBlock($EX, array($starti, $blockFirst - 1), $indent); + $this->decompileBasicBlock($EX, array($starti, $blockFirst - 1)); } - if ($this->decompileComplexBlock($EX, array($blockFirst, $blockLast), $indent) === false) { + if ($this->decompileComplexBlock($EX, array($blockFirst, $blockLast)) === false) { if ($EX['lastBlock'] == 'complex') { echo PHP_EOL; } @@ -1167,7 +1204,7 @@ class Decompiler } } if ($starti <= $range[1]) { - $this->decompileBasicBlock($EX, array($starti, $range[1]), $indent); + $this->decompileBasicBlock($EX, array($starti, $range[1])); } } // }}} @@ -1288,6 +1325,7 @@ class Decompiler $EX['fbc'] = null; $EX['argstack'] = array(); $EX['arg_types_stack'] = array(); + $EX['scopeStack'] = array(); $EX['silence'] = 0; $EX['recvs'] = array(); $EX['uses'] = array(); @@ -1305,124 +1343,6 @@ class Decompiler return $EX; } // }}} - function outputCode(&$EX, $range, $indent, $loop = false) // {{{ - { - $op = &$EX['opcodes'][$range[0]]; - $next = $EX['nextbbs'][$range[0]]; - - $end = $next - 1; - if ($end > $range[1]) { - $end = $range[1]; - } - - if (isset($op['jmpins'])) { - echo "\nline", $op['line'], ":\n"; - } - else { - // echo ";;;\n"; - } - $this->dasmBasicBlock($EX, array($opline, $end)); - $this->outputPhp($EX, array($opline, $end), $indent); - // jmpout op - $op = &$EX['opcodes'][$end]; - $op1 = $op['op1']; - $op2 = $op['op2']; - $ext = $op['extended_value']; - $line = $op['line']; - - if (isset($EX['opcodes'][$next])) { - if (isset($range[1]) && $next > $range[1]) { - $next = null; - } - } - else { - $next = null; - } - /* - if ($op['opcode'] == XC_JMPZ) { - $target = $op2['opline_num']; - if ($line + 1) { - $nextblock = $EX['nextbbs'][$next]; - $jmpop = end($nextblock); - if ($jmpop['opcode'] == XC_JMP) { - $ifendline = $op2['opline_num']; - if ($ifendline >= $line) { - $cond = $op['cond']; - echo "{$indent}if ($cond) {\n"; - $this->outputCode($EX, $next, $range[1], INDENT . $indent); - echo "$indent}\n"; - $this->outputCode($EX, $target, $range[1], $indent); - return; - } - } - } - } - */ - if (!isset($next)) { - return; - } - if (isset($op['jmpouts']) && isset($op['isjmp'])) { - if (isset($op['cond'])) { - echo "{$indent}check (" . str($op["cond"]) . ") {\n"; - echo INDENT; - } - switch ($op['opcode']) { - case XC_CONT: - case XC_BRK: - break; - - default: - echo $indent; - echo xcache_get_opcode($op['opcode']), ' line', $op['jmpouts'][0]; - if (isset($op['jmpouts'][1])) { - echo ', line', $op['jmpouts'][1]; - } - echo ";"; - // echo ' // <- line', $op['line']; - echo "\n"; - } - if (isset($op['cond'])) echo "$indent}\n"; - } - - // proces JMPZ_EX/JMPNZ_EX for AND,OR - $op = &$EX['opcodes'][$next]; - /* - if (isset($op['jmpins'])) { - foreach (array_reverse($op['jmpins']) as $fromline) { - $fromop = $EX['opcodes'][$fromline]; - switch ($fromop['opcode']) { - case XC_JMPZ_EX: $opstr = 'and'; break; - case XC_JMPNZ_EX: $opstr = 'or'; break; - case XC_JMPZNZ: var_dump($fromop); exit; - default: continue 2; - } - - $var = $fromop['result']['var']; - var_dump($EX['Ts'][$var]); - $EX['Ts'][$var] = '(' . $fromop['and_or'] . " $opstr " . $EX['Ts'][$var] . ')'; - } - #$this->outputCode($EX, $next, $range[1], $indent); - #return; - } - */ - if (isset($op['cond_false'])) { - // $this->dumpop($op, $EX); - // any true comes here, so it's a "or" - $cond = implode(' and ', str($op['cond_false'])); - // var_dump($op['cond'] = $cond); - /* - $rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue; - unset($op['cond_true']); - */ - } - - $nextRange = array($next, $range[1]); - if ($loop) { - return $nextRange; - } - $this->outputCode($EX, $nextRange, $indent); - } - // }}} function dasmBasicBlock(&$EX, $range) // {{{ { $T = &$EX['Ts']; @@ -1920,7 +1840,7 @@ class Decompiler unset($fetchop, $addop); $i += 2; } - $this->dclass($class); + $this->dclass($class, $EX['indent']); echo "\n"; unset($class); break; @@ -2069,30 +1989,10 @@ class Decompiler case XC_JMPNZ: // while case XC_JMPZNZ: // for case XC_JMPZ: // {{{ - if ($opc == XC_JMP_NO_CTOR && $EX['object']) { - $rvalue = $EX['object']; - } - else { - $rvalue = $this->getOpVal($op1, $EX); - } - - if (isset($op['cond_true'])) { - // any true comes here, so it's a "or" - $rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue; - unset($op['cond_true']); - } - if (isset($op['cond_false'])) { - echo "TODO(cond_false):\n"; - var_dump($op);// exit; - } - $op['cond'] = $rvalue; - $op['isjmp'] = true; break; // }}} case XC_CONT: case XC_BRK: - $op['cond'] = null; - $op['isjmp'] = true; $resvar = $opc == XC_CONT ? 'continue' : 'break'; $count = str($this->getOpVal($op2, $EX)); if ($count != '1') { @@ -2105,8 +2005,6 @@ class Decompiler break; case XC_JMP: // {{{ - $op['cond'] = null; - $op['isjmp'] = true; break; // }}} case XC_CASE: @@ -2341,15 +2239,15 @@ class Decompiler echo PHP_EOL; } // }}} - function dumpRange(&$EX, $range, $indent = '') // {{{ + function dumpRange(&$EX, $range) // {{{ { for ($i = $range[0]; $i <= $range[1]; ++$i) { - echo $indent, $i, "\t"; $this->dumpop($EX['opcodes'][$i], $EX); + echo $EX['indent'], $i, "\t"; $this->dumpop($EX['opcodes'][$i], $EX); } - echo $indent, "==", PHP_EOL; + echo $EX['indent'], "==", PHP_EOL; } // }}} - function dargs(&$EX, $indent) // {{{ + function dargs(&$EX) // {{{ { $op_array = &$EX['op_array']; @@ -2409,22 +2307,22 @@ class Decompiler assert(0); } } - echo str($arg[0], $indent); + echo str($arg[0], $EX); } if (isset($arg[1])) { - echo ' = ', str($arg[1], $indent); + echo ' = ', str($arg[1], $EX); } } } // }}} - function duses(&$EX, $indent) // {{{ + function duses(&$EX) // {{{ { if ($EX['uses']) { echo ' use(', implode(', ', $EX['uses']), ')'; } } // }}} - function dfunction($func, $indent = '', $nobody = false) // {{{ + function dfunction($func, $indent = '', $decorations = array(), $nobody = false) // {{{ { $this->detectNamespace($func['op_array']['function_name']); @@ -2436,24 +2334,29 @@ class Decompiler } else { ob_start(); - $newindent = INDENT . $indent; - $EX = &$this->dop_array($func['op_array'], $newindent); + $EX = &$this->dop_array($func['op_array'], $indent . INDENT); $body = ob_get_clean(); } $functionName = $this->stripNamespace($func['op_array']['function_name']); + $isExpression = false; if ($functionName == '{closure}') { $functionName = ''; + $isExpression = true; + } + echo $isExpression ? '' : $indent; + if ($decorations) { + echo implode(' ', $decorations), ' '; } echo 'function', $functionName ? ' ' . $functionName : '', '('; - $this->dargs($EX, $indent); + $this->dargs($EX); echo ")"; - $this->duses($EX, $indent); + $this->duses($EX); if ($nobody) { echo ";\n"; } else { - if ($functionName !== '') { + if (!$isExpression) { echo "\n"; echo $indent, "{\n"; } @@ -2463,7 +2366,7 @@ class Decompiler echo $body; echo "$indent}"; - if ($functionName !== '') { + if (!$isExpression) { echo "\n"; } } @@ -2479,21 +2382,27 @@ class Decompiler echo $class['doc_comment']; echo "\n"; } - $isinterface = false; + $isInterface = false; + $decorations = array(); if (!empty($class['ce_flags'])) { if ($class['ce_flags'] & ZEND_ACC_INTERFACE) { - $isinterface = true; + $isInterface = true; } else { if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { - echo "abstract "; + $decorations[] = "abstract"; } if ($class['ce_flags'] & ZEND_ACC_FINAL_CLASS) { - echo "final "; + $decorations[] = "final"; } } } - echo $isinterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']); + + echo $indent; + if ($decorations) { + echo implode(' ', $decorations), ' '; + } + echo $isInterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']); if ($class['parent']) { echo ' extends ', $class['parent']; } @@ -2607,36 +2516,36 @@ class Decompiler echo $opa['doc_comment']; echo "\n"; } - echo $newindent; $isAbstractMethod = false; + $decorations = array(); if (isset($opa['fn_flags'])) { - if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isinterface) { - echo "abstract "; + if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isInterface) { + $decorations[] = "abstract"; $isAbstractMethod = true; } if ($opa['fn_flags'] & ZEND_ACC_FINAL) { - echo "final "; + $decorations[] = "final"; } if ($opa['fn_flags'] & ZEND_ACC_STATIC) { - echo "static "; + $decorations[] = "static"; } switch ($opa['fn_flags'] & ZEND_ACC_PPP_MASK) { case ZEND_ACC_PUBLIC: - echo "public "; + $decorations[] = "public"; break; case ZEND_ACC_PRIVATE: - echo "private "; + $decorations[] = "private"; break; case ZEND_ACC_PROTECTED: - echo "protected "; + $decorations[] = "protected"; break; default: - echo " "; + $decorations[] = ""; break; } } - $this->dfunction($func, $newindent, $isinterface || $isAbstractMethod); + $this->dfunction($func, $newindent, $decorations, $isInterface || $isAbstractMethod); if ($opa['function_name'] == 'Decompiler') { //exit; } diff --git a/decompilesample.php b/decompilesample.php index 4af8ac7..e8490ea 100644 --- a/decompilesample.php +++ b/decompilesample.php @@ -64,8 +64,8 @@ abstract class ClassName /** doc */ protected function protectedMethod(ClassName $a, $b = array( - array('array') - )) + array('array') + )) { $runtimeArray = array('1'); $runtimeArray2 = array( @@ -154,13 +154,17 @@ final class Child extends ClassName implements IInterface } if ($late) { -class LateBindingClass -{} + class LateBindingClass + { + public function __construct() + { + } + } -function lateBindingFunction($arg) -{ - echo 'lateFunction'; -} + function lateBindingFunction($arg) + { + echo 'lateFunction'; + } } echo "\r\n"; @@ -276,12 +280,14 @@ do { if (if_()) { echo 'if'; + if (innerIf_()) { echo 'if innerIf'; } } else if (elseif_()) { echo 'else if'; + if (innerIf_()) { echo 'if innerIf'; } @@ -290,6 +296,7 @@ else { if (innerIf_()) { echo 'if innerIf'; } + echo 'else'; }