XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2818 lines
71 KiB

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