/lib/Decompiler.class.php
PHP | 2817 lines | 2438 code | 174 blank | 205 comment | 561 complexity | 0baf919df9a9d92e2a64709253ecf66c MD5 | raw file
Possible License(s): BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- <?php
- define('INDENT', "\t");
- ini_set('error_reporting', E_ALL);
- function color($str, $color = 33)
- {
- return "\x1B[{$color}m$str\x1B[0m";
- }
- function str($code, $indent = '') // {{{
- {
- if (is_array($code)) {
- $array = array();
- foreach ($code as $key => $value) {
- $array[$key] = str($value, $indent);
- }
- return $array;
- }
- if (is_object($code)) {
- $code = foldToCode($code, $indent);
- return $code->toCode($indent);
- }
- return (string) $code;
- }
- // }}}
- function foldToCode($src, $indent = '') // {{{ wrap or rewrap anything to Decompiler_Code
- {
- if (is_array($indent)) {
- $indent = $indent['indent'];
- }
- if (!is_object($src)) {
- return new Decompiler_Code($src);
- }
- if (!method_exists($src, 'toCode')) {
- var_dump($src);
- exit('no toCode');
- }
- if (get_class($src) != 'Decompiler_Code') {
- // rewrap it
- $src = new Decompiler_Code($src->toCode($indent));
- }
- return $src;
- }
- // }}}
- function value($value) // {{{
- {
- $spec = xcache_get_special_value($value);
- if (isset($spec)) {
- $value = $spec;
- if (!is_array($value)) {
- // constant
- return $value;
- }
- }
- if (is_a($value, 'Decompiler_Object')) {
- // use as is
- }
- else if (is_array($value)) {
- $value = new Decompiler_ConstArray($value);
- }
- else {
- $value = new Decompiler_Value($value);
- }
- return $value;
- }
- // }}}
- function unquoteName_($str, $asVariableName, $indent = '') // {{{
- {
- $str = str($str, $indent);
- if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
- return str_replace('\\\\', '\\', substr($str, 1, -1));
- }
- else if ($asVariableName) {
- return "{" . $str . "}";
- }
- else {
- return $str;
- }
- }
- // }}}
- function unquoteVariableName($str, $indent = '') // {{{
- {
- return unquoteName_($str, true, $indent);
- }
- // }}}
- function unquoteName($str, $indent = '') // {{{
- {
- return unquoteName_($str, false, $indent);
- }
- // }}}
- class Decompiler_Object // {{{
- {
- }
- // }}}
- class Decompiler_Value extends Decompiler_Object // {{{
- {
- var $value;
- function Decompiler_Value($value = null)
- {
- $this->value = $value;
- }
- function toCode($indent)
- {
- $code = var_export($this->value, true);
- if (gettype($this->value) == 'string') {
- switch ($this->value) {
- case "\r":
- return '"\\r"';
- case "\n":
- return '"\\n"';
- case "\r\n":
- return '"\\r\\n"';
- }
- $code = str_replace("\r\n", '\' . "\\r\\n" . \'', $code);
- $code = str_replace("\r", '\' . "\\r" . \'', $code);
- $code = str_replace("\n", '\' . "\\n" . \'', $code);
- }
- return $code;
- }
- }
- // }}}
- class Decompiler_Code extends Decompiler_Object // {{{
- {
- var $src;
- function Decompiler_Code($src)
- {
- assert('isset($src)');
- $this->src = $src;
- }
- function toCode($indent)
- {
- return $this->src;
- }
- }
- // }}}
- class Decompiler_Binop extends Decompiler_Code // {{{
- {
- var $opc;
- var $op1;
- var $op2;
- var $parent;
- function Decompiler_Binop($parent, $op1, $opc, $op2)
- {
- $this->parent = &$parent;
- $this->opc = $opc;
- $this->op1 = $op1;
- $this->op2 = $op2;
- }
- function toCode($indent)
- {
- $opstr = $this->parent->binops[$this->opc];
- if (is_a($this->op1, 'Decompiler_TriOp') || is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) {
- $op1 = "(" . str($this->op1, $indent) . ")";
- }
- else {
- $op1 = $this->op1;
- }
- if (is_a($this->op2, 'Decompiler_TriOp') || is_a($this->op2, 'Decompiler_Binop') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') {
- $op2 = "(" . str($this->op2, $indent) . ")";
- }
- else {
- $op2 = $this->op2;
- }
- if (str($op1) == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
- return $opstr . str($op2, $indent);
- }
- return str($op1, $indent) . ' ' . $opstr . ($this->opc == XC_ASSIGN_REF ? '' : ' ') . str($op2, $indent);
- }
- }
- // }}}
- class Decompiler_TriOp extends Decompiler_Code // {{{
- {
- var $condition;
- var $trueValue;
- var $falseValue;
- function Decompiler_TriOp($condition, $trueValue, $falseValue)
- {
- $this->condition = $condition;
- $this->trueValue = $trueValue;
- $this->falseValue = $falseValue;
- }
- function toCode($indent)
- {
- $trueValue = $this->trueValue;
- if (is_a($this->trueValue, 'Decompiler_TriOp')) {
- $trueValue = "(" . str($trueValue, $indent) . ")";
- }
- $falseValue = $this->falseValue;
- if (is_a($this->falseValue, 'Decompiler_TriOp')) {
- $falseValue = "(" . str($falseValue, $indent) . ")";
- }
- return str($this->condition) . ' ? ' . str($trueValue) . ' : ' . str($falseValue);
- }
- }
- // }}}
- class Decompiler_Fetch extends Decompiler_Code // {{{
- {
- var $src;
- var $fetchType;
- function Decompiler_Fetch($src, $type, $globalsrc)
- {
- $this->src = $src;
- $this->fetchType = $type;
- $this->globalsrc = $globalsrc;
- }
- function toCode($indent)
- {
- switch ($this->fetchType) {
- case ZEND_FETCH_LOCAL:
- return '$' . substr($this->src, 1, -1);
- case ZEND_FETCH_STATIC:
- if (ZEND_ENGINE_2_3) {
- // closure local variable?
- return str($this->src);
- }
- die('static fetch cant to string');
- case ZEND_FETCH_GLOBAL:
- case ZEND_FETCH_GLOBAL_LOCK:
- return $this->globalsrc;
- default:
- var_dump($this->fetchType);
- assert(0);
- }
- }
- }
- // }}}
- class Decompiler_Box // {{{
- {
- var $obj;
- function Decompiler_Box(&$obj)
- {
- $this->obj = &$obj;
- }
- function toCode($indent)
- {
- return $this->obj->toCode($indent);
- }
- }
- // }}}
- class Decompiler_Dim extends Decompiler_Value // {{{
- {
- var $offsets = array();
- var $isLast = false;
- var $isObject = false;
- var $assign = null;
- function toCode($indent)
- {
- if (is_a($this->value, 'Decompiler_ListBox')) {
- $exp = str($this->value->obj->src, $indent);
- }
- else {
- $exp = str($this->value, $indent);
- }
- $last = count($this->offsets) - 1;
- foreach ($this->offsets as $i => $dim) {
- if ($this->isObject && $i == $last) {
- $exp .= '->' . unquoteVariableName($dim, $indent);
- }
- else {
- $exp .= '[' . str($dim, $indent) . ']';
- }
- }
- return $exp;
- }
- }
- // }}}
- class Decompiler_DimBox extends Decompiler_Box // {{{
- {
- }
- // }}}
- class Decompiler_List extends Decompiler_Code // {{{
- {
- var $src;
- var $dims = array();
- var $everLocked = false;
- function toCode($indent)
- {
- if (count($this->dims) == 1 && !$this->everLocked) {
- $dim = $this->dims[0];
- unset($dim->value);
- $dim->value = $this->src;
- if (!isset($dim->assign)) {
- return str($dim, $indent);
- }
- return str($this->dims[0]->assign, $indent) . ' = ' . str($dim, $indent);
- }
- /* flatten dims */
- $assigns = array();
- foreach ($this->dims as $dim) {
- $assign = &$assigns;
- foreach ($dim->offsets as $offset) {
- $assign = &$assign[$offset];
- }
- $assign = foldToCode($dim->assign, $indent);
- }
- return str($this->toList($assigns)) . ' = ' . str($this->src, $indent);
- }
- function toList($assigns)
- {
- $keys = array_keys($assigns);
- if (count($keys) < 2) {
- $keys[] = 0;
- }
- $max = call_user_func_array('max', $keys);
- $list = 'list(';
- for ($i = 0; $i <= $max; $i ++) {
- if ($i) {
- $list .= ', ';
- }
- if (!isset($assigns[$i])) {
- continue;
- }
- if (is_array($assigns[$i])) {
- $list .= $this->toList($assigns[$i]);
- }
- else {
- $list .= $assigns[$i];
- }
- }
- return $list . ')';
- }
- }
- // }}}
- class Decompiler_ListBox extends Decompiler_Box // {{{
- {
- }
- // }}}
- class Decompiler_Array extends Decompiler_Value // {{{
- {
- // emenets
- function Decompiler_Array()
- {
- $this->value = array();
- }
- function toCode($indent)
- {
- $subindent = $indent . INDENT;
- $elementsCode = array();
- $index = 0;
- foreach ($this->value as $element) {
- list($key, $value) = $element;
- if (!isset($key)) {
- $key = $index++;
- }
- $elementsCode[] = array(str($key, $subindent), str($value, $subindent), $key, $value);
- }
- $exp = "array(";
- $indent = $indent . INDENT;
- $assocWidth = 0;
- $multiline = 0;
- $i = 0;
- foreach ($elementsCode as $element) {
- list($keyCode, $valueCode) = $element;
- if ((string) $i !== $keyCode) {
- $assocWidth = 1;
- break;
- }
- ++$i;
- }
- foreach ($elementsCode as $element) {
- list($keyCode, $valueCode, $key, $value) = $element;
- if ($assocWidth) {
- $len = strlen($keyCode);
- if ($assocWidth < $len) {
- $assocWidth = $len;
- }
- }
- if (is_array($value) || is_a($value, 'Decompiler_Array')) {
- $multiline ++;
- }
- }
- $i = 0;
- foreach ($elementsCode as $element) {
- list($keyCode, $value) = $element;
- if ($multiline) {
- if ($i) {
- $exp .= ",";
- }
- $exp .= "\n";
- $exp .= $indent;
- }
- else {
- if ($i) {
- $exp .= ", ";
- }
- }
- if ($assocWidth) {
- if ($multiline) {
- $exp .= sprintf("%-{$assocWidth}s => ", $keyCode);
- }
- else {
- $exp .= $keyCode . ' => ';
- }
- }
- $exp .= $value;
- $i ++;
- }
- if ($multiline) {
- $exp .= "\n$indent)";
- }
- else {
- $exp .= ")";
- }
- return $exp;
- }
- }
- // }}}
- class Decompiler_ConstArray extends Decompiler_Array // {{{
- {
- function Decompiler_ConstArray($array)
- {
- $elements = array();
- foreach ($array as $key => $value) {
- $elements[] = array(value($key), value($value));
- }
- $this->value = $elements;
- }
- }
- // }}}
- class Decompiler_ForeachBox extends Decompiler_Box // {{{
- {
- var $iskey;
- function toCode($indent)
- {
- return 'foreach (' . '';
- }
- }
- // }}}
- class Decompiler
- {
- var $namespace;
- var $namespaceDecided;
- function Decompiler()
- {
- // {{{ testing
- // XC_UNDEF XC_OP_DATA
- $this->test = !empty($_ENV['XCACHE_DECOMPILER_TEST']);
- $this->usedOps = array();
- if ($this->test) {
- $content = file_get_contents(__FILE__);
- for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
- if (!preg_match("/\\bXC_" . $opname . "\\b(?!')/", $content)) {
- echo "not recognized opcode ", $opname, "\n";
- }
- }
- }
- // }}}
- // {{{ opinfo
- $this->unaryops = array(
- XC_BW_NOT => '~',
- XC_BOOL_NOT => '!',
- );
- $this->binops = array(
- XC_ADD => "+",
- XC_ASSIGN_ADD => "+=",
- XC_SUB => "-",
- XC_ASSIGN_SUB => "-=",
- XC_MUL => "*",
- XC_ASSIGN_MUL => "*=",
- XC_DIV => "/",
- XC_ASSIGN_DIV => "/=",
- XC_MOD => "%",
- XC_ASSIGN_MOD => "%=",
- XC_SL => "<<",
- XC_ASSIGN_SL => "<<=",
- XC_SR => ">>",
- XC_ASSIGN_SR => ">>=",
- XC_CONCAT => ".",
- XC_ASSIGN_CONCAT => ".=",
- XC_IS_IDENTICAL => "===",
- XC_IS_NOT_IDENTICAL => "!==",
- XC_IS_EQUAL => "==",
- XC_IS_NOT_EQUAL => "!=",
- XC_IS_SMALLER => "<",
- XC_IS_SMALLER_OR_EQUAL => "<=",
- XC_BW_OR => "|",
- XC_ASSIGN_BW_OR => "|=",
- XC_BW_AND => "&",
- XC_ASSIGN_BW_AND => "&=",
- XC_BW_XOR => "^",
- XC_ASSIGN_BW_XOR => "^=",
- XC_BOOL_XOR => "xor",
- XC_ASSIGN => "=",
- XC_ASSIGN_REF => "= &",
- XC_JMP_SET => "?:",
- XC_JMPZ_EX => "&&",
- XC_JMPNZ_EX => "||",
- );
- // }}}
- $this->includeTypes = array( // {{{
- ZEND_EVAL => 'eval',
- ZEND_INCLUDE => 'include',
- ZEND_INCLUDE_ONCE => 'include_once',
- ZEND_REQUIRE => 'require',
- ZEND_REQUIRE_ONCE => 'require_once',
- );
- // }}}
- }
- function detectNamespace($name) // {{{
- {
- if ($this->namespaceDecided) {
- return;
- }
- if (strpos($name, '\\') !== false) {
- $this->namespace = strtok($name, '\\');
- echo 'namespace ', $this->namespace, ";\n\n";
- }
- $this->namespaceDecided = true;
- }
- // }}}
- function stripNamespace($name) // {{{
- {
- $len = strlen($this->namespace) + 1;
- if (substr($name, 0, $len) == $this->namespace . '\\') {
- return substr($name, $len);
- }
- else {
- return $name;
- }
- }
- // }}}
- function outputPhp(&$EX, $range) // {{{
- {
- $needBlankline = isset($EX['lastBlock']);
- $indent = $EX['indent'];
- $curticks = 0;
- for ($i = $range[0]; $i <= $range[1]; $i ++) {
- $op = $EX['opcodes'][$i];
- if (isset($op['gofrom'])) {
- if ($needBlankline) {
- $needBlankline = false;
- echo PHP_EOL;
- }
- echo 'label' . $i, ":\n";
- }
- if (isset($op['php'])) {
- $toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0;
- if ($curticks != $toticks) {
- $oldticks = $curticks;
- $curticks = $toticks;
- if (!$curticks) {
- echo $EX['indent'], "}\n\n";
- $indent = $EX['indent'];
- }
- else {
- if ($oldticks) {
- echo $EX['indent'], "}\n\n";
- }
- else if (!$oldticks) {
- $indent .= INDENT;
- }
- if ($needBlankline) {
- $needBlankline = false;
- echo PHP_EOL;
- }
- echo $EX['indent'], "declare (ticks=$curticks) {\n";
- }
- }
- if ($needBlankline) {
- $needBlankline = false;
- echo PHP_EOL;
- }
- echo $indent, str($op['php'], $indent), ";\n";
- $EX['lastBlock'] = 'basic';
- }
- }
- if ($curticks) {
- echo $EX['indent'], "}\n";
- }
- }
- // }}}
- function getOpVal($op, &$EX, $free = false) // {{{
- {
- switch ($op['op_type']) {
- case XC_IS_CONST:
- return value($op['constant']);
- case XC_IS_VAR:
- case XC_IS_TMP_VAR:
- $T = &$EX['Ts'];
- $ret = $T[$op['var']];
- if ($free && empty($this->keepTs)) {
- unset($T[$op['var']]);
- }
- return $ret;
- case XC_IS_CV:
- $var = $op['var'];
- $var = $EX['op_array']['vars'][$var];
- return '$' . $var['name'];
- case XC_IS_UNUSED:
- return null;
- }
- }
- // }}}
- function removeKeyPrefix($array, $prefix) // {{{
- {
- $prefixLen = strlen($prefix);
- $ret = array();
- foreach ($array as $key => $value) {
- if (substr($key, 0, $prefixLen) == $prefix) {
- $key = substr($key, $prefixLen);
- }
- $ret[$key] = $value;
- }
- return $ret;
- }
- // }}}
- function &fixOpcode($opcodes, $removeTailing = false, $defaultReturnValue = null) // {{{
- {
- $last = count($opcodes) - 1;
- for ($i = 0; $i <= $last; $i ++) {
- if (function_exists('xcache_get_fixed_opcode')) {
- $opcodes[$i]['opcode'] = xcache_get_fixed_opcode($opcodes[$i]['opcode'], $i);
- }
- if (isset($opcodes[$i]['op1'])) {
- $opcodes[$i]['op1'] = $this->removeKeyPrefix($opcodes[$i]['op1'], 'u.');
- $opcodes[$i]['op2'] = $this->removeKeyPrefix($opcodes[$i]['op2'], 'u.');
- $opcodes[$i]['result'] = $this->removeKeyPrefix($opcodes[$i]['result'], 'u.');
- }
- else {
- $op = array(
- 'op1' => array(),
- 'op2' => array(),
- 'op3' => array(),
- );
- foreach ($opcodes[$i] as $name => $value) {
- if (preg_match('!^(op1|op2|result)\\.(.*)!', $name, $m)) {
- list(, $which, $field) = $m;
- $op[$which][$field] = $value;
- }
- else if (preg_match('!^(op1|op2|result)_type$!', $name, $m)) {
- list(, $which) = $m;
- $op[$which]['op_type'] = $value;
- }
- else {
- $op[$name] = $value;
- }
- }
- $opcodes[$i] = $op;
- }
- }
- if ($removeTailing) {
- $last = count($opcodes) - 1;
- if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
- $this->usedOps[XC_HANDLE_EXCEPTION] = true;
- $opcodes[$last]['opcode'] = XC_NOP;
- --$last;
- }
- if ($opcodes[$last]['opcode'] == XC_RETURN) {
- $op1 = $opcodes[$last]['op1'];
- if ($op1['op_type'] == XC_IS_CONST && array_key_exists('constant', $op1) && $op1['constant'] === $defaultReturnValue) {
- $opcodes[$last]['opcode'] = XC_NOP;
- --$last;
- }
- }
- }
- return $opcodes;
- }
- // }}}
- function decompileBasicBlock(&$EX, $range, $unhandled = false) // {{{
- {
- $this->dasmBasicBlock($EX, $range);
- if ($unhandled) {
- $this->dumpRange($EX, $range);
- }
- $this->outputPhp($EX, $range);
- }
- // }}}
- function isIfCondition(&$EX, $range) // {{{
- {
- $opcodes = &$EX['opcodes'];
- $firstOp = &$opcodes[$range[0]];
- return $firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts']) && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_JMP
- && !empty($opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'])
- && $opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'][0] == $range[1] + 1;
- }
- // }}}
- function removeJmpInfo(&$EX, $line) // {{{
- {
- $opcodes = &$EX['opcodes'];
- foreach ($opcodes[$line]['jmpouts'] as $jmpTo) {
- $jmpins = &$opcodes[$jmpTo]['jmpins'];
- $jmpins = array_flip($jmpins);
- unset($jmpins[$line]);
- $jmpins = array_keys($jmpins);
- }
- // $opcodes[$line]['opcode'] = XC_NOP;
- 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'])) {
- echo PHP_EOL;
- $EX['lastBlock'] = null;
- }
- }
- // }}}
- function endComplexBlock(&$EX) // {{{
- {
- $EX['lastBlock'] = 'complex';
- }
- // }}}
- function decompileComplexBlock(&$EX, $range) // {{{
- {
- $T = &$EX['Ts'];
- $opcodes = &$EX['opcodes'];
- $indent = $EX['indent'];
- $firstOp = &$opcodes[$range[0]];
- $lastOp = &$opcodes[$range[1]];
- // {{{ && || and or
- if (($firstOp['opcode'] == XC_JMPZ_EX || $firstOp['opcode'] == XC_JMPNZ_EX) && !empty($firstOp['jmpouts'])
- && $firstOp['jmpouts'][0] == $range[1] + 1
- && $lastOp['opcode'] == XC_BOOL
- && $firstOp['opcode']['result']['var'] == $lastOp['opcode']['result']['var']
- ) {
- $this->removeJmpInfo($EX, $range[0]);
- $this->recognizeAndDecompileClosedBlocks($EX, array($range[0], $range[0]));
- $op1 = $this->getOpVal($firstOp['result'], $EX, true);
- $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);
- return false;
- }
- // }}}
- // {{{ ?: excluding JMP_SET
- if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts'])
- && $range[1] >= $range[0] + 3
- && $opcodes[$firstOp['jmpouts'][0] - 2]['opcode'] == XC_QM_ASSIGN
- && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_JMP && $opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'][0] == $range[1] + 1
- && $lastOp['opcode'] == XC_QM_ASSIGN
- ) {
- $trueRange = array($range[0] + 1, $firstOp['jmpouts'][0] - 2);
- $falseRange = array($firstOp['jmpouts'][0], $range[1]);
- $this->removeJmpInfo($EX, $range[0]);
- $condition = $this->getOpVal($firstOp['op1'], $EX);
- $this->recognizeAndDecompileClosedBlocks($EX, $trueRange);
- $trueValue = $this->getOpVal($opcodes[$trueRange[1]]['result'], $EX, true);
- $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;
- }
- // }}}
- // {{{ goto
- if ($firstOp['opcode'] == XC_JMP && !empty($firstOp['jmpouts']) && $firstOp['jmpouts'][0] == $range[1] + 1) {
- $this->removeJmpInfo($EX, $range[0]);
- $firstOp['opcode'] = XC_GOTO;
- $target = $firstOp['op1']['var'];
- $firstOp['goto'] = $target;
- $opcodes[$target]['gofrom'][] = $range[0];
- $this->recognizeAndDecompileClosedBlocks($EX, $range);
- return false;
- }
- // }}}
- // {{{ for
- if (!empty($firstOp['jmpins']) && $opcodes[$firstOp['jmpins'][0]]['opcode'] == XC_JMP
- && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts']) && $lastOp['jmpouts'][0] <= $firstOp['jmpins'][0]
- && !empty($opcodes[$range[1] + 1]['jmpins']) && $opcodes[$opcodes[$range[1] + 1]['jmpins'][0]]['opcode'] == XC_JMPZNZ
- ) {
- $nextRange = array($lastOp['jmpouts'][0], $firstOp['jmpins'][0]);
- $conditionRange = array($range[0], $nextRange[0] - 1);
- $this->removeJmpInfo($EX, $conditionRange[1]);
- $bodyRange = array($nextRange[1], $range[1]);
- $this->removeJmpInfo($EX, $bodyRange[1]);
- $initial = '';
- $this->beginScope($EX);
- $this->dasmBasicBlock($EX, $conditionRange);
- $conditionCodes = array();
- for ($i = $conditionRange[0]; $i <= $conditionRange[1]; ++$i) {
- if (isset($opcodes[$i]['php'])) {
- $conditionCodes[] = str($opcodes[$i]['php'], $EX);
- }
- }
- $conditionCodes[] = str($this->getOpVal($opcodes[$conditionRange[1]]['op1'], $EX), $EX);
- if (implode(',', $conditionCodes) == 'true') {
- $conditionCodes = array();
- }
- $this->endScope($EX);
- $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->beginScope($EX);
- $this->recognizeAndDecompileClosedBlocks($EX, $bodyRange);
- $this->endScope($EX);
- echo $indent, '}', PHP_EOL;
- $this->endComplexBlock($EX);
- return;
- }
- // }}}
- // {{{ if/elseif/else
- if ($this->isIfCondition($EX, $range)) {
- $this->beginComplexBlock($EX);
- $isElseIf = false;
- do {
- $ifRange = array($range[0], $opcodes[$range[0]]['jmpouts'][0] - 1);
- $this->removeJmpInfo($EX, $ifRange[0]);
- $this->removeJmpInfo($EX, $ifRange[1]);
- $condition = $this->getOpVal($opcodes[$ifRange[0]]['op1'], $EX);
- echo $indent, $isElseIf ? 'else if' : 'if', ' (', str($condition, $EX), ') ', '{', PHP_EOL;
- $this->beginScope($EX);
- $this->recognizeAndDecompileClosedBlocks($EX, $ifRange);
- $this->endScope($EX);
- $EX['lastBlock'] = null;
- echo $indent, '}', PHP_EOL;
- $isElseIf = true;
- // search for else if
- $range[0] = $ifRange[1] + 1;
- for ($i = $ifRange[1] + 1; $i <= $range[1]; ++$i) {
- // find first jmpout
- if (!empty($opcodes[$i]['jmpouts'])) {
- if ($this->isIfCondition($EX, array($i, $range[1]))) {
- $this->dasmBasicBlock($EX, array($range[0], $i));
- $range[0] = $i;
- }
- break;
- }
- }
- } while ($this->isIfCondition($EX, $range));
- if ($ifRange[1] < $range[1]) {
- $elseRange = array($ifRange[1], $range[1]);
- echo $indent, 'else ', '{', PHP_EOL;
- $this->beginScope($EX);
- $this->recognizeAndDecompileClosedBlocks($EX, $elseRange);
- $this->endScope($EX);
- $EX['lastBlock'] = null;
- echo $indent, '}', PHP_EOL;
- }
- $this->endComplexBlock($EX);
- return;
- }
- if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts'])
- && $firstOp['jmpouts'][0] - 1 == $range[1] && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_RETURN) {
- $this->beginComplexBlock($EX);
- $this->removeJmpInfo($EX, $range[0]);
- $condition = $this->getOpVal($opcodes[$range[0]]['op1'], $EX);
- echo $indent, 'if (', str($condition, $EX), ') ', '{', PHP_EOL;
- $this->beginScope($EX);
- $this->recognizeAndDecompileClosedBlocks($EX, $range);
- $this->endScope($EX);
- echo $indent, '}', PHP_EOL;
- $this->endComplexBlock($EX);
- return;
- }
- // }}}
- // {{{ try/catch
- if (!empty($firstOp['jmpins']) && !empty($opcodes[$firstOp['jmpins'][0]]['isCatchBegin'])) {
- $catchBlocks = array();
- $catchFirst = $firstOp['jmpins'][0];
- $tryRange = array($range[0], $catchFirst - 1);
- // search for XC_CATCH
- $this->removeJmpInfo($EX, $catchFirst);
- for ($i = $catchFirst; $i <= $range[1]; ) {
- if ($opcodes[$i]['opcode'] == XC_CATCH) {
- $catchOpLine = $i;
- $this->removeJmpInfo($EX, $catchOpLine);
- $catchNext = $opcodes[$catchOpLine]['extended_value'];
- $catchBodyLast = $catchNext - 1;
- if ($opcodes[$catchBodyLast]['opcode'] == XC_JMP) {
- --$catchBodyLast;
- }
- $catchBlocks[$catchFirst] = array($catchOpLine, $catchBodyLast);
- $i = $catchFirst = $catchNext;
- }
- else {
- ++$i;
- }
- }
- if ($opcodes[$tryRange[1]]['opcode'] == XC_JMP) {
- --$tryRange[1];
- }
- $this->beginComplexBlock($EX);
- echo $indent, "try {", PHP_EOL;
- $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->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->beginScope($EX);
- $this->recognizeAndDecompileClosedBlocks($EX, array($catchBodyFirst, $catchBodyLast));
- $this->endScope($EX);
- echo $indent, '}', PHP_EOL;
- }
- $this->endComplexBlock($EX);
- return;
- }
- // }}}
- // {{{ switch/case
- if ($firstOp['opcode'] == XC_SWITCH_FREE && isset($T[$firstOp['op1']['var']])) {
- // TODO: merge this code to CASE code. use SWITCH_FREE to detect begin of switch by using $Ts if possible
- $this->beginComplexBlock($EX);
- echo $indent, 'switch (', str($this->getOpVal($firstOp['op1'], $EX)), ") {", PHP_EOL;
- echo $indent, '}', PHP_EOL;
- $this->endComplexBlock($EX);
- return;
- }
- if (
- ($firstOp['opcode'] == XC_CASE
- || $firstOp['opcode'] == XC_JMP && !empty($firstOp['jmpouts']) && $opcodes[$firstOp['jmpouts'][0]]['opcode'] == XC_CASE
- )
- && !empty($lastOp['jmpouts'])
- ) {
- $cases = array();
- $caseDefault = null;
- $caseOp = null;
- for ($i = $range[0]; $i <= $range[1]; ) {
- $op = $opcodes[$i];
- if ($op['opcode'] == XC_CASE) {
- if (!isset($caseOp)) {
- $caseOp = $op;
- }
- $jmpz = $opcodes[$i + 1];
- assert('$jmpz["opcode"] == XC_JMPZ');
- $caseNext = $jmpz['jmpouts'][0];
- $cases[$i] = $caseNext - 1;
- $i = $caseNext;
- }
- else if ($op['opcode'] == XC_JMP && $op['jmpouts'][0] >= $i) {
- // default
- $caseNext = $op['jmpouts'][0];
- $caseDefault = $i;
- $cases[$i] = $caseNext - 1;
- $i = $caseNext;
- }
- else {
- ++$i;
- }
- }
- $this->beginComplexBlock($EX);
- echo $indent, 'switch (', str($this->getOpVal($caseOp['op1'], $EX), $EX), ") {", PHP_EOL;
- $caseIsOut = false;
- foreach ($cases as $caseFirst => $caseLast) {
- if ($caseIsOut && empty($lastCaseFall)) {
- echo PHP_EOL;
- }
- $caseOp = $opcodes[$caseFirst];
- echo $indent;
- if ($caseOp['opcode'] == XC_CASE) {
- echo 'case ';
- echo str($this->getOpVal($caseOp['op2'], $EX), $EX);
- echo ':', PHP_EOL;
- $this->removeJmpInfo($EX, $caseFirst);
- ++$caseFirst;
- assert('$opcodes[$caseFirst]["opcode"] == XC_JMPZ');
- $this->removeJmpInfo($EX, $caseFirst);
- ++$caseFirst;
- }
- else {
- echo 'default';
- echo ':', PHP_EOL;
- assert('$opcodes[$caseFirst]["opcode"] == XC_JMP');
- $this->removeJmpInfo($EX, $caseFirst);
- ++$caseFirst;
- }
- assert('$opcodes[$caseLast]["opcode"] == XC_JMP');
- $this->removeJmpInfo($EX, $caseLast);
- --$caseLast;
- switch ($opcodes[$caseLast]['opcode']) {
- case XC_BRK:
- case XC_CONT:
- case XC_GOTO:
- $lastCaseFall = false;
- break;
- default:
- $lastCaseFall = true;
- }
- $this->beginScope($EX);
- $this->recognizeAndDecompileClosedBlocks($EX, array($caseFirst, $caseLast));
- $this->endScope($EX);
- $caseIsOut = true;
- }
- echo $indent, '}', PHP_EOL;
- $this->endComplexBlock($EX);
- return;
- }
- // }}}
- // {{{ do/while
- if ($lastOp['opcode'] == XC_JMPNZ && !empty($lastOp['jmpouts'])
- && $lastOp['jmpouts'][0] == $range[0]) {
- $this->removeJmpInfo($EX, $range[1]);
- $this->beginComplexBlock($EX);
- echo $indent, "do {", PHP_EOL;
- $this->beginScope($EX);
- $this->recognizeAndDecompileClosedBlocks($EX, $range);
- $this->endScope($EX);
- echo $indent, "} while (", str($this->getOpVal($lastOp['op1'], $EX)), ');', PHP_EOL;
- $this->endComplexBlock($EX);
- return;
- }
- // }}}
- // {{{ search firstJmpOp
- $firstJmp = null;
- $firstJmpOp = null;
- for ($i = $range[0]; $i <= $range[1]; ++$i) {
- if (!empty($opcodes[$i]['jmpouts'])) {
- $firstJmp = $i;
- $firstJmpOp = &$opcodes[$firstJmp];
- break;
- }
- }
- // }}}
- // {{{ while
- if (isset($firstJmpOp)
- && $firstJmpOp['opcode'] == XC_JMPZ
- && $firstJmpOp['jmpouts'][0] > $range[1]
- && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts'])
- && $lastOp['jmpouts'][0] == $range[0]) {
- $this->removeJmpInfo($EX, $firstJmp);
- $this->removeJmpInfo($EX, $range[1]);
- $this->beginComplexBlock($EX);
- ob_start();
- $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;
- echo $body;
- echo $indent, '}', PHP_EOL;
- $this->endComplexBlock($EX);
- return;
- }
- // }}}
- // {{{ foreach
- if (isset($firstJmpOp)
- && $firstJmpOp['opcode'] == XC_FE_FETCH
- && $firstJmpOp['jmpouts'][0] > $range[1]
- && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts'])
- && $lastOp['jmpouts'][0] == $firstJmp) {
- $this->removeJmpInfo($EX, $firstJmp);
- $this->removeJmpInfo($EX, $range[1]);
- $this->beginComplexBlock($EX);
- ob_start();
- $this->beginScope($EX);
- $this->recognizeAndDecompileClosedBlocks($EX, $range);
- $this->endScope($EX);
- $body = ob_get_clean();
- $as = foldToCode($firstJmpOp['fe_as'], $EX);
- if (isset($firstJmpOp['fe_key'])) {
- $as = str($firstJmpOp['fe_key'], $EX) . ' => ' . str($as);
- }
- echo $indent, 'foreach (', str($firstJmpOp['fe_src'], $EX), " as $as) {", PHP_EOL;
- echo $body;
- echo $indent, '}', PHP_EOL;
- $this->endComplexBlock($EX);
- if ($opcodes[$range[1] + 1]['opcode'] == XC_SWITCH_FREE) {
- $this->removeJmpInfo($EX, $range[1] + 1);
- }
- return;
- }
- // }}}
- $this->decompileBasicBlock($EX, $range, true);
- }
- // }}}
- function recognizeAndDecompileClosedBlocks(&$EX, $range) // {{{ decompile in a tree way
- {
- $opcodes = &$EX['opcodes'];
- $starti = $range[0];
- for ($i = $starti; $i <= $range[1]; ) {
- if (!empty($opcodes[$i]['jmpins']) || !empty($opcodes[$i]['jmpouts'])) {
- $blockFirst = $i;
- $blockLast = -1;
- $j = $blockFirst;
- do {
- $op = $opcodes[$j];
- if (!empty($op['jmpins'])) {
- // care about jumping from blocks behind, not before
- foreach ($op['jmpins'] as $oplineNumber) {
- if ($oplineNumber <= $range[1] && $blockLast < $oplineNumber) {
- $blockLast = $oplineNumber;
- }
- }
- }
- if (!empty($op['jmpouts'])) {
- $blockLast = max($blockLast, max($op['jmpouts']) - 1);
- }
- ++$j;
- } while ($j <= $blockLast);
- if (!assert('$blockLast <= $range[1]')) {
- var_dump($blockLast, $range[1]);
- }
- if ($blockLast >= $blockFirst) {
- if ($blockFirst > $starti) {
- $this->decompileBasicBlock($EX, array($starti, $blockFirst - 1));
- }
- if ($this->decompileComplexBlock($EX, array($blockFirst, $blockLast)) === false) {
- if ($EX['lastBlock'] == 'complex') {
- echo PHP_EOL;
- }
- $EX['lastBlock'] = null;
- }
- $starti = $blockLast + 1;
- $i = $starti;
- }
- else {
- ++$i;
- }
- }
- else {
- ++$i;
- }
- }
- if ($starti <= $range[1]) {
- $this->decompileBasicBlock($EX, array($starti, $range[1]));
- }
- }
- // }}}
- function &dop_array($op_array, $indent = '') // {{{
- {
- $op_array['opcodes'] = $this->fixOpcode($op_array['opcodes'], true, $indent == '' ? 1 : null);
- $opcodes = &$op_array['opcodes'];
- $last = count($opcodes) - 1;
- // {{{ build jmpins/jmpouts to op_array
- for ($i = 0; $i <= $last; $i ++) {
- $op = &$opcodes[$i];
- $op['line'] = $i;
- switch ($op['opcode']) {
- case XC_CONT:
- case XC_BRK:
- $op['jmpouts'] = array();
- break;
- case XC_GOTO:
- $target = $op['op1']['var'];
- $op['goto'] = $target;
- $opcodes[$target]['gofrom'][] = $i;
- break;
- case XC_JMP:
- $target = $op['op1']['var'];
- $op['jmpouts'] = array($target);
- $opcodes[$target]['jmpins'][] = $i;
- break;
- case XC_JMPZNZ:
- $jmpz = $op['op2']['opline_num'];
- $jmpnz = $op['extended_value'];
- $op['jmpouts'] = array($jmpz, $jmpnz);
- $opcodes[$jmpz]['jmpins'][] = $i;
- $opcodes[$jmpnz]['jmpins'][] = $i;
- break;
- case XC_JMPZ:
- case XC_JMPNZ:
- case XC_JMPZ_EX:
- case XC_JMPNZ_EX:
- // case XC_JMP_SET:
- // case XC_FE_RESET:
- case XC_FE_FETCH:
- // case XC_JMP_NO_CTOR:
- $target = $op['op2']['opline_num'];
- //if (!isset($target)) {
- // $this->dumpop($op, $EX);
- // var_dump($op); exit;
- //}
- $op['jmpouts'] = array($target);
- $opcodes[$target]['jmpins'][] = $i;
- break;
- /*
- case XC_RETURN:
- $op['jmpouts'] = array();
- break;
- */
- case XC_SWITCH_FREE:
- $op['jmpouts'] = array($i + 1);
- $opcodes[$i + 1]['jmpins'][] = $i;
- break;
- case XC_CASE:
- // just to link together
- $op['jmpouts'] = array($i + 2);
- $opcodes[$i + 2]['jmpins'][] = $i;
- break;
- case XC_CATCH:
- $catchNext = $op['extended_value'];
- $op['jmpouts'] = array($catchNext);
- $opcodes[$catchNext]['jmpins'][] = $i;
- break;
- }
- /*
- if (!empty($op['jmpouts']) || !empty($op['jmpins'])) {
- echo $i, "\t", xcache_get_opcode($op['opcode']), PHP_EOL;
- }
- // */
- }
- unset($op);
- if ($op_array['try_catch_array']) {
- foreach ($op_array['try_catch_array'] as $try_catch_element) {
- $catch_op = $try_catch_element['catch_op'];
- $try_op = $try_catch_element['try_op'];
- $opcodes[$try_op]['jmpins'][] = $catch_op;
- $opcodes[$catch_op]['jmpouts'][] = $try_op;
- $opcodes[$catch_op]['isCatchBegin'] = true;
- }
- }
- // }}}
- // build semi-basic blocks
- $nextbbs = array();
- $starti = 0;
- for ($i = 1; $i <= $last; $i ++) {
- if (isset($opcodes[$i]['jmpins'])
- || isset($opcodes[$i - 1]['jmpouts'])) {
- $nextbbs[$starti] = $i;
- $starti = $i;
- }
- }
- $nextbbs[$starti] = $last + 1;
- $EX = array();
- $EX['Ts'] = array();
- $EX['indent'] = $indent;
- $EX['nextbbs'] = $nextbbs;
- $EX['op_array'] = &$op_array;
- $EX['opcodes'] = &$opcodes;
- $EX['range'] = array(0, count($opcodes) - 1);
- // func call
- $EX['object'] = null;
- $EX['called_scope'] = null;
- $EX['fbc'] = null;
- $EX['argstack'] = array();
- $EX['arg_types_stack'] = array();
- $EX['scopeStack'] = array();
- $EX['silence'] = 0;
- $EX['recvs'] = array();
- $EX['uses'] = array();
- $EX['lastBlock'] = null;
- /* dump whole array
- $this->keepTs = true;
- $this->dasmBasicBlock($EX, $range);
- for ($i = $range[0]; $i <= $range[1]; ++$i) {
- echo $i, "\t", $this->dumpop($opcodes[$i], $EX);
- }
- // */
- // decompile in a tree way
- $this->recognizeAndDecompileClosedBlocks($EX, $EX['range'], $EX['indent']);
- return $EX;
- }
- // }}}
- function dasmBasicBlock(&$EX, $range) // {{{
- {
- $T = &$EX['Ts'];
- $opcodes = &$EX['opcodes'];
- $lastphpop = null;
- for ($i = $range[0]; $i <= $range[1]; $i ++) {
- // {{{ prepair
- $op = &$opcodes[$i];
- $opc = $op['opcode'];
- if ($opc == XC_NOP) {
- $this->usedOps[$opc] = true;
- continue;
- }
- $op1 = $op['op1'];
- $op2 = $op['op2'];
- $res = $op['result'];
- $ext = $op['extended_value'];
- $opname = xcache_get_opcode($opc);
- if ($opname == 'UNDEF' || !isset($opname)) {
- echo 'UNDEF OP:';
- $this->dumpop($op, $EX);
- continue;
- }
- // echo $i, ' '; $this->dumpop($op, $EX); //var_dump($op);
- $resvar = null;
- unset($curResVar);
- if (array_key_exists($res['var'], $T)) {
- $curResVar = &$T[$res['var']];
- }
- if ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) {
- $istmpres = false;
- }
- else {
- $istmpres = true;
- }
- // }}}
- // echo $opname, "\n";
- $notHandled = false;
- switch ($opc) {
- case XC_NEW: // {{{
- array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
- $EX['object'] = (int) $res['var'];
- $EX['called_scope'] = null;
- $EX['fbc'] = 'new ' . unquoteName($this->getOpVal($op1, $EX), $EX);
- if (!ZEND_ENGINE_2) {
- $resvar = '$new object$';
- }
- break;
- // }}}
- case XC_THROW: // {{{
- $resvar = 'throw ' . str($this->getOpVal($op1, $EX));
- break;
- // }}}
- case XC_CLONE: // {{{
- $resvar = 'clone ' . str($this->getOpVal($op1, $EX));
- break;
- // }}}
- case XC_CATCH: // {{{
- break;
- // }}}
- case XC_INSTANCEOF: // {{{
- $resvar = str($this->getOpVal($op1, $EX)) . ' instanceof ' . str($this->getOpVal($op2, $EX));
- break;
- // }}}
- case XC_FETCH_CLASS: // {{{
- if ($op2['op_type'] == XC_IS_UNUSED) {
- switch (($ext & (defined('ZEND_FETCH_CLASS_MASK') ? ZEND_FETCH_CLASS_MASK : 0xFF))) {
- case ZEND_FETCH_CLASS_SELF:
- $class = 'self';
- break;
- case ZEND_FETCH_CLASS_PARENT:
- $class = 'parent';
- break;
- case ZEND_FETCH_CLASS_STATIC:
- $class = 'static';
- break;
- }
- $istmpres = true;
- }
- else {
- $class = $this->getOpVal($op2, $EX);
- if (isset($op2['constant'])) {
- $class = $this->stripNamespace(unquoteName($class));
- }
- }
- $resvar = $class;
- break;
- // }}}
- case XC_FETCH_CONSTANT: // {{{
- if ($op1['op_type'] == XC_IS_UNUSED) {
- $resvar = $this->stripNamespace($op2['constant']);
- break;
- }
- if ($op1['op_type'] == XC_IS_CONST) {
- $resvar = $this->stripNamespace($op1['constant']);
- }
- else {
- $resvar = $this->getOpVal($op1, $EX);
- }
- $resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2, $EX));
- break;
- // }}}
- // {{{ case XC_FETCH_*
- case XC_FETCH_R:
- case XC_FETCH_W:
- case XC_FETCH_RW:
- case XC_FETCH_FUNC_ARG:
- case XC_FETCH_UNSET:
- case XC_FETCH_IS:
- case XC_UNSET_VAR:
- $rvalue = $this->getOpVal($op1, $EX);
- if (defined('ZEND_FETCH_TYPE_MASK')) {
- $fetchtype = ($ext & ZEND_FETCH_TYPE_MASK);
- }
- else {
- $fetchtype = $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type'];
- }
- switch ($fetchtype) {
- case ZEND_FETCH_STATIC_MEMBER:
- $class = $this->getOpVal($op2, $EX);
- $rvalue = str($class) . '::$' . unquoteName($rvalue, $EX);
- break;
- default:
- $name = unquoteName($rvalue, $EX);
- $globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($rvalue) . "]";
- $rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname);
- break;
- }
- if ($opc == XC_UNSET_VAR) {
- $op['php'] = "unset(" . str($rvalue, $EX) . ")";
- $lastphpop = &$op;
- }
- else if ($res['op_type'] != XC_IS_UNUSED) {
- $resvar = $rvalue;
- }
- break;
- // }}}
- // {{{ case XC_FETCH_DIM_*
- case XC_FETCH_DIM_TMP_VAR:
- case XC_FETCH_DIM_R:
- case XC_FETCH_DIM_W:
- case XC_FETCH_DIM_RW:
- case XC_FETCH_DIM_FUNC_ARG:
- case XC_FETCH_DIM_UNSET:
- case XC_FETCH_DIM_IS:
- case XC_ASSIGN_DIM:
- case XC_UNSET_DIM_OBJ: // PHP 4 only
- case XC_UNSET_DIM:
- case XC_UNSET_OBJ:
- $src = $this->getOpVal($op1, $EX);
- if (is_a($src, "Decompiler_ForeachBox")) {
- $src->iskey = $this->getOpVal($op2, $EX);
- $resvar = $src;
- break;
- }
- if (is_a($src, "Decompiler_DimBox")) {
- $dimbox = $src;
- }
- else {
- if (!is_a($src, "Decompiler_ListBox")) {
- $op1val = $this->getOpVal($op1, $EX);
- $list = new Decompiler_List(isset($op1val) ? $op1val : '$this');
- $src = new Decompiler_ListBox($list);
- if (!isset($op1['var'])) {
- $this->dumpop($op, $EX);
- var_dump($op);
- die('missing var');
- }
- $T[$op1['var']] = $src;
- unset($list);
- }
- $dim = new Decompiler_Dim($src);
- $src->obj->dims[] = &$dim;
- $dimbox = new Decompiler_DimBox($dim);
- }
- $dim = &$dimbox->obj;
- $dim->offsets[] = $this->getOpVal($op2, $EX);
- if ($ext == ZEND_FETCH_ADD_LOCK) {
- $src->obj->everLocked = true;
- }
- else if ($ext == ZEND_FETCH_STANDARD) {
- $dim->isLast = true;
- }
- if ($opc == XC_UNSET_OBJ) {
- $dim->isObject = true;
- }
- unset($dim);
- $rvalue = $dimbox;
- unset($dimbox);
- if ($opc == XC_ASSIGN_DIM) {
- $lvalue = $rvalue;
- ++ $i;
- $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
- $resvar = str($lvalue, $EX) . ' = ' . str($rvalue);
- }
- else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ) {
- $op['php'] = "unset(" . str($rvalue, $EX) . ")";
- $lastphpop = &$op;
- }
- else if ($res['op_type'] != XC_IS_UNUSED) {
- $resvar = $rvalue;
- }
- break;
- // }}}
- case XC_ASSIGN: // {{{
- $lvalue = $this->getOpVal($op1, $EX);
- $rvalue = $this->getOpVal($op2, $EX);
- if (is_a($rvalue, 'Decompiler_ForeachBox')) {
- $type = $rvalue->iskey ? 'fe_key' : 'fe_as';
- $rvalue->obj[$type] = $lvalue;
- unset($T[$op2['var']]);
- break;
- }
- if (is_a($rvalue, "Decompiler_DimBox")) {
- $dim = &$rvalue->obj;
- $dim->assign = $lvalue;
- if ($dim->isLast) {
- $resvar = foldToCode($dim->value, $EX);
- }
- unset($dim);
- break;
- }
- if (is_a($rvalue, 'Decompiler_Fetch')) {
- $src = str($rvalue->src, $EX);
- if ('$' . unquoteName($src) == $lvalue) {
- switch ($rvalue->fetchType) {
- case ZEND_FETCH_STATIC:
- $statics = &$EX['op_array']['static_variables'];
- if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) {
- $EX['uses'][] = str($lvalue);
- unset($statics);
- break 2;
- }
- unset($statics);
- }
- }
- }
- $resvar = new Decompiler_Binop($this, $lvalue, XC_ASSIGN, $rvalue);
- break;
- // }}}
- case XC_ASSIGN_REF: // {{{
- $lvalue = $this->getOpVal($op1, $EX);
- $rvalue = $this->getOpVal($op2, $EX);
- if (is_a($rvalue, 'Decompiler_Fetch')) {
- $src = str($rvalue->src, $EX);
- if ('$' . unquoteName($src) == $lvalue) {
- switch ($rvalue->fetchType) {
- case ZEND_FETCH_GLOBAL:
- case ZEND_FETCH_GLOBAL_LOCK:
- $resvar = 'global ' . $lvalue;
- break 2;
- case ZEND_FETCH_STATIC:
- $statics = &$EX['op_array']['static_variables'];
- if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) {
- $EX['uses'][] = '&' . str($lvalue);
- unset($statics);
- break 2;
- }
- $resvar = 'static ' . $lvalue;
- $name = unquoteName($src);
- if (isset($statics[$name])) {
- $var = $statics[$name];
- $resvar .= ' = ';
- $resvar .= str(value($var), $EX);
- }
- unset($statics);
- break 2;
- default:
- }
- }
- }
- // TODO: PHP_6 global
- $resvar = new Decompiler_Binop($this, $lvalue, XC_ASSIGN_REF, $rvalue);
- break;
- // }}}
- // {{{ case XC_FETCH_OBJ_*
- case XC_FETCH_OBJ_R:
- case XC_FETCH_OBJ_W:
- case XC_FETCH_OBJ_RW:
- case XC_FETCH_OBJ_FUNC_ARG:
- case XC_FETCH_OBJ_UNSET:
- case XC_FETCH_OBJ_IS:
- case XC_ASSIGN_OBJ:
- $obj = $this->getOpVal($op1, $EX);
- if (!isset($obj)) {
- $obj = '$this';
- }
- $rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
- if ($res['op_type'] != XC_IS_UNUSED) {
- $resvar = $rvalue;
- }
- if ($opc == XC_ASSIGN_OBJ) {
- ++ $i;
- $lvalue = $rvalue;
- $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
- $resvar = "$lvalue = " . str($rvalue);
- }
- break;
- // }}}
- case XC_ISSET_ISEMPTY_DIM_OBJ:
- case XC_ISSET_ISEMPTY_PROP_OBJ:
- case XC_ISSET_ISEMPTY:
- case XC_ISSET_ISEMPTY_VAR: // {{{
- if ($opc == XC_ISSET_ISEMPTY_VAR) {
- $rvalue = $this->getOpVal($op1, $EX);
- // for < PHP_5_3
- if ($op1['op_type'] == XC_IS_CONST) {
- $rvalue = '$' . unquoteVariableName($this->getOpVal($op1, $EX));
- }
- if ($op2['EA.type'] == ZEND_FETCH_STATIC_MEMBER) {
- $class = $this->getOpVal($op2, $EX);
- $rvalue = $class . '::' . $rvalue;
- }
- }
- else if ($opc == XC_ISSET_ISEMPTY) {
- $rvalue = $this->getOpVal($op1, $EX);
- }
- else {
- $container = $this->getOpVal($op1, $EX);
- $dim = $this->getOpVal($op2, $EX);
- if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) {
- if (!isset($container)) {
- $container = '$this';
- }
- $rvalue = $container . "->" . unquoteVariableName($dim);
- }
- else {
- $rvalue = $container . '[' . str($dim) .']';
- }
- }
- switch ((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & (ZEND_ISSET|ZEND_ISEMPTY)) {
- case ZEND_ISSET:
- $rvalue = "isset(" . str($rvalue) . ")";
- break;
- case ZEND_ISEMPTY:
- $rvalue = "empty(" . str($rvalue) . ")";
- break;
- }
- $resvar = $rvalue;
- break;
- // }}}
- case XC_SEND_VAR_NO_REF:
- case XC_SEND_VAL:
- case XC_SEND_REF:
- case XC_SEND_VAR: // {{{
- $ref = ($opc == XC_SEND_REF ? '&' : '');
- $EX['argstack'][] = $ref . str($this->getOpVal($op1, $EX));
- break;
- // }}}
- case XC_INIT_STATIC_METHOD_CALL:
- case XC_INIT_METHOD_CALL: // {{{
- array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
- if ($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) {
- $obj = $this->getOpVal($op1, $EX);
- if (!isset($obj)) {
- $obj = '$this';
- }
- if ($opc == XC_INIT_STATIC_METHOD_CALL || /* PHP4 */ isset($op1['constant'])) {
- $EX['object'] = null;
- $EX['called_scope'] = $this->stripNamespace(unquoteName($obj, $EX));
- }
- else {
- $EX['object'] = $obj;
- $EX['called_scope'] = null;
- }
- if ($res['op_type'] != XC_IS_UNUSED) {
- $resvar = '$obj call$';
- }
- }
- else {
- $EX['object'] = null;
- $EX['called_scope'] = null;
- }
- $EX['fbc'] = $this->getOpVal($op2, $EX);
- if (($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL) && !isset($EX['fbc'])) {
- $EX['fbc'] = '__construct';
- }
- break;
- // }}}
- case XC_INIT_NS_FCALL_BY_NAME:
- case XC_INIT_FCALL_BY_NAME: // {{{
- array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
- if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) {
- break;
- }
- $EX['object'] = null;
- $EX['called_scope'] = null;
- $EX['fbc'] = $this->getOpVal($op2, $EX);
- break;
- // }}}
- case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4?
- $EX['object'] = null;
- $EX['called_scope'] = null;
- $which = $op1['var'];
- $EX['fbc'] = $EX['op_array']['funcs'][$which]['name'];
- break;
- // }}}
- case XC_DO_FCALL_BY_FUNC:
- $which = $op1['var'];
- $fname = $EX['op_array']['funcs'][$which]['name'];
- $args = $this->popargs($EX, $ext);
- $resvar = $fname . "($args)";
- break;
- case XC_DO_FCALL:
- $fname = unquoteName($this->getOpVal($op1, $EX), $EX);
- $args = $this->popargs($EX, $ext);
- $resvar = $fname . "($args)";
- break;
- case XC_DO_FCALL_BY_NAME: // {{{
- $object = null;
- $fname = unquoteName($EX['fbc'], $EX);
- if (!is_int($EX['object'])) {
- $object = $EX['object'];
- }
- $args = $this->popargs($EX, $ext);
- $prefix = (isset($object) ? $object . '->' : '' )
- . (isset($EX['called_scope']) ? $EX['called_scope'] . '::' : '' );
- $resvar = $prefix
- . (!$prefix ? $this->stripNamespace($fname) : $fname)
- . "($args)";
- unset($args);
- if (is_int($EX['object'])) {
- $T[$EX['object']] = $resvar;
- $resvar = null;
- }
- list($EX['fbc'], $EX['object'], $EX['called_scope']) = array_pop($EX['arg_types_stack']);
- break;
- // }}}
- case XC_VERIFY_ABSTRACT_CLASS: // {{{
- //unset($T[$op1['var']]);
- break;
- // }}}
- case XC_DECLARE_CLASS:
- case XC_DECLARE_INHERITED_CLASS:
- case XC_DECLARE_INHERITED_CLASS_DELAYED: // {{{
- $key = $op1['constant'];
- if (!isset($this->dc['class_table'][$key])) {
- echo 'class not found: ', $key, 'existing classes are:', "\n";
- var_dump(array_keys($this->dc['class_table']));
- exit;
- }
- $class = &$this->dc['class_table'][$key];
- if (!isset($class['name'])) {
- $class['name'] = unquoteName($this->getOpVal($op2, $EX), $EX);
- }
- if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) {
- $ext /= XC_SIZEOF_TEMP_VARIABLE;
- $class['parent'] = $T[$ext];
- unset($T[$ext]);
- }
- else {
- $class['parent'] = null;
- }
- for (;;) {
- if ($i + 1 <= $range[1]
- && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE
- && $opcodes[$i + 1]['op1']['var'] == $res['var']) {
- // continue
- }
- else if ($i + 2 <= $range[1]
- && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
- && $opcodes[$i + 2]['op1']['var'] == $res['var']
- && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
- // continue
- }
- else {
- break;
- }
- $this->usedOps[XC_ADD_INTERFACE] = true;
- $fetchop = &$opcodes[$i + 1];
- $interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2'], $EX), $EX));
- $addop = &$opcodes[$i + 2];
- $class['interfaces'][$addop['extended_value']] = $interface;
- unset($fetchop, $addop);
- $i += 2;
- }
- $this->dclass($class, $EX['indent']);
- echo "\n";
- unset($class);
- break;
- // }}}
- case XC_INIT_STRING: // {{{
- $resvar = "''";
- break;
- // }}}
- case XC_ADD_CHAR:
- case XC_ADD_STRING:
- case XC_ADD_VAR: // {{{
- $op1val = $this->getOpVal($op1, $EX);
- $op2val = $this->getOpVal($op2, $EX);
- switch ($opc) {
- case XC_ADD_CHAR:
- $op2val = value(chr(str($op2val)));
- break;
- case XC_ADD_STRING:
- break;
- case XC_ADD_VAR:
- break;
- }
- if (str($op1val) == "''") {
- $rvalue = $op2val;
- }
- else if (str($op2val) == "''") {
- $rvalue = $op1val;
- }
- else {
- $rvalue = str($op1val) . ' . ' . str($op2val);
- }
- $resvar = $rvalue;
- // }}}
- break;
- case XC_PRINT: // {{{
- $op1val = $this->getOpVal($op1, $EX);
- $resvar = "print(" . str($op1val) . ")";
- break;
- // }}}
- case XC_ECHO: // {{{
- $op1val = $this->getOpVal($op1, $EX);
- $resvar = "echo " . str($op1val);
- break;
- // }}}
- case XC_EXIT: // {{{
- $op1val = $this->getOpV…
Large files files are truncated, but you can click here to view the full file