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.

1886 lines
44 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($src, $indent = '') // {{{
  9. {
  10. if (is_array($indent)) {
  11. $indent = $indent['indent'];
  12. }
  13. /*
  14. $e = xcache_get_special_value($src);
  15. if (isset($e)) {
  16. if (is_array($e)) {
  17. $src = $e;
  18. }
  19. else {
  20. return $e;
  21. }
  22. }
  23. */
  24. if (is_array($src)) {
  25. die_error('array str');
  26. $src = new Decompiler_Array($src);
  27. return $src->__toString($indent);
  28. }
  29. if (is_object($src)) {
  30. if (!method_exists($src, '__toString')) {
  31. var_dump($src);
  32. die_error('no __toString');
  33. }
  34. return $src->__toString($indent);
  35. }
  36. return $src;
  37. }
  38. // }}}
  39. function value($value) // {{{
  40. {
  41. $spec = xcache_get_special_value($value);
  42. if (isset($spec)) {
  43. $value = $spec;
  44. if (!is_array($value)) {
  45. // constant
  46. return $value;
  47. }
  48. }
  49. if (is_array($value)) {
  50. $value = new Decompiler_Array($value, true);
  51. }
  52. else {
  53. $value = new Decompiler_Value($value, true);
  54. }
  55. return $value;
  56. }
  57. // }}}
  58. class Decompiler_Object // {{{
  59. {
  60. }
  61. // }}}
  62. class Decompiler_Value extends Decompiler_Object // {{{
  63. {
  64. var $value;
  65. function Decompiler_Value($value = null)
  66. {
  67. $this->value = $value;
  68. }
  69. function __toString()
  70. {
  71. return var_export($this->value, true);
  72. }
  73. }
  74. // }}}
  75. class Decompiler_Code extends Decompiler_Object // {{{
  76. {
  77. var $src;
  78. function Decompiler_Code($src)
  79. {
  80. $this->src = $src;
  81. }
  82. function __toString()
  83. {
  84. return $this->src;
  85. }
  86. }
  87. // }}}
  88. class Decompiler_Binop extends Decompiler_Code // {{{
  89. {
  90. var $opc;
  91. var $op1;
  92. var $op2;
  93. var $parent;
  94. function Decompiler_Binop($parent, $op1, $opc, $op2)
  95. {
  96. $this->parent = &$parent;
  97. $this->opc = $opc;
  98. $this->op1 = $op1;
  99. $this->op2 = $op2;
  100. }
  101. function __toString()
  102. {
  103. $op1 = str($this->op1);
  104. if (is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) {
  105. $op1 = "($op1)";
  106. }
  107. $opstr = $this->parent->binops[$this->opc];
  108. if ($op1 == '0' && $this->opc == XC_SUB) {
  109. return $opstr . str($this->op2);
  110. }
  111. return $op1 . ' ' . $opstr . ' ' . str($this->op2);
  112. }
  113. }
  114. // }}}
  115. class Decompiler_Fetch extends Decompiler_Code // {{{
  116. {
  117. var $src;
  118. var $fetchType;
  119. function Decompiler_Fetch($src, $type, $globalsrc)
  120. {
  121. $this->src = $src;
  122. $this->fetchType = $type;
  123. $this->globalsrc = $globalsrc;
  124. }
  125. function __toString()
  126. {
  127. switch ($this->fetchType) {
  128. case ZEND_FETCH_LOCAL:
  129. return '$' . substr($this->src, 1, -1);
  130. case ZEND_FETCH_STATIC:
  131. die('static fetch cant to string');
  132. case ZEND_FETCH_GLOBAL:
  133. case ZEND_FETCH_GLOBAL_LOCK:
  134. return $this->globalsrc;
  135. default:
  136. var_dump($this->fetchType);
  137. assert(0);
  138. }
  139. }
  140. }
  141. // }}}
  142. class Decompiler_Box // {{{
  143. {
  144. var $obj;
  145. function Decompiler_Box(&$obj)
  146. {
  147. $this->obj = &$obj;
  148. }
  149. function __toString($indent)
  150. {
  151. return $this->obj->__toString($indent);
  152. }
  153. }
  154. // }}}
  155. class Decompiler_Dim extends Decompiler_Value // {{{
  156. {
  157. var $offsets = array();
  158. var $isLast = false;
  159. var $assign = null;
  160. function __toString()
  161. {
  162. if (is_a($this->value, 'Decompiler_ListBox')) {
  163. $exp = str($this->value->obj->src);
  164. }
  165. else {
  166. $exp = str($this->value);
  167. }
  168. foreach ($this->offsets as $dim) {
  169. $exp .= '[' . str($dim) . ']';
  170. }
  171. return $exp;
  172. }
  173. }
  174. // }}}
  175. class Decompiler_DimBox extends Decompiler_Box // {{{
  176. {
  177. }
  178. // }}}
  179. class Decompiler_List extends Decompiler_Code // {{{
  180. {
  181. var $src;
  182. var $dims = array();
  183. var $everLocked = false;
  184. function __toString()
  185. {
  186. if (count($this->dims) == 1 && !$this->everLocked) {
  187. $dim = $this->dims[0];
  188. unset($dim->value);
  189. $dim->value = $this->src;
  190. if (!isset($dim->assign)) {
  191. return str($dim);
  192. }
  193. return str($this->dims[0]->assign) . ' = ' . str($dim);
  194. }
  195. /* flatten dims */
  196. $assigns = array();
  197. foreach ($this->dims as $dim) {
  198. $assign = &$assigns;
  199. foreach ($dim->offsets as $offset) {
  200. $assign = &$assign[$offset];
  201. }
  202. $assign = str($dim->assign);
  203. }
  204. return $this->toList($assigns) . ' = ' . str($this->src);
  205. }
  206. function toList($assigns)
  207. {
  208. $keys = array_keys($assigns);
  209. if (count($keys) < 2) {
  210. $keys[] = 0;
  211. }
  212. $max = call_user_func_array('max', $keys);
  213. $list = 'list(';
  214. for ($i = 0; $i <= $max; $i ++) {
  215. if ($i) {
  216. $list .= ', ';
  217. }
  218. if (!isset($assigns[$i])) {
  219. continue;
  220. }
  221. if (is_array($assigns[$i])) {
  222. $list .= $this->toList($assigns[$i]);
  223. }
  224. else {
  225. $list .= $assigns[$i];
  226. }
  227. }
  228. return $list . ')';
  229. }
  230. }
  231. // }}}
  232. class Decompiler_ListBox extends Decompiler_Box // {{{
  233. {
  234. }
  235. // }}}
  236. class Decompiler_Array extends Decompiler_Value // {{{
  237. {
  238. var $needExport = false;
  239. function Decompiler_Array($value = array(), $needexport = false)
  240. {
  241. $this->value = $value;
  242. $this->needExport = $needexport;
  243. }
  244. function __toString($indent)
  245. {
  246. $exp = "array(";
  247. $indent .= INDENT;
  248. $assoclen = 0;
  249. $multiline = 0;
  250. $i = 0;
  251. foreach ($this->value as $k => $v) {
  252. if ($i !== $k) {
  253. $len = strlen($k);
  254. if ($assoclen < $len) {
  255. $assoclen = $len;
  256. }
  257. }
  258. if (is_array($v)) {
  259. $multiline ++;
  260. }
  261. ++ $i;
  262. }
  263. if ($assoclen && $this->needExport) {
  264. $assoclen += 2;
  265. }
  266. $i = 0;
  267. $subindent = $indent . INDENT;
  268. foreach ($this->value as $k => $v) {
  269. if ($multiline) {
  270. if ($i) {
  271. $exp .= ",";
  272. }
  273. $exp .= "\n";
  274. $exp .= $indent;
  275. }
  276. else {
  277. if ($i) {
  278. $exp .= ", ";
  279. }
  280. }
  281. if ($this->needExport) {
  282. $k = var_export($k, true);
  283. }
  284. if ($multiline) {
  285. $exp .= sprintf("%{$assoclen}s => ", $k);
  286. }
  287. else if ($assoclen) {
  288. $exp .= $k . ' => ';
  289. }
  290. if (is_array($v)) {
  291. $v = new Decompiler_Array($v, $this->needExport);
  292. }
  293. $exp .= str($v, $subindent);
  294. $i ++;
  295. }
  296. if ($multiline) {
  297. $exp .= "$indent);";
  298. }
  299. else {
  300. $exp .= ")";
  301. }
  302. return $exp;
  303. }
  304. }
  305. // }}}
  306. class Decompiler_ForeachBox extends Decompiler_Box // {{{
  307. {
  308. var $iskey;
  309. function __toString($indent)
  310. {
  311. return 'foreach (' . '';
  312. }
  313. }
  314. // }}}
  315. class Decompiler
  316. {
  317. var $rName = '!^[\\w_][\\w\\d_]*$!';
  318. var $rQuotedName = "!^'[\\w_][\\w\\d_]*'\$!";
  319. function Decompiler()
  320. {
  321. // {{{ opinfo
  322. $this->unaryops = array(
  323. XC_BW_NOT => '~',
  324. XC_BOOL_NOT => '!',
  325. );
  326. $this->binops = array(
  327. XC_ADD => "+",
  328. XC_ASSIGN_ADD => "+=",
  329. XC_SUB => "-",
  330. XC_ASSIGN_SUB => "-=",
  331. XC_MUL => "*",
  332. XC_ASSIGN_MUL => "*=",
  333. XC_DIV => "/",
  334. XC_ASSIGN_DIV => "/=",
  335. XC_MOD => "%",
  336. XC_ASSIGN_MOD => "%=",
  337. XC_SL => "<<",
  338. XC_ASSIGN_SL => "<<=",
  339. XC_SR => ">>",
  340. XC_ASSIGN_SR => ">>=",
  341. XC_CONCAT => ".",
  342. XC_ASSIGN_CONCAT => ".=",
  343. XC_IS_IDENTICAL => "===",
  344. XC_IS_NOT_IDENTICAL => "!==",
  345. XC_IS_EQUAL => "==",
  346. XC_IS_NOT_EQUAL => "!=",
  347. XC_IS_SMALLER => "<",
  348. XC_IS_SMALLER_OR_EQUAL => "<=",
  349. XC_BW_OR => "|",
  350. XC_ASSIGN_BW_OR => "|=",
  351. XC_BW_AND => "&",
  352. XC_ASSIGN_BW_AND => "&=",
  353. XC_BW_XOR => "^",
  354. XC_ASSIGN_BW_XOR => "^=",
  355. XC_BOOL_XOR => "xor",
  356. );
  357. // }}}
  358. $this->includeTypes = array( // {{{
  359. ZEND_EVAL => 'eval',
  360. ZEND_INCLUDE => 'include',
  361. ZEND_INCLUDE_ONCE => 'include_once',
  362. ZEND_REQUIRE => 'require',
  363. ZEND_REQUIRE_ONCE => 'require_once',
  364. );
  365. // }}}
  366. }
  367. function outputPhp(&$opcodes, $opline, $last, $indent) // {{{
  368. {
  369. $origindent = $indent;
  370. $curticks = 0;
  371. for ($i = $opline; $i <= $last; $i ++) {
  372. $op = $opcodes[$i];
  373. if (isset($op['php'])) {
  374. $toticks = isset($op['ticks']) ? $op['ticks'] : 0;
  375. if ($curticks != $toticks) {
  376. if (!$toticks) {
  377. echo $origindent, "}\n";
  378. $indent = $origindent;
  379. }
  380. else {
  381. if ($curticks) {
  382. echo $origindent, "}\n";
  383. }
  384. else if (!$curticks) {
  385. $indent .= INDENT;
  386. }
  387. echo $origindent, "declare(ticks=$curticks) {\n";
  388. }
  389. $curticks = $toticks;
  390. }
  391. echo $indent, str($op['php']), ";\n";
  392. }
  393. }
  394. if ($curticks) {
  395. echo $origindent, "}\n";
  396. }
  397. }
  398. // }}}
  399. function getOpVal($op, &$EX, $tostr = true, $free = false) // {{{
  400. {
  401. switch ($op['op_type']) {
  402. case XC_IS_CONST:
  403. return str(value($op['u.constant']));
  404. case XC_IS_VAR:
  405. case XC_IS_TMP_VAR:
  406. $T = &$EX['Ts'];
  407. $ret = $T[$op['u.var']];
  408. if ($tostr) {
  409. $ret = str($ret, $EX);
  410. }
  411. if ($free) {
  412. unset($T[$op['u.var']]);
  413. }
  414. return $ret;
  415. case XC_IS_CV:
  416. $var = $op['u.var'];
  417. $var = $EX['op_array']['vars'][$var];
  418. return '$' . $var['name'];
  419. case XC_IS_UNUSED:
  420. return null;
  421. }
  422. }
  423. // }}}
  424. function &dop_array($op_array, $indent = '') // {{{
  425. {
  426. $opcodes = &$op_array['opcodes'];
  427. $last = count($opcodes) - 1;
  428. if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
  429. unset($opcodes[$last]);
  430. }
  431. $EX['indent'] = '';
  432. //for ($i = 0, $cnt = count($opcodes); $i < $cnt; $i ++) {
  433. // $opcodes[$i]['opcode'] = xcache_get_fixed_opcode($opcodes[$i]['opcode'], $i);
  434. //}
  435. // {{{ build jmp array
  436. for ($i = 0, $cnt = count($opcodes); $i < $cnt; $i ++) {
  437. $op = &$opcodes[$i];
  438. /*
  439. if ($op['opcode'] == XC_JMPZ) {
  440. $this->dumpop($op, $EX);
  441. var_dump($op);
  442. }
  443. continue;
  444. */
  445. $op['line'] = $i;
  446. switch ($op['opcode']) {
  447. case XC_JMP:
  448. $target = $op['op1']['u.var'];
  449. $op['jmpouts'] = array($target);
  450. $opcodes[$target]['jmpins'][] = $i;
  451. break;
  452. case XC_JMPZNZ:
  453. $jmpz = $op['op2']['u.opline_num'];
  454. $jmpnz = $op['extended_value'];
  455. $op['jmpouts'] = array($jmpz, $jmpnz);
  456. $opcodes[$jmpz]['jmpins'][] = $i;
  457. $opcodes[$jmpnz]['jmpins'][] = $i;
  458. break;
  459. case XC_JMPZ:
  460. case XC_JMPNZ:
  461. case XC_JMPZ_EX:
  462. case XC_JMPNZ_EX:
  463. // case XC_FE_RESET:
  464. case XC_FE_FETCH:
  465. // case XC_JMP_NO_CTOR:
  466. $target = $op['op2']['u.opline_num'];
  467. //if (!isset($target)) {
  468. // $this->dumpop($op, $EX);
  469. // var_dump($op); exit;
  470. //}
  471. $op['jmpouts'] = array($target);
  472. $opcodes[$target]['jmpins'][] = $i;
  473. break;
  474. /*
  475. case XC_RETURN:
  476. $op['jmpouts'] = array();
  477. break;
  478. */
  479. }
  480. }
  481. unset($op);
  482. // }}}
  483. // build semi-basic blocks
  484. $nextbbs = array();
  485. $starti = 0;
  486. for ($i = 1, $cnt = count($opcodes); $i < $cnt; $i ++) {
  487. if (isset($opcodes[$i]['jmpins'])
  488. || isset($opcodes[$i - 1]['jmpouts'])) {
  489. $nextbbs[$starti] = $i;
  490. $starti = $i;
  491. }
  492. }
  493. $nextbbs[$starti] = $cnt;
  494. $EX = array();
  495. $EX['Ts'] = array();
  496. $EX['indent'] = $indent;
  497. $EX['nextbbs'] = $nextbbs;
  498. $EX['op_array'] = &$op_array;
  499. $EX['opcodes'] = &$opcodes;
  500. // func call
  501. $EX['object'] = null;
  502. $EX['fbc'] = null;
  503. $EX['argstack'] = array();
  504. $EX['arg_types_stack'] = array();
  505. $EX['last'] = count($opcodes) - 1;
  506. $EX['silence'] = 0;
  507. for ($next = 0, $last = $EX['last'];
  508. $loop = $this->outputCode($EX, $next, $last, $indent, true);
  509. list($next, $last) = $loop) {
  510. // empty
  511. }
  512. return $EX;
  513. }
  514. // }}}
  515. function outputCode(&$EX, $opline, $last, $indent, $loop = false) // {{{
  516. {
  517. $op = &$EX['opcodes'][$opline];
  518. $next = $EX['nextbbs'][$opline];
  519. $end = $next - 1;
  520. if ($end > $last) {
  521. $end = $last;
  522. }
  523. if (isset($op['jmpins'])) {
  524. echo "\nline", $op['line'], ":\n";
  525. }
  526. else {
  527. // echo ";;;\n";
  528. }
  529. $this->dasmBasicBlock($EX, $opline, $end);
  530. $this->outputPhp($EX['opcodes'], $opline, $end, $indent);
  531. // jmpout op
  532. $op = &$EX['opcodes'][$end];
  533. $op1 = $op['op1'];
  534. $op2 = $op['op2'];
  535. $ext = $op['extended_value'];
  536. $line = $op['line'];
  537. if (isset($EX['opcodes'][$next])) {
  538. if (isset($last) && $next > $last) {
  539. $next = null;
  540. }
  541. }
  542. else {
  543. $next = null;
  544. }
  545. if ($op['opcode'] == XC_FE_FETCH) {
  546. $opline = $next;
  547. $next = $op['op2']['u.opline_num'];
  548. $end = $next - 1;
  549. ob_start();
  550. $this->outputCode($EX, $opline, $end /* - 1 skip last jmp */, $indent . INDENT);
  551. $body = ob_get_clean();
  552. $as = str($op['fe_as']);
  553. if (isset($op['fe_key'])) {
  554. $as = str($op['fe_key']) . ' => ' . $as;
  555. }
  556. echo "{$indent}foreach (" . str($op['fe_src']) . " as $as) {\n";
  557. echo $body;
  558. echo "{$indent}}";
  559. // $this->outputCode($EX, $next, $last, $indent);
  560. // return;
  561. }
  562. /*
  563. if ($op['opcode'] == XC_JMPZ) {
  564. $target = $op2['u.opline_num'];
  565. if ($line + 1) {
  566. $nextblock = $EX['nextbbs'][$next];
  567. $jmpop = end($nextblock);
  568. if ($jmpop['opcode'] == XC_JMP) {
  569. $ifendline = $op2['u.opline_num'];
  570. if ($ifendline >= $line) {
  571. $cond = $op['cond'];
  572. echo "{$indent}if ($cond) {\n";
  573. $this->outputCode($EX, $next, $last, INDENT . $indent);
  574. echo "$indent}\n";
  575. $this->outputCode($EX, $target, $last, $indent);
  576. return;
  577. }
  578. }
  579. }
  580. }
  581. */
  582. if (!isset($next)) {
  583. return;
  584. }
  585. if (!empty($op['jmpouts']) && isset($op['isjmp'])) {
  586. if (isset($op['cond'])) {
  587. echo "{$indent}check ($op[cond]) {\n";
  588. echo INDENT;
  589. }
  590. echo $indent;
  591. echo xcache_get_opcode($op['opcode']), ' line', $op['jmpouts'][0];
  592. if (isset($op['jmpouts'][1])) {
  593. echo ', line', $op['jmpouts'][1];
  594. }
  595. echo ";";
  596. // echo ' // <- line', $op['line'];
  597. echo "\n";
  598. if (isset($op['cond'])) echo "$indent}\n";
  599. }
  600. // proces JMPZ_EX/JMPNZ_EX for AND,OR
  601. $op = &$EX['opcodes'][$next];
  602. /*
  603. if (isset($op['jmpins'])) {
  604. foreach (array_reverse($op['jmpins']) as $fromline) {
  605. $fromop = $EX['opcodes'][$fromline];
  606. switch ($fromop['opcode']) {
  607. case XC_JMPZ_EX: $opstr = 'and'; break;
  608. case XC_JMPNZ_EX: $opstr = 'or'; break;
  609. case XC_JMPZNZ: var_dump($fromop); exit;
  610. default: continue 2;
  611. }
  612. $var = $fromop['result']['u.var'];
  613. var_dump($EX['Ts'][$var]);
  614. $EX['Ts'][$var] = '(' . $fromop['and_or'] . " $opstr " . $EX['Ts'][$var] . ')';
  615. }
  616. #$this->outputCode($EX, $next, $last, $indent);
  617. #return;
  618. }
  619. */
  620. if (isset($op['cond_false'])) {
  621. // $this->dumpop($op, $EX);
  622. // any true comes here, so it's a "or"
  623. $cond = implode(' and ', $op['cond_false']);
  624. // var_dump($op['cond'] = $cond);
  625. /*
  626. $rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue;
  627. unset($op['cond_true']);
  628. */
  629. }
  630. if ($loop) {
  631. return array($next, $last);
  632. }
  633. $this->outputCode($EX, $next, $last, $indent);
  634. }
  635. // }}}
  636. function unquoteName($str) // {{{
  637. {
  638. if (preg_match($this->rQuotedName, $str)) {
  639. $str = substr($str, 1, -1);
  640. }
  641. return $str;
  642. }
  643. // }}}
  644. function dasmBasicBlock(&$EX, $opline, $last) // {{{
  645. {
  646. $T = &$EX['Ts'];
  647. $opcodes = &$EX['opcodes'];
  648. $lastphpop = null;
  649. for ($i = $opline, $ic = $last + 1; $i < $ic; $i ++) {
  650. // {{{ prepair
  651. $op = &$opcodes[$i];
  652. $opc = $op['opcode'];
  653. if ($opc == XC_NOP) {
  654. continue;
  655. }
  656. $op1 = $op['op1'];
  657. $op2 = $op['op2'];
  658. $res = $op['result'];
  659. $ext = $op['extended_value'];
  660. $opname = xcache_get_opcode($opc);
  661. if ($opname == 'UNDEF' || !isset($opname)) {
  662. echo 'UNDEF OP:';
  663. $this->dumpop($op, $EX);
  664. continue;
  665. }
  666. // $this->dumpop($op, $EX); //var_dump($op);
  667. $resvar = null;
  668. if (($res['u.EA.type'] & EXT_TYPE_UNUSED) || $res['op_type'] == XC_IS_UNUSED) {
  669. $istmpres = false;
  670. }
  671. else {
  672. $istmpres = true;
  673. }
  674. // }}}
  675. // echo $opname, "\n";
  676. $call = array(&$this, $opname);
  677. if (is_callable($call)) {
  678. $this->{$opname}($op, $EX);
  679. }
  680. else if (isset($this->binops[$opc])) { // {{{
  681. $op1val = $this->getOpVal($op1, $EX, false);
  682. $op2val = $this->getOpVal($op2, $EX, false);
  683. $rvalue = new Decompiler_Binop($this, $op1val, $opc, $op2val);
  684. $resvar = $rvalue;
  685. // }}}
  686. }
  687. else if (isset($this->unaryops[$opc])) { // {{{
  688. $op1val = $this->getOpVal($op1, $EX);
  689. $myop = $this->unaryops[$opc];
  690. $rvalue = "$myop$op1val";
  691. $resvar = $rvalue;
  692. // }}}
  693. }
  694. else {
  695. switch ($opc) {
  696. case XC_NEW: // {{{
  697. array_push($EX['arg_types_stack'], array($EX['object'], $EX['fbc']));
  698. $EX['object'] = (int) $res['u.var'];
  699. $EX['fbc'] = 'new ' . $this->unquoteName($this->getOpVal($op1, $EX));
  700. if (PHP_VERSION < 5) {
  701. $resvar = '$new object$';
  702. }
  703. break;
  704. // }}}
  705. case XC_FETCH_CLASS: // {{{
  706. if ($op2['op_type'] == XC_IS_UNUSED) {
  707. switch ($ext) {
  708. case ZEND_FETCH_CLASS_SELF:
  709. $class = 'self';
  710. break;
  711. case ZEND_FETCH_CLASS_PARENT:
  712. $class = 'parent';
  713. }
  714. }
  715. else {
  716. $class = $op2['u.constant'];
  717. if (is_object($class)) {
  718. $class = get_class($class);
  719. }
  720. }
  721. $resvar = $class;
  722. break;
  723. // }}}
  724. case XC_FETCH_CONSTANT: // {{{
  725. if ($op1['op_type'] == XC_IS_CONST) {
  726. $resvar = $op1['u.constant'];
  727. }
  728. else if ($op1['op_type'] == XC_IS_UNUSED) {
  729. $resvar = $op2['u.constant'];
  730. }
  731. else {
  732. $class = $T[$op1['u.var']];
  733. assert($class[0] == 'class');
  734. $resvar = $class[1] . '::' . $op2['u.constant'];
  735. }
  736. break;
  737. // }}}
  738. // {{{ case XC_FETCH_*
  739. case XC_FETCH_R:
  740. case XC_FETCH_W:
  741. case XC_FETCH_RW:
  742. case XC_FETCH_FUNC_ARG:
  743. case XC_FETCH_UNSET:
  744. case XC_FETCH_IS:
  745. case XC_UNSET_VAR:
  746. $rvalue = $this->getOpVal($op1, $EX);
  747. $fetchtype = $op2[PHP_VERSION < 5 ? 'u.fetch_type' : 'u.EA.type'];
  748. switch ($fetchtype) {
  749. case ZEND_FETCH_STATIC_MEMBER:
  750. $class = $this->getOpVal($op2, $EX);
  751. $rvalue = $class . '::$' . $this->unquoteName($rvalue);
  752. break;
  753. default:
  754. $name = $this->unquoteName($rvalue);
  755. $globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[$rvalue]";
  756. $rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname);
  757. break;
  758. }
  759. if ($opc == XC_UNSET_VAR) {
  760. $op['php'] = "unset(" . str($rvalue) . ")";
  761. $lastphpop = &$op;
  762. }
  763. else if ($res['op_type'] != XC_IS_UNUSED) {
  764. $resvar = $rvalue;
  765. }
  766. break;
  767. // }}}
  768. // {{{ case XC_FETCH_DIM_*
  769. case XC_FETCH_DIM_TMP_VAR:
  770. case XC_FETCH_DIM_R:
  771. case XC_FETCH_DIM_W:
  772. case XC_FETCH_DIM_RW:
  773. case XC_FETCH_DIM_FUNC_ARG:
  774. case XC_FETCH_DIM_UNSET:
  775. case XC_FETCH_DIM_IS:
  776. case XC_ASSIGN_DIM:
  777. case XC_UNSET_DIM:
  778. case XC_UNSET_DIM_OBJ:
  779. $src = $this->getOpVal($op1, $EX, false);
  780. if (is_a($src, "Decompiler_ForeachBox")) {
  781. $src->iskey = $this->getOpVal($op2, $EX);
  782. $resvar = $src;
  783. break;
  784. }
  785. else if (is_a($src, "Decompiler_DimBox")) {
  786. $dimbox = $src;
  787. }
  788. else {
  789. if (!is_a($src, "Decompiler_ListBox")) {
  790. $list = new Decompiler_List($this->getOpVal($op1, $EX, false));
  791. $src = new Decompiler_ListBox($list);
  792. if (!isset($op1['u.var'])) {
  793. $this->dumpop($op, $EX);
  794. var_dump($op);
  795. die('missing u.var');
  796. }
  797. $T[$op1['u.var']] = $src;
  798. unset($list);
  799. }
  800. $dim = new Decompiler_Dim($src);
  801. $src->obj->dims[] = &$dim;
  802. $dimbox = new Decompiler_DimBox($dim);
  803. }
  804. $dim = &$dimbox->obj;
  805. $dim->offsets[] = $this->getOpVal($op2, $EX);
  806. if ($ext == ZEND_FETCH_ADD_LOCK) {
  807. $src->obj->everLocked = true;
  808. }
  809. else if ($ext == ZEND_FETCH_STANDARD) {
  810. $dim->isLast = true;
  811. }
  812. unset($dim);
  813. $rvalue = $dimbox;
  814. if ($opc == XC_ASSIGN_DIM) {
  815. $lvalue = $rvalue;
  816. ++ $i;
  817. $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
  818. $resvar = str($lvalue) . ' = ' . $rvalue;
  819. }
  820. else if ($opc == XC_UNSET_DIM) {
  821. $op['php'] = "unset(" . str($rvalue) . ")";
  822. $lastphpop = &$op;
  823. }
  824. else if ($res['op_type'] != XC_IS_UNUSED) {
  825. $resvar = $rvalue;
  826. }
  827. break;
  828. // }}}
  829. case XC_ASSIGN: // {{{
  830. $lvalue = $this->getOpVal($op1, $EX);
  831. $rvalue = $this->getOpVal($op2, $EX, false);
  832. if (is_a($rvalue, 'Decompiler_ForeachBox')) {
  833. $type = $rvalue->iskey ? 'fe_key' : 'fe_as';
  834. $rvalue->obj[$type] = $lvalue;
  835. unset($T[$op2['u.var']]);
  836. break;
  837. }
  838. if (is_a($rvalue, "Decompiler_DimBox")) {
  839. $dim = &$rvalue->obj;
  840. $dim->assign = $lvalue;
  841. if ($dim->isLast) {
  842. $resvar = str($dim->value);
  843. }
  844. unset($dim);
  845. break;
  846. }
  847. $resvar = "$lvalue = " . str($rvalue, $EX);
  848. break;
  849. // }}}
  850. case XC_ASSIGN_REF: // {{{
  851. $lvalue = $this->getOpVal($op1, $EX);
  852. $rvalue = $this->getOpVal($op2, $EX, false);
  853. if (is_a($rvalue, 'Decompiler_Fetch')) {
  854. $src = str($rvalue->src);
  855. if (substr($src, 1, -1) == substr($lvalue, 1)) {
  856. switch ($rvalue->fetchType) {
  857. case ZEND_FETCH_GLOBAL:
  858. $resvar = 'global ' . $lvalue;
  859. break 2;
  860. case ZEND_FETCH_STATIC:
  861. $statics = &$EX['op_array']['static_variables'];
  862. $resvar = 'static ' . $lvalue;
  863. $name = substr($src, 1, -1);
  864. if (isset($statics[$name])) {
  865. $var = $statics[$name];
  866. $resvar .= ' = ';
  867. $resvar .= str(value($var), $EX);
  868. }
  869. unset($statics);
  870. break 2;
  871. }
  872. }
  873. }
  874. $rvalue = str($rvalue);
  875. $resvar = "$lvalue = &$rvalue";
  876. break;
  877. // }}}
  878. // {{{ case XC_FETCH_OBJ_*
  879. case XC_FETCH_OBJ_R:
  880. case XC_FETCH_OBJ_W:
  881. case XC_FETCH_OBJ_RW:
  882. case XC_FETCH_OBJ_FUNC_ARG:
  883. case XC_FETCH_OBJ_UNSET:
  884. case XC_FETCH_OBJ_IS:
  885. case XC_ASSIGN_OBJ:
  886. $obj = $this->getOpVal($op1, $EX);
  887. if (!isset($obj)) {
  888. $obj = '$this';
  889. }
  890. $prop = $this->getOpVal($op2, $EX);
  891. if (preg_match($this->rQuotedName, $prop)) {
  892. $prop = substr($prop, 1, -1);;
  893. $rvalue = "{$obj}->$prop";
  894. }
  895. else {
  896. $rvalue = "{$obj}->{" . "$prop}";
  897. }
  898. if ($res['op_type'] != XC_IS_UNUSED) {
  899. $resvar = $rvalue;
  900. }
  901. if ($opc == XC_ASSIGN_OBJ) {
  902. ++ $i;
  903. $lvalue = $rvalue;
  904. $rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
  905. $resvar = "$lvalue = $rvalue";
  906. }
  907. break;
  908. // }}}
  909. case XC_ISSET_ISEMPTY_DIM_OBJ:
  910. case XC_ISSET_ISEMPTY_PROP_OBJ:
  911. case XC_ISSET_ISEMPTY:
  912. case XC_ISSET_ISEMPTY_VAR: // {{{
  913. if ($opc == XC_ISSET_ISEMPTY_VAR) {
  914. $rvalue = $this->getOpVal($op1, $EX);;
  915. if (preg_match($this->rQuotedName, $rvalue)) {
  916. $rvalue = '$' . substr($rvalue, 1, -1);
  917. }
  918. else {
  919. $rvalue = "${" . $rvalue . "}";
  920. }
  921. if ($op2['u.EA.type'] == ZEND_FETCH_STATIC_MEMBER) {
  922. $class = $this->getOpVal($op2, $EX);
  923. $rvalue = $class . '::' . $rvalue;
  924. }
  925. }
  926. else if ($opc == XC_ISSET_ISEMPTY) {
  927. $rvalue = $this->getOpVal($op1, $EX);
  928. }
  929. else {
  930. $container = $this->getOpVal($op1, $EX);
  931. $dim = $this->getOpVal($op2, $EX);
  932. $rvalue = $container . "[$dim]";
  933. }
  934. switch (PHP_VERSION < 5 ? $op['op2']['u.var'] /* u.constant */ : $ext) {
  935. case ZEND_ISSET:
  936. $rvalue = "isset($rvalue)";
  937. break;
  938. case ZEND_ISEMPTY:
  939. $rvalue = "empty($rvalue)";
  940. break;
  941. default:
  942. $this->dumpop($op, $EX);
  943. die_error('1');
  944. }
  945. $resvar = $rvalue;
  946. break;
  947. // }}}
  948. case XC_SEND_VAR_NO_REF:
  949. case XC_SEND_VAL:
  950. case XC_SEND_REF:
  951. case XC_SEND_VAR: // {{{
  952. $ref = ($opc == XC_SEND_REF ? '&' : '');
  953. $EX['argstack'][] = $ref . $this->getOpVal($op1, $EX);
  954. break;
  955. // }}}
  956. case XC_INIT_METHOD_CALL:
  957. case XC_INIT_FCALL_BY_FUNC:
  958. case XC_INIT_FCALL_BY_NAME: // {{{
  959. if (($ext & ZEND_CTOR_CALL)) {
  960. break;
  961. }
  962. array_push($EX['arg_types_stack'], array($EX['object'], $EX['fbc']));
  963. if ($opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) {
  964. $obj = $this->getOpVal($op1, $EX);
  965. if (!isset($obj)) {
  966. $obj = '$this';
  967. }
  968. $EX['object'] = $obj;
  969. if ($res['op_type'] != XC_IS_UNUSED) {
  970. $resvar = '$obj call$';
  971. }
  972. }
  973. else {
  974. $EX['object'] = null;
  975. }
  976. if ($opc == XC_INIT_FCALL_BY_FUNC) {
  977. $which = $op1['u.var'];
  978. $EX['fbc'] = $EX['op_array']['funcs'][$which]['name'];
  979. }
  980. else {
  981. $EX['fbc'] = $this->getOpVal($op2, $EX, false);
  982. }
  983. break;
  984. // }}}
  985. case XC_DO_FCALL_BY_FUNC:
  986. $which = $op1['u.var'];
  987. $fname = $EX['op_array']['funcs'][$which]['name'];
  988. $args = $this->popargs($EX, $ext);
  989. $resvar = $fname . "($args)";
  990. break;
  991. case XC_DO_FCALL:
  992. $fname = $this->unquoteName($this->getOpVal($op1, $EX, false));
  993. $args = $this->popargs($EX, $ext);
  994. $resvar = $fname . "($args)";
  995. break;
  996. case XC_DO_FCALL_BY_NAME: // {{{
  997. $object = null;
  998. $fname = $this->unquoteName($EX['fbc']);
  999. if (!is_int($EX['object'])) {
  1000. $object = $EX['object'];
  1001. }
  1002. $args = $this->popargs($EX, $ext);
  1003. $resvar =
  1004. (isset($object) ? $object . '->' : '' )
  1005. . $fname . "($args)";
  1006. unset($args);
  1007. if (is_int($EX['object'])) {
  1008. $T[$EX['object']] = $resvar;
  1009. $resvar = null;
  1010. }
  1011. list($EX['object'], $EX['fbc']) = array_pop($EX['arg_types_stack']);
  1012. break;
  1013. // }}}
  1014. case XC_VERIFY_ABSTRACT_CLASS: // {{{
  1015. //unset($T[$op1['u.var']]);
  1016. break;
  1017. // }}}
  1018. case XC_DECLARE_CLASS:
  1019. case XC_DECLARE_INHERITED_CLASS: // {{{
  1020. $key = $op1['u.constant'];
  1021. $class = &$this->dc['class_table'][$key];
  1022. if (!isset($class)) {
  1023. echo 'class not found: ' . $key;
  1024. exit;
  1025. }
  1026. $class['name'] = $this->unquoteName($this->getOpVal($op2, $EX));
  1027. if ($opc == XC_DECLARE_INHERITED_CLASS) {
  1028. $ext /= XC_SIZEOF_TEMP_VARIABLE;
  1029. $class['parent'] = $T[$ext];
  1030. unset($T[$ext]);
  1031. }
  1032. else {
  1033. $class['parent'] = null;
  1034. }
  1035. while ($i + 2 < $ic
  1036. && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
  1037. && $opcodes[$i + 2]['op1']['u.var'] == $res['u.var']
  1038. && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
  1039. $fetchop = &$opcodes[$i + 1];
  1040. $impl = $this->unquoteName($this->getOpVal($fetchop['op2'], $EX));
  1041. $addop = &$opcodes[$i + 2];
  1042. $class['interfaces'][$addop['extended_value']] = $impl;
  1043. unset($fetchop, $addop);
  1044. $i += 2;
  1045. }
  1046. $this->dclass($class);
  1047. unset($class);
  1048. break;
  1049. // }}}
  1050. case XC_INIT_STRING: // {{{
  1051. $resvar = "''";
  1052. break;
  1053. // }}}
  1054. case XC_ADD_CHAR:
  1055. case XC_ADD_STRING:
  1056. case XC_ADD_VAR: // {{{
  1057. $op1val = $this->getOpVal($op1, $EX);
  1058. $op2val = $this->getOpVal($op2, $EX);
  1059. switch ($opc) {
  1060. case XC_ADD_CHAR:
  1061. $op2val = str(chr($op2val), $EX);
  1062. break;
  1063. case XC_ADD_STRING:
  1064. $op2val = str($op2val, $EX);
  1065. break;
  1066. case XC_ADD_VAR:
  1067. break;
  1068. }
  1069. if ($op1val == "''") {
  1070. $rvalue = $op2val;
  1071. }
  1072. else if ($op2val == "''") {
  1073. $rvalue = $op1val;
  1074. }
  1075. else {
  1076. $rvalue = $op1val . ' . ' . $op2val;
  1077. }
  1078. $resvar = $rvalue;
  1079. // }}}
  1080. break;
  1081. case XC_PRINT: // {{{
  1082. $op1val = $this->getOpVal($op1, $EX);
  1083. $resvar = "print($op1val)";
  1084. break;
  1085. // }}}
  1086. case XC_ECHO: // {{{
  1087. $op1val = $this->getOpVal($op1, $EX);
  1088. $resvar = "echo $op1val";
  1089. break;
  1090. // }}}
  1091. case XC_EXIT: // {{{
  1092. $op1val = $this->getOpVal($op1, $EX);
  1093. $resvar = "exit($op1val)";
  1094. break;
  1095. // }}}
  1096. case XC_INIT_ARRAY:
  1097. case XC_ADD_ARRAY_ELEMENT: // {{{
  1098. $rvalue = $this->getOpVal($op1, $EX, false, true);
  1099. if ($opc == XC_ADD_ARRAY_ELEMENT) {
  1100. $offset = $this->getOpVal($op2, $EX);
  1101. if (isset($offset)) {
  1102. $T[$res['u.var']]->value[$offset] = $rvalue;
  1103. }
  1104. else {
  1105. $T[$res['u.var']]->value[] = $rvalue;
  1106. }
  1107. }
  1108. else {
  1109. if ($opc == XC_INIT_ARRAY) {
  1110. $resvar = new Decompiler_Array();
  1111. if (!isset($rvalue)) {
  1112. continue;
  1113. }
  1114. }
  1115. $offset = $this->getOpVal($op2, $EX);
  1116. if (isset($offset)) {
  1117. $resvar->value[$offset] = $rvalue;
  1118. }
  1119. else {
  1120. $resvar->value[] = $rvalue;
  1121. }
  1122. }
  1123. break;
  1124. // }}}
  1125. case XC_QM_ASSIGN: // {{{
  1126. $resvar = $this->getOpVal($op1, $EX);
  1127. break;
  1128. // }}}
  1129. case XC_BOOL: // {{{
  1130. $resvar = /*'(bool) ' .*/ $this->getOpVal($op1, $EX);
  1131. break;
  1132. // }}}
  1133. case XC_RETURN: // {{{
  1134. $resvar = "return " . $this->getOpVal($op1, $EX);
  1135. break;
  1136. // }}}
  1137. case XC_INCLUDE_OR_EVAL: // {{{
  1138. $type = $op2['u.var']; // hack
  1139. $keyword = $this->includeTypes[$type];
  1140. $resvar = "$keyword(" . $this->getOpVal($op1, $EX) . ")";
  1141. break;
  1142. // }}}
  1143. case XC_FE_RESET: // {{{
  1144. $resvar = $this->getOpVal($op1, $EX);
  1145. break;
  1146. // }}}
  1147. case XC_FE_FETCH: // {{{
  1148. $op['fe_src'] = $this->getOpVal($op1, $EX);
  1149. $fe = new Decompiler_ForeachBox($op);
  1150. $fe->iskey = false;
  1151. $T[$res['u.var']] = $fe;
  1152. ++ $i;
  1153. if (($ext & ZEND_FE_FETCH_WITH_KEY)) {
  1154. $fe = new Decompiler_ForeachBox($op);
  1155. $fe->iskey = true;
  1156. $res = $opcodes[$i]['result'];
  1157. $T[$res['u.var']] = $fe;
  1158. }
  1159. break;
  1160. // }}}
  1161. case XC_SWITCH_FREE: // {{{
  1162. // unset($T[$op1['u.var']]);
  1163. break;
  1164. // }}}
  1165. case XC_FREE: // {{{
  1166. $free = $T[$op1['u.var']];
  1167. if (!is_a($free, 'Decompiler_Array') && !is_a($free, 'Decompiler_Box')) {
  1168. $op['php'] = is_object($free) ? $free : $this->unquote($free, '(', ')');
  1169. $lastphpop = &$op;
  1170. }
  1171. unset($T[$op1['u.var']], $free);
  1172. break;
  1173. // }}}
  1174. case XC_JMP_NO_CTOR:
  1175. break;
  1176. case XC_JMPNZ: // while
  1177. case XC_JMPZNZ: // for
  1178. case XC_JMPZ_EX: // and
  1179. case XC_JMPNZ_EX: // or
  1180. case XC_JMPZ: // {{{
  1181. if ($opc == XC_JMP_NO_CTOR && $EX['object']) {
  1182. $rvalue = $EX['object'];
  1183. }
  1184. else {
  1185. $rvalue = $this->getOpVal($op1, $EX);
  1186. }
  1187. if (isset($op['cond_true'])) {
  1188. // any true comes here, so it's a "or"
  1189. $rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue;
  1190. unset($op['cond_true']);
  1191. }
  1192. if (isset($op['cond_false'])) {
  1193. var_dump($op);// exit;
  1194. }
  1195. if ($opc == XC_JMPZ_EX || $opc == XC_JMPNZ_EX || $opc == XC_JMPZ) {
  1196. $targetop = &$EX['opcodes'][$op2['u.opline_num']];
  1197. if ($opc == XC_JMPNZ_EX) {
  1198. $targetop['cond_true'][] = str($rvalue);
  1199. }
  1200. else {
  1201. $targetop['cond_false'][] = str($rvalue);
  1202. }
  1203. unset($targetop);
  1204. }
  1205. else {
  1206. $op['cond'] = $rvalue;
  1207. $op['isjmp'] = true;
  1208. }
  1209. break;
  1210. // }}}
  1211. case XC_JMP: // {{{
  1212. $op['cond'] = null;
  1213. $op['isjmp'] = true;
  1214. break;
  1215. // }}}
  1216. case XC_CASE:
  1217. case XC_BRK:
  1218. break;
  1219. case XC_RECV_INIT:
  1220. case XC_RECV:
  1221. $offset = $this->getOpVal($op1, $EX);
  1222. $lvalue = $this->getOpVal($op['result'], $EX);
  1223. if ($opc == XC_RECV_INIT) {
  1224. $default = value($op['op2']['u.constant']);
  1225. }
  1226. else {
  1227. $default = null;
  1228. }
  1229. $EX['recvs'][$offset] = array($lvalue, $default);
  1230. break;
  1231. case XC_POST_DEC:
  1232. case XC_POST_INC:
  1233. case XC_POST_DEC_OBJ:
  1234. case XC_POST_INC_OBJ:
  1235. case XC_PRE_DEC:
  1236. case XC_PRE_INC:
  1237. case XC_PRE_DEC_OBJ:
  1238. case XC_PRE_INC_OBJ: // {{{
  1239. $flags = array_flip(explode('_', $opname));
  1240. if (isset($flags['OBJ'])) {
  1241. $resvar = $this->getOpVal($op1, $EX);
  1242. $prop = $this->unquoteName($this->getOpVal($op2, $EX));
  1243. if ($prop{0} == '$') {
  1244. $resvar = $resvar . "{" . $prop . "}";
  1245. }
  1246. else {
  1247. $resvar = $resvar . "->" . $prop;
  1248. }
  1249. }
  1250. else {
  1251. $resvar = $this->getOpVal($op1, $EX);
  1252. }
  1253. $opstr = isset($flags['DEC']) ? '--' : '++';
  1254. if (isset($flags['POST'])) {
  1255. $resvar .= ' ' . $opstr;
  1256. }
  1257. else {
  1258. $resvar = "$opstr $resvar";
  1259. }
  1260. break;
  1261. // }}}
  1262. case XC_BEGIN_SILENCE: // {{{
  1263. $EX['silence'] ++;
  1264. break;
  1265. // }}}
  1266. case XC_END_SILENCE: // {{{
  1267. $EX['silence'] --;
  1268. $lastresvar = '@' . str($lastresvar);
  1269. break;
  1270. // }}}
  1271. case XC_CONT: // {{{
  1272. break;
  1273. // }}}
  1274. case XC_CAST: // {{{
  1275. $type = $ext;
  1276. static $type2cast = array(
  1277. IS_LONG => '(int)',
  1278. IS_DOUBLE => '(double)',
  1279. IS_STRING => '(string)',
  1280. IS_ARRAY => '(array)',
  1281. IS_OBJECT => '(object)',
  1282. IS_BOOL => '(bool)',
  1283. IS_NULL => '(unset)',
  1284. );
  1285. assert(isset($type2cast[$type]));
  1286. $cast = $type2cast[$type];
  1287. $resvar = $cast . ' ' . $this->getOpVal($op1, $EX);
  1288. break;
  1289. // }}}
  1290. case XC_EXT_STMT:
  1291. case XC_EXT_FCALL_BEGIN:
  1292. case XC_EXT_FCALL_END:
  1293. case XC_EXT_NOP:
  1294. break;
  1295. case XC_DECLARE_FUNCTION_OR_CLASS:
  1296. /* always removed by compiler */
  1297. break;
  1298. case XC_TICKS:
  1299. $lastphpop['ticks'] = $this->getOpVal($op1, $EX);
  1300. // $EX['tickschanged'] = true;
  1301. break;
  1302. default: // {{{
  1303. echo "\x1B[31m * TODO ", $opname, "\x1B[0m\n";
  1304. // }}}
  1305. }
  1306. }
  1307. if (isset($resvar)) {
  1308. if ($istmpres) {
  1309. $T[$res['u.var']] = $resvar;
  1310. $lastresvar = &$T[$res['u.var']];
  1311. }
  1312. else {
  1313. $op['php'] = $resvar;
  1314. $lastphpop = &$op;
  1315. $lastresvar = &$op['php'];
  1316. }
  1317. }
  1318. }
  1319. return $T;
  1320. }
  1321. // }}}
  1322. function unquote($str, $st, $ed) // {{{
  1323. {
  1324. $l1 = strlen($st);
  1325. $l2 = strlen($ed);
  1326. if (substr($str, 0, $l1) === $st && substr($str, -$l2) === $ed) {
  1327. $str = substr($str, $l1, -$l2);
  1328. }
  1329. return $str;
  1330. }
  1331. // }}}
  1332. function popargs(&$EX, $n) // {{{
  1333. {
  1334. $args = array();
  1335. for ($i = 0; $i < $n; $i ++) {
  1336. $a = array_pop($EX['argstack']);
  1337. if (is_array($a)) {
  1338. array_unshift($args, str($a, $EX));
  1339. }
  1340. else {
  1341. array_unshift($args, $a);
  1342. }
  1343. }
  1344. return implode(', ', $args);
  1345. }
  1346. // }}}
  1347. function dumpop($op, &$EX) // {{{
  1348. {
  1349. $op1 = $op['op1'];
  1350. $op2 = $op['op2'];
  1351. $d = array('opname' => xcache_get_opcode($op['opcode']), 'opcode' => $op['opcode']);
  1352. foreach (array('op1' => 'op1', 'op2' => 'op2', 'result' => 'res') as $k => $kk) {
  1353. switch ($op[$k]['op_type']) {
  1354. case XC_IS_UNUSED:
  1355. $d[$kk] = '*UNUSED* ' . $op[$k]['u.opline_num'];
  1356. break;
  1357. case XC_IS_VAR:
  1358. $d[$kk] = '$' . $op[$k]['u.var'];
  1359. if ($kk != 'res') {
  1360. $d[$kk] .= ':' . $this->getOpVal($op[$k], $EX);
  1361. }
  1362. break;
  1363. case XC_IS_TMP_VAR:
  1364. $d[$kk] = '#' . $op[$k]['u.var'];
  1365. if ($kk != 'res') {
  1366. $d[$kk] .= ':' . $this->getOpVal($op[$k], $EX);
  1367. }
  1368. break;
  1369. case XC_IS_CV:
  1370. $d[$kk] = $this->getOpVal($op[$k], $EX);
  1371. break;
  1372. default:
  1373. if ($kk == 'res') {
  1374. assert(0);
  1375. }
  1376. else {
  1377. $d[$kk] = $this->getOpVal($op[$k], $EX);
  1378. }
  1379. }
  1380. }
  1381. $d['ext'] = $op['extended_value'];
  1382. var_dump($d);
  1383. }
  1384. // }}}
  1385. function dargs(&$EX, $indent) // {{{
  1386. {
  1387. $EX['indent'] = $indent;
  1388. $op_array = &$EX['op_array'];
  1389. if (isset($op_array['num_args'])) {
  1390. $c = $op_array['num_args'];
  1391. }
  1392. else if ($op_array['arg_types']) {
  1393. $c = count($op_array['arg_types']);
  1394. }
  1395. else {
  1396. // php4
  1397. $c = count($EX['recvs']);
  1398. }
  1399. $refrest = false;
  1400. for ($i = 0; $i < $c; $i ++) {
  1401. if ($i) {
  1402. echo ', ';
  1403. }
  1404. if (isset($op_array['arg_info'])) {
  1405. $ai = $op_array['arg_info'][$i];
  1406. if (!empty($ai['class_name'])) {
  1407. echo $ai['class_name'], ' ';
  1408. if ($ai['allow_null']) {
  1409. echo 'or NULL ';
  1410. }
  1411. }
  1412. else if (!empty($ai['array_type_hint'])) {
  1413. echo 'array ';
  1414. if ($ai['allow_null']) {
  1415. echo 'or NULL ';
  1416. }
  1417. }
  1418. if ($ai['pass_by_reference']) {
  1419. echo '&';
  1420. }
  1421. printf("\$%s", $ai['name']);
  1422. }
  1423. else {
  1424. if ($refrest) {
  1425. echo '&';
  1426. }
  1427. else if (isset($op_array['arg_types'][$i])) {
  1428. switch ($op_array['arg_types'][$i]) {
  1429. case BYREF_FORCE_REST:
  1430. $refrest = true;
  1431. /* fall */
  1432. case BYREF_FORCE:
  1433. echo '&';
  1434. break;
  1435. case BYREF_NONE:
  1436. case BYREF_ALLOW:
  1437. break;
  1438. default:
  1439. assert(0);
  1440. }
  1441. }
  1442. $arg = $EX['recvs'][$i + 1];
  1443. echo str($arg[0]);
  1444. if (isset($arg[1])) {
  1445. echo ' = ', str($arg[1]);
  1446. }
  1447. }
  1448. }
  1449. }
  1450. // }}}
  1451. function dfunction($func, $indent = '', $nobody = false) // {{{
  1452. {
  1453. if ($nobody) {
  1454. $body = ";\n";
  1455. $EX = array();
  1456. $EX['op_array'] = &$func['op_array'];
  1457. $EX['recvs'] = array();
  1458. }
  1459. else {
  1460. ob_start();
  1461. $newindent = INDENT . $indent;
  1462. $EX = &$this->dop_array($func['op_array'], $newindent);
  1463. $body = ob_get_clean();
  1464. if (!isset($EX['recvs'])) {
  1465. $EX['recvs'] = array();
  1466. }
  1467. }
  1468. echo 'function ', $func['op_array']['function_name'], '(';
  1469. $this->dargs($EX, $indent);
  1470. echo ")\n";
  1471. echo $indent, "{\n";
  1472. echo $body;
  1473. echo "$indent}\n";
  1474. }
  1475. // }}}
  1476. function dclass($class, $indent = '') // {{{
  1477. {
  1478. // {{{ class decl
  1479. if (!empty($class['doc_comment'])) {
  1480. echo $indent;
  1481. echo $class['doc_comment'];
  1482. echo "\n";
  1483. }
  1484. $isinterface = false;
  1485. if (!empty($class['ce_flags'])) {
  1486. if ($class['ce_flags'] & ZEND_ACC_INTERFACE) {
  1487. echo 'interface ';
  1488. $isinterface = true;
  1489. }
  1490. else {
  1491. if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT) {
  1492. echo "abstract ";
  1493. }
  1494. if ($class['ce_flags'] & ZEND_ACC_FINAL) {
  1495. echo "final ";
  1496. }
  1497. }
  1498. }
  1499. echo 'class ', $class['name'];
  1500. if ($class['parent']) {
  1501. echo ' extends ', $class['parent'];
  1502. }
  1503. /* TODO */
  1504. if (!empty($class['interfaces'])) {
  1505. echo ' implements ';
  1506. echo implode(', ', $class['interfaces']);
  1507. }
  1508. echo "\n";
  1509. echo $indent, "{";
  1510. // }}}
  1511. $newindent = INDENT . $indent;
  1512. // {{{ const, static
  1513. foreach (array('constants_table' => 'const '
  1514. , 'static_members' => 'static $') as $type => $prefix) {
  1515. if (!empty($class[$type])) {
  1516. echo "\n";
  1517. // TODO: skip shadow?
  1518. foreach ($class[$type] as $name => $v) {
  1519. echo $newindent;
  1520. echo $prefix, $name, ' = ';
  1521. echo str(value($v), $EX);
  1522. echo ";\n";
  1523. }
  1524. }
  1525. }
  1526. // }}}
  1527. // {{{ properties
  1528. if (!empty($class['default_properties'])) {
  1529. echo "\n";
  1530. $infos = empty($class['properties_info']) ? null : $class['properties_info'];
  1531. foreach ($class['default_properties'] as $name => $v) {
  1532. $info = (isset($infos) && isset($infos[$name])) ? $infos[$name] : null;
  1533. if (isset($info)) {
  1534. if (!empty($info['doc_comment'])) {
  1535. echo $newindent;
  1536. echo $info['doc_comment'];
  1537. echo "\n";
  1538. }
  1539. }
  1540. echo $newindent;
  1541. if (PHP_VERSION < 5) {
  1542. echo 'var ';
  1543. }
  1544. else if (!isset($info)) {
  1545. echo 'public ';
  1546. }
  1547. else {
  1548. if ($info['flags'] & ZEND_ACC_SHADOW) {
  1549. continue;
  1550. }
  1551. switch ($info['flags'] & ZEND_ACC_PPP_MASK) {
  1552. case ZEND_ACC_PUBLIC:
  1553. echo "public ";
  1554. break;
  1555. case ZEND_ACC_PRIVATE:
  1556. echo "private ";
  1557. break;
  1558. case ZEND_ACC_PROTECTED:
  1559. echo "protected ";
  1560. break;
  1561. }
  1562. if ($info['flags'] & ZEND_ACC_STATIC) {
  1563. echo "static ";
  1564. }
  1565. }
  1566. echo '$', $name;
  1567. if (isset($v)) {
  1568. echo ' = ';
  1569. echo str(value($v));
  1570. }
  1571. echo ";\n";
  1572. }
  1573. }
  1574. // }}}
  1575. // {{{ function_table
  1576. if (isset($class['function_table'])) {
  1577. foreach ($class['function_table'] as $func) {
  1578. if (!isset($func['scope']) || $func['scope'] == $class['name']) {
  1579. // TODO: skip shadow here
  1580. echo "\n";
  1581. $opa = $func['op_array'];
  1582. if (!empty($opa['doc_comment'])) {
  1583. echo $newindent;
  1584. echo $opa['doc_comment'];
  1585. echo "\n";
  1586. }
  1587. echo $newindent;
  1588. if (isset($opa['fn_flags'])) {
  1589. if ($opa['fn_flags'] & ZEND_ACC_ABSTRACT) {
  1590. echo "abstract ";
  1591. }
  1592. if ($opa['fn_flags'] & ZEND_ACC_FINAL) {
  1593. echo "final ";
  1594. }
  1595. if ($opa['fn_flags'] & ZEND_ACC_STATIC) {
  1596. echo "static ";
  1597. }
  1598. switch ($opa['fn_flags'] & ZEND_ACC_PPP_MASK) {
  1599. case ZEND_ACC_PUBLIC:
  1600. echo "public ";
  1601. break;
  1602. case ZEND_ACC_PRIVATE:
  1603. echo "private ";
  1604. break;
  1605. case ZEND_ACC_PROTECTED:
  1606. echo "protected ";
  1607. break;
  1608. default:
  1609. echo "<visibility error> ";
  1610. break;
  1611. }
  1612. }
  1613. $this->dfunction($func, $newindent, $isinterface);
  1614. if ($opa['function_name'] == 'Decompiler') {
  1615. //exit;
  1616. }
  1617. }
  1618. }
  1619. }
  1620. // }}}
  1621. echo $indent, "}\n";
  1622. }
  1623. // }}}
  1624. function decompileString($string) // {{{
  1625. {
  1626. $this->dc = xcache_dasm_string($string);
  1627. if ($this->dc === false) {
  1628. echo "error compling string\n";
  1629. return false;
  1630. }
  1631. }
  1632. // }}}
  1633. function decompileFile($file) // {{{
  1634. {
  1635. $this->dc = xcache_dasm_file($file);
  1636. if ($this->dc === false) {
  1637. echo "error compling $file\n";
  1638. return false;
  1639. }
  1640. }
  1641. // }}}
  1642. function output() // {{{
  1643. {
  1644. echo "<?". "php\n";
  1645. foreach ($this->dc['class_table'] as $key => $class) {
  1646. if ($key{0} != "\0") {
  1647. echo "\n";
  1648. $this->dclass($class);
  1649. }
  1650. }
  1651. foreach ($this->dc['function_table'] as $key => $func) {
  1652. if ($key{0} != "\0") {
  1653. echo "\n";
  1654. $this->dfunction($func);
  1655. }
  1656. }
  1657. echo "\n";
  1658. $this->dop_array($this->dc['op_array']);
  1659. echo "\n?" . ">\n";
  1660. return true;
  1661. }
  1662. // }}}
  1663. }
  1664. // {{{ defines
  1665. define('ZEND_ACC_STATIC', 0x01);
  1666. define('ZEND_ACC_ABSTRACT', 0x02);
  1667. define('ZEND_ACC_FINAL', 0x04);
  1668. define('ZEND_ACC_IMPLEMENTED_ABSTRACT', 0x08);
  1669. define('ZEND_ACC_IMPLICIT_ABSTRACT_CLASS', 0x10);
  1670. define('ZEND_ACC_EXPLICIT_ABSTRACT_CLASS', 0x20);
  1671. define('ZEND_ACC_FINAL_CLASS', 0x40);
  1672. define('ZEND_ACC_INTERFACE', 0x80);
  1673. define('ZEND_ACC_PUBLIC', 0x100);
  1674. define('ZEND_ACC_PROTECTED', 0x200);
  1675. define('ZEND_ACC_PRIVATE', 0x400);
  1676. define('ZEND_ACC_PPP_MASK', (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE));
  1677. define('ZEND_ACC_CHANGED', 0x800);
  1678. define('ZEND_ACC_IMPLICIT_PUBLIC', 0x1000);
  1679. define('ZEND_ACC_CTOR', 0x2000);
  1680. define('ZEND_ACC_DTOR', 0x4000);
  1681. define('ZEND_ACC_CLONE', 0x8000);
  1682. define('ZEND_ACC_ALLOW_STATIC', 0x10000);
  1683. define('ZEND_ACC_SHADOW', 0x2000);
  1684. define('ZEND_FETCH_GLOBAL', 0);
  1685. define('ZEND_FETCH_LOCAL', 1);
  1686. define('ZEND_FETCH_STATIC', 2);
  1687. define('ZEND_FETCH_STATIC_MEMBER', 3);
  1688. define('ZEND_FETCH_GLOBAL_LOCK', 4);
  1689. define('ZEND_FETCH_CLASS_DEFAULT', 0);
  1690. define('ZEND_FETCH_CLASS_SELF', 1);
  1691. define('ZEND_FETCH_CLASS_PARENT', 2);
  1692. define('ZEND_FETCH_CLASS_MAIN', 3);
  1693. define('ZEND_FETCH_CLASS_GLOBAL', 4);
  1694. define('ZEND_FETCH_CLASS_AUTO', 5);
  1695. define('ZEND_FETCH_CLASS_INTERFACE', 6);
  1696. define('ZEND_EVAL', (1<<0));
  1697. define('ZEND_INCLUDE', (1<<1));
  1698. define('ZEND_INCLUDE_ONCE', (1<<2));
  1699. define('ZEND_REQUIRE', (1<<3));
  1700. define('ZEND_REQUIRE_ONCE', (1<<4));
  1701. define('ZEND_ISSET', (1<<0));
  1702. define('ZEND_ISEMPTY', (1<<1));
  1703. define('EXT_TYPE_UNUSED', (1<<0));
  1704. define('ZEND_FETCH_STANDARD', 0);
  1705. define('ZEND_FETCH_ADD_LOCK', 1);
  1706. define('ZEND_FE_FETCH_BYREF', 1);
  1707. define('ZEND_FE_FETCH_WITH_KEY', 2);
  1708. define('ZEND_MEMBER_FUNC_CALL', 1<<0);
  1709. define('ZEND_CTOR_CALL', 1<<1);
  1710. define('ZEND_ARG_SEND_BY_REF', (1<<0));
  1711. define('ZEND_ARG_COMPILE_TIME_BOUND', (1<<1));
  1712. define('ZEND_ARG_SEND_FUNCTION', (1<<2));
  1713. define('BYREF_NONE', 0);
  1714. define('BYREF_FORCE', 1);
  1715. define('BYREF_ALLOW', 2);
  1716. define('BYREF_FORCE_REST', 3);
  1717. define('IS_NULL', 0);
  1718. define('IS_LONG', 1);
  1719. define('IS_DOUBLE', 2);
  1720. define('IS_STRING', 3);
  1721. define('IS_ARRAY', 4);
  1722. define('IS_OBJECT', 5);
  1723. define('IS_BOOL', 6);
  1724. define('IS_RESOURCE', 7);
  1725. define('IS_CONSTANT', 8);
  1726. define('IS_CONSTANT_ARRAY', 9);
  1727. @define('XC_IS_CV', 16);
  1728. /*
  1729. if (preg_match_all('!XC_[A-Z_]+!', file_get_contents(__FILE__), $ms)) {
  1730. $verdiff = array();
  1731. foreach ($ms[0] as $k) {
  1732. if (!defined($k)) {
  1733. $verdiff[$k] = -1;
  1734. define($k, -1);
  1735. }
  1736. }
  1737. var_export($verdiff);
  1738. }
  1739. /*/
  1740. foreach (array (
  1741. 'XC_HANDLE_EXCEPTION' => -1,
  1742. 'XC_FETCH_CLASS' => -1,
  1743. 'XC_FETCH_' => -1,
  1744. 'XC_FETCH_DIM_' => -1,
  1745. 'XC_ASSIGN_DIM' => -1,
  1746. 'XC_UNSET_DIM' => -1,
  1747. 'XC_FETCH_OBJ_' => -1,
  1748. 'XC_ASSIGN_OBJ' => -1,
  1749. 'XC_ISSET_ISEMPTY_DIM_OBJ' => -1,
  1750. 'XC_ISSET_ISEMPTY_PROP_OBJ' => -1,
  1751. 'XC_ISSET_ISEMPTY_VAR' => -1,
  1752. 'XC_INIT_METHOD_CALL' => -1,
  1753. 'XC_VERIFY_ABSTRACT_CLASS' => -1,
  1754. 'XC_DECLARE_CLASS' => -1,
  1755. 'XC_DECLARE_INHERITED_CLASS' => -1,
  1756. 'XC_ADD_INTERFACE' => -1,
  1757. 'XC_POST_DEC_OBJ' => -1,
  1758. 'XC_POST_INC_OBJ' => -1,
  1759. 'XC_PRE_DEC_OBJ' => -1,
  1760. 'XC_PRE_INC_OBJ' => -1,
  1761. 'XC_UNSET_OBJ' => -1,
  1762. 'XC_JMP_NO_CTOR' => -1,
  1763. 'XC_FETCH_' => -1,
  1764. 'XC_FETCH_DIM_' => -1,
  1765. 'XC_UNSET_DIM_OBJ' => -1,
  1766. 'XC_FETCH_OBJ_' => -1,
  1767. 'XC_ISSET_ISEMPTY' => -1,
  1768. 'XC_INIT_FCALL_BY_FUNC' => -1,
  1769. 'XC_DO_FCALL_BY_FUNC' => -1,
  1770. 'XC_DECLARE_FUNCTION_OR_CLASS' => -1,
  1771. ) as $k => $v) {
  1772. if (!defined($k)) {
  1773. define($k, $v);
  1774. }
  1775. }
  1776. /* XC_UNDEF XC_OP_DATA
  1777. $content = file_get_contents(__FILE__);
  1778. for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
  1779. if (!preg_match("/\\bXC_" . $opname . "\\b(?!')/", $content)) {
  1780. echo "not done ", $opname, "\n";
  1781. }
  1782. }
  1783. // */
  1784. // }}}