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.
 
 
 
 
 
 

3597 lines
94 KiB

  1. <?php
  2. define('INDENT', "\t");
  3. ini_set('error_reporting', E_ALL);
  4. assert_options(ASSERT_ACTIVE, 0);
  5. if (function_exists("gc_disable")) {
  6. gc_collect_cycles();
  7. gc_disable();
  8. }
  9. function color($str, $color = 33)
  10. {
  11. return "\x1B[{$color}m$str\x1B[0m";
  12. }
  13. class Decompiler_Output
  14. {
  15. // {{{
  16. var $errorToOutput = true;
  17. var $target;
  18. function Decompiler_Output($target)
  19. {
  20. $this->target = $target;
  21. }
  22. // }}}
  23. var $indent = "";
  24. function indent() // {{{
  25. {
  26. $this->indent .= INDENT;
  27. }
  28. // }}}
  29. function outdent() // {{{
  30. {
  31. $this->indent = substr($this->indent, 0, -strlen(INDENT));
  32. }
  33. // }}}
  34. function writeOne_($code) // {{{
  35. {
  36. if ($this->target == STDOUT) {
  37. echo $code;
  38. }
  39. else {
  40. fwrite($this->target, $code);
  41. }
  42. }
  43. // }}}
  44. var $writes = 0;
  45. function write() // {{{
  46. {
  47. ++$this->writes;
  48. foreach (func_get_args() as $code) {
  49. if (is_object($code)) {
  50. $s = $code;
  51. $code = $code->toCode($this->indent);
  52. }
  53. if (is_array($code)) {
  54. call_user_func_array(array(&$this, 'write'), $code);
  55. }
  56. else {
  57. $this->writeOne_((string) $code);
  58. }
  59. }
  60. }
  61. // }}}
  62. function writeln() // {{{
  63. {
  64. if ($this->codeTypeCurrent != $this->codeTypeNext) {
  65. if ($this->codeTypeCurrent && $this->codeTypeNext == 'line') {
  66. $this->writeOne_(PHP_EOL);
  67. }
  68. $this->codeTypeCurrent = $this->codeTypeNext;
  69. }
  70. $this->writeOne_($this->indent);
  71. $args = func_get_args();
  72. call_user_func_array(array(&$this, 'write'), $args);
  73. $this->writeOne_(PHP_EOL);
  74. }
  75. // }}}
  76. function printfError($format) // {{{
  77. {
  78. $args = func_get_args();
  79. unset($args[0]);
  80. foreach ($args as $i => $arg) {
  81. unset($arg);
  82. if (is_object($args[$i])) {
  83. $args[$i] = $args[$i]->toCode($this->indent);
  84. }
  85. }
  86. $error = implode("", $args);
  87. trigger_error($error);
  88. if ($this->errorToOutput) {
  89. $this->beginComment();
  90. fwrite($this->target, $error);
  91. $this->endComment();
  92. }
  93. }
  94. // }}}
  95. var $inComment = 0;
  96. function beginComment() // {{{
  97. {
  98. if (!$this->inComment++) {
  99. $this->writeln("/*");
  100. }
  101. }
  102. // }}}
  103. function endComment() // {{{
  104. {
  105. if (!--$this->inComment) {
  106. $this->writeln("*/");
  107. }
  108. }
  109. // }}}
  110. var $scopeStack = array();
  111. var $codeTypeCurrent = null;
  112. var $codeTypeNext = 'line';
  113. /*
  114. line <- line
  115. something { <- complexblock
  116. line <- line
  117. } <- complexblock
  118. sdf <- line
  119. */
  120. function beginBlock_($isComplex) // {{{
  121. {
  122. // array_push($this->scopeStack, array($this->codeTypeCurrent, $this->codeTypeNext));
  123. array_push($this->scopeStack, $this->codeTypeNext);
  124. $this->codeTypeCurrent = null;
  125. $this->codeTypeNext = $isComplex ? 'complexblock' : 'line';
  126. }
  127. // }}}
  128. function endBlock_() // {{{
  129. {
  130. // list($this->codeTypeCurrent, $this->codeTypeNext) = array_pop($this->scopeStack);
  131. $this->codeTypeNext = array_pop($this->scopeStack);
  132. }
  133. // }}}
  134. function beginScope() // {{{
  135. {
  136. $this->beginBlock_(false);
  137. $this->indent();
  138. }
  139. // }}}
  140. function endScope() // {{{
  141. {
  142. $this->outdent();
  143. $this->endBlock_();
  144. }
  145. // }}}
  146. function beginComplexBlock() // {{{
  147. {
  148. if (isset($this->codeTypeCurrent)) {
  149. $this->writeOne_(PHP_EOL);
  150. }
  151. $this->beginBlock_(true);
  152. }
  153. // }}}
  154. function endComplexBlock() // {{{
  155. {
  156. $this->endBlock_();
  157. }
  158. // }}}
  159. }
  160. class Decompiler_Backtrace // {{{
  161. {
  162. function Decompiler_Backtrace()
  163. {
  164. $this->backtrace = debug_backtrace();
  165. array_shift($this->backtrace);
  166. }
  167. function toCode($indent)
  168. {
  169. $code = array();
  170. foreach ($this->backtrace as $stack) {
  171. $args = array();
  172. foreach ($stack['args'] as $arg) {
  173. if (is_scalar($arg)) {
  174. $args[] = var_export($arg, true);
  175. }
  176. else if (is_array($arg)) {
  177. $array = array();
  178. foreach ($arg as $key => $value) {
  179. $array[] = var_export($key, true) . " => " . (is_scalar($value) ? var_export($value, true) : gettype($value));
  180. if (count($array) >= 5) {
  181. $array[] = '...';
  182. break;
  183. }
  184. }
  185. $args[] = "array(" . implode(', ', $array) . ')';
  186. }
  187. else {
  188. $args[] = gettype($arg);
  189. }
  190. }
  191. $code[] = sprintf("%s%d: %s::%s(%s)" . PHP_EOL
  192. , $indent
  193. , $stack['line']
  194. , isset($stack['class']) ? $stack['class'] : ''
  195. , $stack['function']
  196. , implode(', ', $args)
  197. );
  198. }
  199. return implode("", $code);
  200. }
  201. }
  202. // }}}
  203. function printBacktrace() // {{{
  204. {
  205. $decompiler = &$GLOBALS['__xcache_decompiler'];
  206. $decompiler->output->beginComment();
  207. $decompiler->output->write(new Decompiler_Backtrace());
  208. $decompiler->output->endComment();
  209. }
  210. // }}}
  211. function decompileAst($ast) // {{{
  212. {
  213. $kind = $ast['kind'];
  214. $children = $ast['children'];
  215. unset($ast['kind']);
  216. unset($ast['children']);
  217. $decompiler = &$GLOBALS['__xcache_decompiler'];
  218. switch ($kind) {
  219. case ZEND_CONST:
  220. return value($ast[0]);
  221. case XC_INIT_ARRAY:
  222. $array = new Decompiler_Array($decompiler);
  223. for ($i = 0; $i < $children; $i += 2) {
  224. if (isset($ast[$i + 1])) {
  225. $key = decompileAst($ast[$i]);
  226. $value = decompileAst($ast[$i + 1]);
  227. $array->value[] = array($key, $value, '');
  228. }
  229. else {
  230. $array->value[] = array(null, decompileAst($ast[$i]), '');
  231. }
  232. }
  233. return $array;
  234. // ZEND_BOOL_AND: handled in binop
  235. // ZEND_BOOL_OR: handled in binop
  236. case ZEND_SELECT:
  237. return new Decompiler_TernaryOp($decompiler
  238. , decompileAst($ast[0])
  239. , decompileAst($ast[1])
  240. , decompileAst($ast[2])
  241. );
  242. case ZEND_UNARY_PLUS:
  243. return new Decompiler_UnaryOp($decompiler, XC_ADD, decompileAst($ast[0]));
  244. case ZEND_UNARY_MINUS:
  245. return new Decompiler_UnaryOp($decompiler, XC_SUB, decompileAst($ast[0]));
  246. default:
  247. if (isset($decompiler->binaryOp[$kind])) {
  248. return new Decompiler_BinaryOp($decompiler
  249. , decompileAst($ast[0])
  250. , $kind
  251. , decompileAst($ast[1])
  252. );
  253. }
  254. return "un-handled kind $kind in zend_ast";
  255. }
  256. }
  257. // }}}
  258. function value($value) // {{{
  259. {
  260. if (ZEND_ENGINE_2_6 && (xcache_get_type($value) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT_AST) {
  261. return decompileAst(xcache_dasm_ast($value));
  262. }
  263. $decompiler = &$GLOBALS['__xcache_decompiler'];
  264. $originalValue = xcache_get_special_value($value);
  265. if (isset($originalValue)) {
  266. if ((xcache_get_type($value) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT) {
  267. // constant
  268. return new Decompiler_Code($decompiler, $decompiler->stripNamespace($originalValue));
  269. }
  270. $value = $originalValue;
  271. }
  272. if (is_a($value, 'Decompiler_Object')) {
  273. // use as is
  274. }
  275. else if (is_array($value)) {
  276. $value = new Decompiler_ConstArray($decompiler, $value);
  277. }
  278. else {
  279. if (is_scalar($value) && ($constant = $decompiler->value2constant($value)) && isset($constant)) {
  280. $value = new Decompiler_Code($decompiler, $constant);
  281. }
  282. else {
  283. $value = new Decompiler_Value($decompiler, $value);
  284. }
  285. }
  286. return $value;
  287. }
  288. // }}}
  289. function unquoteName_($str, $asVariableName, $indent = '') // {{{
  290. {
  291. $str = is_object($str) ? $str->toCode($indent) : $str;
  292. if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
  293. return str_replace('\\\\', '\\', substr($str, 1, -1));
  294. }
  295. else if ($asVariableName) {
  296. return "{" . $str . "}";
  297. }
  298. else {
  299. return $str;
  300. }
  301. }
  302. // }}}
  303. function unquoteVariableName($str, $indent = '') // {{{
  304. {
  305. return unquoteName_($str, true, $indent);
  306. }
  307. // }}}
  308. function unquoteName($str, $indent = '') // {{{
  309. {
  310. return unquoteName_($str, false, $indent);
  311. }
  312. // }}}
  313. class Decompiler_Object // {{{
  314. {
  315. function toCode($indent)
  316. {
  317. return "";
  318. }
  319. }
  320. // }}}
  321. class Decompiler_Value extends Decompiler_Object // {{{
  322. {
  323. var $value;
  324. function Decompiler_Value(&$decompiler, $value = null)
  325. {
  326. $this->decompiler = &$decompiler;
  327. $this->value = $value;
  328. }
  329. function toCode($indent)
  330. {
  331. $code = var_export($this->value, true);
  332. if (gettype($this->value) == 'string') {
  333. $code = preg_replace_callback("![\t\r\n]+!", array(&$this, 'convertNewline'), $code);
  334. $code = preg_replace_callback("![\\x01-\\x1f]+!", array(&$this, 'escapeString'), $code);
  335. $code = preg_replace_callback("![\\x7f-\\xff]+!", array(&$this, 'escape8BitString'), $code);
  336. $code = preg_replace("!^'' \\. \"|\" \\. ''\$!", '"', $code);
  337. }
  338. return $code;
  339. }
  340. function convertNewline($m)
  341. {
  342. return "' . \"" . strtr($m[0], array("\t" => "\\t", "\r" => "\\r", "\n" => "\\n")) . "\" . '";
  343. }
  344. function escape8BitString($m)
  345. {
  346. if (function_exists("iconv") && @iconv("UTF-8", "UTF-8//IGNORE", $m[0]) == $m[0]) {
  347. return $m[0];
  348. }
  349. return $this->escapeString($m);
  350. }
  351. function escapeString($m)
  352. {
  353. $s = $m[0];
  354. $escaped = '';
  355. for ($i = 0, $c = strlen($s); $i < $c; ++$i) {
  356. $escaped .= "\\x" . dechex(ord($s[$i]));
  357. }
  358. return "' . \"" . $escaped . "\" . '";
  359. }
  360. }
  361. // }}}
  362. class Decompiler_Code extends Decompiler_Object // {{{
  363. {
  364. var $src;
  365. function Decompiler_Code(&$decompiler, $src)
  366. {
  367. $this->decompiler = &$decompiler;
  368. if (!assert('isset($src)')) {
  369. printBacktrace();
  370. }
  371. $this->src = $src;
  372. }
  373. function toCode($indent)
  374. {
  375. return $this->src;
  376. }
  377. }
  378. // }}}
  379. class Decompiler_Statements extends Decompiler_Code // {{{
  380. {
  381. var $src;
  382. function toCode($indent)
  383. {
  384. $code = array();
  385. foreach ($this->src as $i => $src) {
  386. if ($i) {
  387. $code[] = ', ';
  388. }
  389. $code[] = $src;
  390. }
  391. return $code;
  392. }
  393. }
  394. // }}}
  395. class Decompiler_UnaryOp extends Decompiler_Code // {{{
  396. {
  397. var $opc;
  398. var $op;
  399. function Decompiler_UnaryOp(&$decompiler, $opc, $op)
  400. {
  401. $this->decompiler = &$decompiler;
  402. $this->opc = $opc;
  403. $this->op = $op;
  404. }
  405. function toCode($indent)
  406. {
  407. $opstr = $this->decompiler->unaryOp[$this->opc];
  408. if (is_a($this->op, 'Decompiler_TernaryOp') || is_a($this->op, 'Decompiler_BinaryOp') && $this->op->opc != $this->opc) {
  409. $op = array("(", $this->op, ")");
  410. }
  411. else {
  412. $op = $this->op;
  413. }
  414. return array($opstr, $op);
  415. }
  416. }
  417. // }}}
  418. class Decompiler_BinaryOp extends Decompiler_Code // {{{
  419. {
  420. var $opc;
  421. var $op1;
  422. var $op2;
  423. function Decompiler_BinaryOp(&$decompiler, $op1, $opc, $op2)
  424. {
  425. $this->decompiler = &$decompiler;
  426. $this->opc = $opc;
  427. $this->op1 = $op1;
  428. $this->op2 = $op2;
  429. }
  430. function toCode($indent)
  431. {
  432. $opstr = $this->decompiler->binaryOp[$this->opc];
  433. if (is_a($this->op1, 'Decompiler_TernaryOp') || is_a($this->op1, 'Decompiler_BinaryOp') && $this->op1->opc != $this->opc) {
  434. $op1 = array("(", $this->op1, ")");
  435. }
  436. else {
  437. $op1 = $this->op1;
  438. }
  439. if (is_a($this->op2, 'Decompiler_TernaryOp') || is_a($this->op2, 'Decompiler_BinaryOp') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') {
  440. $op2 = array("(", $this->op2, ")");
  441. }
  442. else {
  443. $op2 = $this->op2;
  444. }
  445. if (is_a($op1, 'Decompiler_Value') && $op1->value == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
  446. $unaryOp = new Decompiler_UnaryOp($this->decompiler, $this->opc, $op2);
  447. return $unaryOp->toCode($indent);
  448. }
  449. return array($op1, ' ', $opstr, ($this->opc == XC_ASSIGN_REF ? '' : ' '), $op2);
  450. }
  451. }
  452. // }}}
  453. class Decompiler_TernaryOp extends Decompiler_Code // {{{
  454. {
  455. var $condition;
  456. var $trueValue;
  457. var $falseValue;
  458. function Decompiler_TernaryOp(&$decompiler, $condition, $trueValue, $falseValue)
  459. {
  460. $this->decompiler = &$decompiler;
  461. $this->condition = $condition;
  462. $this->trueValue = $trueValue;
  463. $this->falseValue = $falseValue;
  464. }
  465. function toCode($indent)
  466. {
  467. $trueValue = $this->trueValue;
  468. if (is_a($this->trueValue, 'Decompiler_TernaryOp')) {
  469. $trueValue = array("(", $trueValue, ")");
  470. }
  471. $falseValue = $this->falseValue;
  472. if (is_a($this->falseValue, 'Decompiler_TernaryOp')) {
  473. $falseValue = array("(", $falseValue, ")");
  474. }
  475. return array($this->condition, ' ? ', $trueValue, ' : ', $falseValue);
  476. }
  477. }
  478. // }}}
  479. class Decompiler_Fetch extends Decompiler_Code // {{{
  480. {
  481. var $fetchType;
  482. var $name;
  483. var $scope;
  484. function Decompiler_Fetch(&$decompiler, $scope, $name, $type)
  485. {
  486. $this->decompiler = &$decompiler;
  487. $this->scope = $scope;
  488. $this->name = $name;
  489. unset($this->src);
  490. $this->fetchType = $type;
  491. }
  492. function toCode($indent)
  493. {
  494. switch ($this->fetchType) {
  495. case XC_FETCH_PROPERTY:
  496. return array($this->scope, '->', $this->name);
  497. case ZEND_FETCH_STATIC_MEMBER:
  498. return array($this->scope, '::$', $this->name);
  499. case ZEND_FETCH_LOCAL:
  500. return array('$', $this->name);
  501. case ZEND_FETCH_STATIC:
  502. if (ZEND_ENGINE_2_3) {
  503. // closure local variable?
  504. return 'STR' . $this->name;
  505. }
  506. else {
  507. return value($this->name);
  508. }
  509. die('static fetch cant to string');
  510. case ZEND_FETCH_GLOBAL:
  511. case ZEND_FETCH_GLOBAL_LOCK:
  512. return xcache_is_autoglobal($this->name) ? array('$', $this->name) : array("\$GLOBALS[", value($this->name), "]");
  513. default:
  514. var_dump($this->fetchType);
  515. assert(0);
  516. }
  517. }
  518. }
  519. // }}}
  520. class Decompiler_Box extends Decompiler_Object // {{{
  521. {
  522. var $obj;
  523. function Decompiler_Box(&$decompiler, &$obj)
  524. {
  525. $this->decompiler = &$decompiler;
  526. $this->obj = &$obj;
  527. }
  528. function toCode($indent)
  529. {
  530. return $this->obj->toCode($indent);
  531. }
  532. }
  533. // }}}
  534. class Decompiler_Dim extends Decompiler_Value // {{{
  535. {
  536. var $offsets = array();
  537. var $isLast = false;
  538. var $isObject = false;
  539. var $assign = null;
  540. function toCode($indent)
  541. {
  542. if (is_a($this->value, 'Decompiler_ListBox')) {
  543. $exp = array($this->value->obj->src);
  544. }
  545. else {
  546. $exp = array($this->value);
  547. }
  548. $last = count($this->offsets) - 1;
  549. foreach ($this->offsets as $i => $dim) {
  550. if ($this->isObject && $i == $last) {
  551. $exp[] = '->';
  552. $exp[] = unquoteVariableName($dim, $indent);
  553. }
  554. else {
  555. $exp[] = '[';
  556. $exp[] = $dim;
  557. $exp[] = ']';
  558. }
  559. }
  560. return $exp;
  561. }
  562. }
  563. // }}}
  564. class Decompiler_DimBox extends Decompiler_Box // {{{
  565. {
  566. }
  567. // }}}
  568. class Decompiler_List extends Decompiler_Code // {{{
  569. {
  570. var $src;
  571. var $dims = array();
  572. var $everLocked = false;
  573. function toCode($indent)
  574. {
  575. if (count($this->dims) == 1 && !$this->everLocked) {
  576. $dim = $this->dims[0];
  577. unset($dim->value);
  578. $dim->value = $this->src;
  579. if (!isset($dim->assign)) {
  580. return $dim;
  581. }
  582. return array($this->dims[0]->assign, ' = ', $dim);
  583. }
  584. /* flatten dims */
  585. $assigns = array();
  586. foreach ($this->dims as $dim) {
  587. $assign = &$assigns;
  588. foreach ($dim->offsets as $offset) {
  589. $assign = &$assign[$offset];
  590. }
  591. $assign = $dim->assign;
  592. }
  593. return array($this->toList($assigns), ' = ', $this->src);
  594. }
  595. function toList($assigns)
  596. {
  597. $keys = array_keys($assigns);
  598. if (count($keys) < 2) {
  599. $keys[] = 0;
  600. }
  601. $max = call_user_func_array('max', $keys);
  602. $code = array("list(");
  603. for ($i = 0; $i <= $max; $i++) {
  604. if ($i) {
  605. $code[] = ', ';
  606. }
  607. if (!isset($assigns[$i])) {
  608. continue;
  609. }
  610. if (is_array($assigns[$i])) {
  611. $code[] = $this->toList($assigns[$i]);
  612. }
  613. else {
  614. $code[] = $assigns[$i];
  615. }
  616. }
  617. $code[] = ')';
  618. return $code;
  619. }
  620. }
  621. // }}}
  622. class Decompiler_ListBox extends Decompiler_Box // {{{
  623. {
  624. }
  625. // }}}
  626. class Decompiler_Array extends Decompiler_Value // {{{
  627. {
  628. // elements
  629. function Decompiler_Array(&$decompiler)
  630. {
  631. $this->decompiler = &$decompiler;
  632. $this->value = array();
  633. }
  634. function toCode($indent)
  635. {
  636. $subindent = $indent . INDENT;
  637. $elementsCode = array();
  638. $index = 0;
  639. foreach ($this->value as $element) {
  640. $key = $element[0];
  641. if (isset($key)) {
  642. $key = value($key);
  643. $keyCode = $key->toCode($subindent);
  644. }
  645. else {
  646. $keyCode = $index++;
  647. }
  648. $element[0] = $keyCode;
  649. $elementsCode[] = $element;
  650. }
  651. $code = array("array(");
  652. $indent = $indent . INDENT;
  653. $assocWidth = 0;
  654. $multiline = 0;
  655. $i = 0;
  656. foreach ($elementsCode as $element) {
  657. $keyCode = $element[0];
  658. if (is_array($keyCode) || (string) $i !== (string) $keyCode) {
  659. $assocWidth = 1;
  660. break;
  661. }
  662. ++$i;
  663. }
  664. foreach ($elementsCode as $element) {
  665. list($keyCode, $value) = $element;
  666. if ($assocWidth && !is_array($keyCode)) {
  667. $len = strlen($keyCode);
  668. if ($assocWidth < $len) {
  669. $assocWidth = $len;
  670. }
  671. }
  672. if (is_array($value) || is_a($value, 'Decompiler_Array')) {
  673. $multiline++;
  674. }
  675. }
  676. $i = 0;
  677. foreach ($elementsCode as $element) {
  678. list($keyCode, $value, $ref) = $element;
  679. if ($multiline) {
  680. if ($i) {
  681. $code[] = ",";
  682. }
  683. $code[] = PHP_EOL;
  684. $code[] = $indent;
  685. }
  686. else {
  687. if ($i) {
  688. $code[] = ", ";
  689. }
  690. }
  691. if ($assocWidth) {
  692. if ($multiline && !is_array($keyCode)) {
  693. $code[] = sprintf("%-{$assocWidth}s => ", $keyCode);
  694. }
  695. else {
  696. $code[] = $keyCode;
  697. $code[] = ' => ';
  698. }
  699. }
  700. $code[] = $ref;
  701. $value = value($value);
  702. $code[] = $value->toCode($subindent);
  703. $i++;
  704. }
  705. if ($multiline) {
  706. $code[] = PHP_EOL . "$indent)";
  707. }
  708. else {
  709. $code[] = ")";
  710. }
  711. return $code;
  712. }
  713. }
  714. // }}}
  715. class Decompiler_ConstArray extends Decompiler_Array // {{{
  716. {
  717. function Decompiler_ConstArray(&$decompiler, $array)
  718. {
  719. $this->decompiler = &$decompiler;
  720. $elements = array();
  721. foreach ($array as $key => $value) {
  722. if ((xcache_get_type($value) & IS_CONSTANT_INDEX)) {
  723. $keyCode = new Decompiler_Code($this, $GLOBALS['__xcache_decompiler']->stripNamespace(
  724. ZEND_ENGINE_2_3
  725. ? substr($key, 0, -2)
  726. : $key
  727. ));
  728. }
  729. else {
  730. $keyCode = $key;
  731. }
  732. $elements[] = array($keyCode, value($value), '');
  733. }
  734. $this->value = $elements;
  735. }
  736. }
  737. // }}}
  738. class Decompiler_ForeachBox extends Decompiler_Box // {{{
  739. {
  740. var $iskey;
  741. function toCode($indent)
  742. {
  743. return '#foreachBox#';
  744. }
  745. }
  746. // }}}
  747. class Decompiler
  748. {
  749. var $namespace;
  750. var $namespaceDecided;
  751. var $activeFile;
  752. var $activeDir;
  753. var $activeClass;
  754. var $activeMethod;
  755. var $activeFunction;
  756. var $outputPhp;
  757. var $outputOpcode;
  758. var $value2constant = array();
  759. var $output;
  760. function Decompiler($outputTypes)
  761. {
  762. $this->outputPhp = in_array('php', $outputTypes);
  763. $this->outputOpcode = in_array('opcode', $outputTypes);
  764. $this->output = new Decompiler_Output(STDOUT);
  765. $GLOBALS['__xcache_decompiler'] = $this;
  766. // {{{ testing
  767. // XC_UNDEF XC_OP_DATA
  768. $this->test = !empty($_ENV['XCACHE_DECOMPILER_TEST']);
  769. $this->usedOps = array();
  770. if ($this->test) {
  771. $content = file_get_contents(__FILE__);
  772. for ($i = 0; $opname = xcache_get_opcode($i); $i++) {
  773. if (!preg_match("/\\bXC_" . $opname . "\\b(?!')/", $content)) {
  774. echo "not recognized opcode ", $opname, PHP_EOL;
  775. }
  776. }
  777. }
  778. // }}}
  779. // {{{ opinfo
  780. $this->unaryOp = array(
  781. XC_BW_NOT => '~',
  782. XC_BOOL_NOT => '!',
  783. XC_ADD => "+",
  784. XC_SUB => "-",
  785. XC_NEW => "new ",
  786. XC_THROW => "throw ",
  787. XC_CLONE => "clone ",
  788. );
  789. $this->binaryOp = array(
  790. XC_ADD => "+",
  791. XC_ASSIGN_ADD => "+=",
  792. XC_SUB => "-",
  793. XC_ASSIGN_SUB => "-=",
  794. XC_MUL => "*",
  795. XC_ASSIGN_MUL => "*=",
  796. XC_DIV => "/",
  797. XC_ASSIGN_DIV => "/=",
  798. XC_MOD => "%",
  799. XC_ASSIGN_MOD => "%=",
  800. XC_SL => "<<",
  801. XC_ASSIGN_SL => "<<=",
  802. XC_SR => ">>",
  803. XC_ASSIGN_SR => ">>=",
  804. XC_CONCAT => ".",
  805. XC_ASSIGN_CONCAT => ".=",
  806. XC_POW => "**",
  807. XC_ASSIGN_POW => "*=",
  808. XC_IS_IDENTICAL => "===",
  809. XC_IS_NOT_IDENTICAL => "!==",
  810. XC_IS_EQUAL => "==",
  811. XC_IS_NOT_EQUAL => "!=",
  812. XC_IS_SMALLER => "<",
  813. XC_IS_SMALLER_OR_EQUAL => "<=",
  814. XC_BW_OR => "|",
  815. XC_ASSIGN_BW_OR => "|=",
  816. XC_BW_AND => "&",
  817. XC_ASSIGN_BW_AND => "&=",
  818. XC_BW_XOR => "^",
  819. XC_ASSIGN_BW_XOR => "^=",
  820. XC_BOOL_XOR => "xor",
  821. XC_ASSIGN => "=",
  822. XC_ASSIGN_DIM => "=",
  823. XC_ASSIGN_OBJ => "=",
  824. XC_ASSIGN_REF => "= &",
  825. XC_JMP_SET => "?:",
  826. XC_JMP_SET_VAR => "?:",
  827. XC_JMPZ_EX => "&&",
  828. XC_JMPNZ_EX => "||",
  829. XC_INSTANCEOF => "instanceof",
  830. );
  831. if (defined('IS_CONSTANT_AST')) {
  832. $this->binaryOp[ZEND_BOOL_AND] = '&&';
  833. $this->binaryOp[ZEND_BOOL_OR] = '||';
  834. }
  835. // }}}
  836. $this->includeTypes = array( // {{{
  837. ZEND_EVAL => 'eval',
  838. ZEND_INCLUDE => 'include',
  839. ZEND_INCLUDE_ONCE => 'include_once',
  840. ZEND_REQUIRE => 'require',
  841. ZEND_REQUIRE_ONCE => 'require_once',
  842. );
  843. // }}}
  844. }
  845. function detectNamespace($name) // {{{
  846. {
  847. if ($this->namespaceDecided) {
  848. return;
  849. }
  850. if (strpos($name, '\\') !== false) {
  851. $namespace = strtok($name, '\\');
  852. if ($namespace == $this->namespace) {
  853. return;
  854. }
  855. $this->namespace = $namespace;
  856. $this->output->beginComplexBlock();
  857. $this->output->writeln('namespace ', $this->namespace, ";");
  858. $this->output->endComplexBlock();
  859. }
  860. $this->namespaceDecided = true;
  861. }
  862. // }}}
  863. function stripNamespace($name) // {{{
  864. {
  865. if (!isset($name)) {
  866. return $name;
  867. }
  868. $len = strlen($this->namespace) + 1;
  869. if (!is_string($name)) {
  870. printBacktrace();
  871. exit;
  872. }
  873. if (strncasecmp($name, $this->namespace . '\\', $len) == 0) {
  874. return substr($name, $len);
  875. }
  876. else {
  877. return $name;
  878. }
  879. }
  880. // }}}
  881. function value2constant($value) // {{{
  882. {
  883. if (isset($this->EX) && isset($this->EX['value2constant'][$value])) {
  884. return $this->EX['value2constant'][$value];
  885. }
  886. else if (isset($this->value2constant[$value])) {
  887. return $this->value2constant[$value];
  888. }
  889. }
  890. // }}}
  891. function outputPhp($range) // {{{
  892. {
  893. $curticks = 0;
  894. for ($i = $range[0]; $i <= $range[1]; $i++) {
  895. $op = $this->EX['opcodes'][$i];
  896. if (isset($op['gofrom'])) {
  897. $this->output->beginComplexBlock();
  898. echo 'label' . $i, ":", PHP_EOL;
  899. $this->output->endComplexBlock();
  900. }
  901. if (isset($op['php'])) {
  902. $toticks = isset($op['ticks']) ? (int) $op['ticks'] : 0;
  903. if ($curticks != $toticks) {
  904. if ($curticks) {
  905. $this->output->endScope();
  906. $this->output->writeln("}");
  907. $this->output->endComplexBlock();
  908. }
  909. $curticks = $toticks;
  910. if ($curticks) {
  911. $this->output->beginComplexBlock();
  912. $this->output->writeln("declare (ticks=$curticks) {");
  913. $this->output->beginScope();
  914. }
  915. }
  916. $this->output->writeln($op['php'], ';');
  917. unset($op['php']);
  918. }
  919. }
  920. if ($curticks) {
  921. $this->output->endScope();
  922. $this->output->writeln("}");
  923. $this->output->endComplexBlock();
  924. }
  925. }
  926. // }}}
  927. function getOpVal($op, $free = false, $namespaced = false) // {{{
  928. {
  929. switch ($op['op_type']) {
  930. case XC_IS_CONST:
  931. if ($namespaced && isset($op['constant'])) {
  932. return $this->stripNamespace($op['constant']);
  933. }
  934. return value($op['constant']);
  935. case XC_IS_VAR:
  936. case XC_IS_TMP_VAR:
  937. $T = &$this->EX['Ts'];
  938. if (!isset($T[$op['var']])) {
  939. if ($this->outputPhp && isset($free)) {
  940. printBacktrace();
  941. }
  942. return null;
  943. }
  944. $ret = $T[$op['var']];
  945. if ($free && empty($this->keepTs)) {
  946. unset($T[$op['var']]);
  947. }
  948. return $ret;
  949. case XC_IS_CV:
  950. $vars = &$this->EX['op_array']['vars'];
  951. $var = $op['var'];
  952. return new Decompiler_Fetch($this, null, isset($vars[$var]) ? $vars[$var]['name'] : '?', ZEND_FETCH_LOCAL);
  953. case XC_IS_UNUSED:
  954. return null;
  955. }
  956. }
  957. // }}}
  958. function removeKeyPrefix($array, $prefix) // {{{
  959. {
  960. $prefixLen = strlen($prefix);
  961. $ret = array();
  962. foreach ($array as $key => $value) {
  963. if (substr($key, 0, $prefixLen) == $prefix) {
  964. $key = substr($key, $prefixLen);
  965. }
  966. $ret[$key] = $value;
  967. }
  968. return $ret;
  969. }
  970. // }}}
  971. function fixOpCode(&$opcodes, $removeTailing = false, $defaultReturnValue = null) // {{{
  972. {
  973. $last = count($opcodes) - 1;
  974. for ($i = 0; $i <= $last; $i++) {
  975. if (function_exists('xcache_get_fixed_opcode')) {
  976. $opcodes[$i]['opcode'] = xcache_get_fixed_opcode($opcodes[$i]['opcode'], $i);
  977. }
  978. if (isset($opcodes[$i]['op1'])) {
  979. $opcodes[$i]['op1'] = $this->removeKeyPrefix($opcodes[$i]['op1'], 'u.');
  980. $opcodes[$i]['op2'] = $this->removeKeyPrefix($opcodes[$i]['op2'], 'u.');
  981. $opcodes[$i]['result'] = $this->removeKeyPrefix($opcodes[$i]['result'], 'u.');
  982. }
  983. else {
  984. $op = array(
  985. 'op1' => array(),
  986. 'op2' => array(),
  987. 'result' => array(),
  988. );
  989. foreach ($opcodes[$i] as $name => $value) {
  990. if (preg_match('!^(op1|op2|result)\\.(.*)!', $name, $m)) {
  991. list(, $which, $field) = $m;
  992. $op[$which][$field] = $value;
  993. }
  994. else if (preg_match('!^(op1|op2|result)_type$!', $name, $m)) {
  995. list(, $which) = $m;
  996. $op[$which]['op_type'] = $value;
  997. }
  998. else {
  999. $op[$name] = $value;
  1000. }
  1001. }
  1002. $opcodes[$i] = $op;
  1003. }
  1004. }
  1005. if ($removeTailing) {
  1006. $last = count($opcodes) - 1;
  1007. if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
  1008. $this->usedOps[XC_HANDLE_EXCEPTION] = true;
  1009. $opcodes[$last]['opcode'] = XC_NOP;
  1010. --$last;
  1011. }
  1012. if ($opcodes[$last]['opcode'] == XC_RETURN
  1013. || $opcodes[$last]['opcode'] == XC_GENERATOR_RETURN) {
  1014. $op1 = $opcodes[$last]['op1'];
  1015. if ($op1['op_type'] == XC_IS_CONST && array_key_exists('constant', $op1) && $op1['constant'] === $defaultReturnValue) {
  1016. $opcodes[$last]['opcode'] = XC_NOP;
  1017. --$last;
  1018. }
  1019. }
  1020. }
  1021. }
  1022. // }}}
  1023. function decompileBasicBlock($range, $unhandled = false) // {{{
  1024. {
  1025. $this->dasmBasicBlock($range);
  1026. if ($unhandled) {
  1027. $this->dumpRange($range);
  1028. }
  1029. $this->outputPhp($range);
  1030. }
  1031. // }}}
  1032. function isIfCondition($range) // {{{
  1033. {
  1034. $opcodes = &$this->EX['opcodes'];
  1035. $firstOp = &$opcodes[$range[0]];
  1036. return $firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmptos']) && $opcodes[$firstOp['jmptos'][0] - 1]['opcode'] == XC_JMP
  1037. && !empty($opcodes[$firstOp['jmptos'][0] - 1]['jmptos'])
  1038. && $opcodes[$firstOp['jmptos'][0] - 1]['jmptos'][0] == $range[1] + 1;
  1039. }
  1040. // }}}
  1041. function removeJmpInfo($line) // {{{
  1042. {
  1043. $opcodes = &$this->EX['opcodes'];
  1044. if (!isset($opcodes[$line]['jmptos'])) {
  1045. printBacktrace();
  1046. }
  1047. foreach ($opcodes[$line]['jmptos'] as $jmpTo) {
  1048. $jmpfroms = &$opcodes[$jmpTo]['jmpfroms'];
  1049. $jmpfroms = array_flip($jmpfroms);
  1050. unset($jmpfroms[$line]);
  1051. $jmpfroms = array_keys($jmpfroms);
  1052. }
  1053. // $opcodes[$line]['opcode'] = XC_NOP;
  1054. unset($opcodes[$line]['jmptos']);
  1055. }
  1056. // }}}
  1057. function op($range, $offset) // {{{
  1058. {
  1059. $opcodes = &$this->EX['opcodes'];
  1060. if ($offset > 0) {
  1061. for ($i = $offset; $i <= $range[1]; ++$i) {
  1062. if ($opcodes[$i]['opcode'] != XC_NOP) {
  1063. return $i;
  1064. }
  1065. }
  1066. }
  1067. else {
  1068. for ($i = -$offset; $i >= $range[0]; --$i) {
  1069. if ($opcodes[$i]['opcode'] != XC_NOP) {
  1070. return $i;
  1071. }
  1072. }
  1073. }
  1074. return -1;
  1075. }
  1076. // }}}
  1077. function decompileComplexBlock($range) // {{{
  1078. {
  1079. $opcodes = &$this->EX['opcodes'];
  1080. $firstOp = &$opcodes[$this->op($range, $range[0])];
  1081. $lastOp = &$opcodes[$this->op($range, -$range[1])];
  1082. // {{{ && || and or
  1083. if (($firstOp['opcode'] == XC_JMPZ_EX || $firstOp['opcode'] == XC_JMPNZ_EX) && !empty($firstOp['jmptos'])
  1084. && $firstOp['jmptos'][0] == $range[1] + 1
  1085. && $lastOp['opcode'] == XC_BOOL
  1086. && $firstOp['opcode']['result']['var'] == $lastOp['opcode']['result']['var']
  1087. ) {
  1088. $this->removeJmpInfo($range[0]);
  1089. $this->recognizeAndDecompileClosedBlocks(array($range[0], $range[0]));
  1090. $op1 = $this->getOpVal($firstOp['result'], true);
  1091. $this->recognizeAndDecompileClosedBlocks(array($range[0] + 1, $range[1]));
  1092. $op2 = $this->getOpVal($lastOp['result'], true);
  1093. $this->EX['Ts'][$firstOp['result']['var']] = new Decompiler_BinaryOp($this, $op1, $firstOp['opcode'], $op2);
  1094. return false;
  1095. }
  1096. // }}}
  1097. // {{{ ?: excluding JMP_SET/JMP_SET_VAR
  1098. if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmptos'])
  1099. && $range[1] >= $range[0] + 3
  1100. && ($opcodes[$firstOp['jmptos'][0] - 2]['opcode'] == XC_QM_ASSIGN || $opcodes[$firstOp['jmptos'][0] - 2]['opcode'] == XC_QM_ASSIGN_VAR)
  1101. && $opcodes[$firstOp['jmptos'][0] - 1]['opcode'] == XC_JMP && $opcodes[$firstOp['jmptos'][0] - 1]['jmptos'][0] == $range[1] + 1
  1102. && ($lastOp['opcode'] == XC_QM_ASSIGN || $lastOp['opcode'] == XC_QM_ASSIGN_VAR)
  1103. ) {
  1104. $trueRange = array($range[0] + 1, $firstOp['jmptos'][0] - 2);
  1105. $falseRange = array($firstOp['jmptos'][0], $range[1]);
  1106. $this->removeJmpInfo($range[0]);
  1107. $condition = $this->getOpVal($firstOp['op1']);
  1108. $this->recognizeAndDecompileClosedBlocks($trueRange);
  1109. $trueValue = $this->getOpVal($opcodes[$trueRange[1]]['result'], true);
  1110. $this->recognizeAndDecompileClosedBlocks($falseRange);
  1111. $falseValue = $this->getOpVal($opcodes[$falseRange[1]]['result'], true);
  1112. $this->EX['Ts'][$opcodes[$trueRange[1]]['result']['var']] = new Decompiler_TernaryOp($this, $condition, $trueValue, $falseValue);
  1113. return false;
  1114. }
  1115. // }}}
  1116. // {{{ goto (TODO: recognize BRK which is translated to JMP by optimizer)
  1117. if ($firstOp['opcode'] == XC_JMP && !empty($firstOp['jmptos']) && $firstOp['jmptos'][0] == $range[1] + 1) {
  1118. $this->removeJmpInfo($range[0]);
  1119. assert(XC_GOTO != -1);
  1120. $firstOp['opcode'] = XC_GOTO;
  1121. $target = $firstOp['op1']['var'];
  1122. $firstOp['goto'] = $target;
  1123. $opcodes[$target]['gofrom'][] = $range[0];
  1124. $this->recognizeAndDecompileClosedBlocks($range);
  1125. return false;
  1126. }
  1127. // }}}
  1128. // {{{ search firstJmpOp
  1129. $firstJmpOp = null;
  1130. for ($i = $range[0]; $i <= $range[1]; ++$i) {
  1131. if (!empty($opcodes[$i]['jmptos'])) {
  1132. $firstJmpOp = &$opcodes[$i];
  1133. break;
  1134. }
  1135. }
  1136. // }}}
  1137. if (!isset($firstJmpOp)) {
  1138. return;
  1139. }
  1140. // {{{ search lastJmpOp
  1141. $lastJmpOp = null;
  1142. for ($i = $range[1]; $i > $firstJmpOp['line']; --$i) {
  1143. if (!empty($opcodes[$i]['jmptos'])) {
  1144. $lastJmpOp = &$opcodes[$i];
  1145. break;
  1146. }
  1147. }
  1148. // }}}
  1149. if ($this->decompile_foreach($range, $opcodes, $firstOp, $lastOp, $firstJmpOp, $lastJmpOp)) {
  1150. return true;
  1151. }
  1152. if ($this->decompile_while($range, $opcodes, $firstOp, $lastOp, $firstJmpOp)) {
  1153. return true;
  1154. }
  1155. if ($this->decompile_for($range, $opcodes, $firstOp, $lastOp)) {
  1156. return true;
  1157. }
  1158. if ($this->decompile_if($range, $opcodes, $firstOp, $lastOp)) {
  1159. return true;
  1160. }
  1161. if ($this->decompile_switch($range, $opcodes, $firstOp, $lastOp)) {
  1162. return true;
  1163. }
  1164. if ($this->decompile_tryCatch($range, $opcodes, $firstOp, $lastOp)) {
  1165. return true;
  1166. }
  1167. if ($this->decompile_doWhile($range, $opcodes, $firstOp, $lastOp)) {
  1168. return true;
  1169. }
  1170. $this->decompileBasicBlock($range, true);
  1171. }
  1172. // }}}
  1173. function decompile_for($range, &$opcodes, &$firstOp, &$lastOp) // {{{
  1174. {
  1175. if (!empty($firstOp['jmpfroms']) && $opcodes[$firstOp['jmpfroms'][0]]['opcode'] == XC_JMP
  1176. && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmptos']) && $lastOp['jmptos'][0] <= $firstOp['jmpfroms'][0]
  1177. && !empty($opcodes[$range[1] + 1]['jmpfroms']) && $opcodes[$opcodes[$range[1] + 1]['jmpfroms'][0]]['opcode'] == XC_JMPZNZ
  1178. ) {
  1179. $nextRange = array($lastOp['jmptos'][0], $firstOp['jmpfroms'][0]);
  1180. $conditionRange = array($range[0], $nextRange[0] - 1);
  1181. $this->removeJmpInfo($conditionRange[1]);
  1182. $bodyRange = array($nextRange[1], $range[1]);
  1183. $this->removeJmpInfo($bodyRange[1]);
  1184. $this->output->beginComplexBlock();
  1185. $initial = '';
  1186. $this->output->beginScope();
  1187. $this->dasmBasicBlock($conditionRange);
  1188. $conditionCodes = array();
  1189. for ($i = $conditionRange[0]; $i <= $conditionRange[1]; ++$i) {
  1190. if (isset($opcodes[$i]['php'])) {
  1191. $conditionCodes[] = $opcodes[$i]['php'];
  1192. }
  1193. }
  1194. $conditionCodes[] = $this->getOpVal($opcodes[$conditionRange[1]]['op1']);
  1195. if (count($conditionCodes) == 1 && $conditionCodes[0] == 'true') {
  1196. $conditionCodes = array();
  1197. }
  1198. $this->output->endScope();
  1199. $this->output->beginScope();
  1200. $this->dasmBasicBlock($nextRange);
  1201. $nextCodes = array();
  1202. for ($i = $nextRange[0]; $i <= $nextRange[1]; ++$i) {
  1203. if (isset($opcodes[$i]['php'])) {
  1204. $nextCodes[] = $opcodes[$i]['php'];
  1205. }
  1206. }
  1207. $this->output->endScope();
  1208. $this->output->writeln('for (', $initial, '; ', new Decompiler_Statements($this, $conditionCodes), '; ', new Decompiler_Statements($this, $nextCodes), ') ', '{');
  1209. $this->clearJmpInfo_brk_cont($bodyRange);
  1210. $this->output->beginScope();
  1211. $this->recognizeAndDecompileClosedBlocks($bodyRange);
  1212. $this->output->endScope();
  1213. $this->output->writeln('}');
  1214. $this->output->endComplexBlock();
  1215. return true;
  1216. }
  1217. }
  1218. // }}}
  1219. function decompile_if($range, &$opcodes, &$firstOp, &$lastOp) // {{{
  1220. {
  1221. if ($this->isIfCondition($range)) {
  1222. $this->output->beginComplexBlock();
  1223. $isElseIf = false;
  1224. do {
  1225. $ifRange = array($range[0], $opcodes[$range[0]]['jmptos'][0] - 1);
  1226. $this->removeJmpInfo($ifRange[0]);
  1227. $this->removeJmpInfo($ifRange[1]);
  1228. $condition = $this->getOpVal($opcodes[$ifRange[0]]['op1']);
  1229. $this->output->writeln($isElseIf ? 'else if' : 'if', ' (', $condition, ') ', '{');
  1230. $this->output->beginScope();
  1231. $this->recognizeAndDecompileClosedBlocks($ifRange);
  1232. $this->output->endScope();
  1233. $this->output->writeln("}");
  1234. $isElseIf = true;
  1235. // search for else if
  1236. $range[0] = $ifRange[1] + 1;
  1237. for ($i = $ifRange[1] + 1; $i <= $range[1]; ++$i) {
  1238. // find first jmpout
  1239. if (!empty($opcodes[$i]['jmptos'])) {
  1240. if ($this->isIfCondition(array($i, $range[1]))) {
  1241. $this->dasmBasicBlock(array($range[0], $i));
  1242. $range[0] = $i;
  1243. }
  1244. break;
  1245. }
  1246. }
  1247. } while ($this->isIfCondition($range));
  1248. if ($ifRange[1] <= $range[1]) {
  1249. $elseRange = array($ifRange[1], $range[1]);
  1250. $this->output->writeln('else ', '{');
  1251. $this->output->beginScope();
  1252. $this->recognizeAndDecompileClosedBlocks($elseRange);
  1253. $this->output->endScope();
  1254. $this->output->writeln("}");
  1255. }
  1256. $this->output->endComplexBlock();
  1257. return true;
  1258. }
  1259. if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmptos'])
  1260. && $firstOp['jmptos'][0] - 1 == $range[1]
  1261. && ($opcodes[$firstOp['jmptos'][0] - 1]['opcode'] == XC_RETURN || $opcodes[$firstOp['jmptos'][0] - 1]['opcode'] == XC_GENERATOR_RETURN)) {
  1262. $this->output->beginComplexBlock();
  1263. $this->removeJmpInfo($range[0]);
  1264. $condition = $this->getOpVal($opcodes[$range[0]]['op1']);
  1265. $this->output->writeln('if (', $condition, ') ', '{');
  1266. $this->output->beginScope();
  1267. $this->recognizeAndDecompileClosedBlocks($range);
  1268. $this->output->endScope();
  1269. $this->output->writeln('}');
  1270. $this->output->endComplexBlock();
  1271. return true;
  1272. }
  1273. }
  1274. // }}}
  1275. function decompile_tryCatch($range, &$opcodes, &$firstOp, &$lastOp) // {{{
  1276. {
  1277. if (!empty($firstOp['jmpfroms']) && !empty($opcodes[$firstOp['jmpfroms'][0]]['isCatchBegin'])) {
  1278. $catchBlocks = array();
  1279. $catchFirst = $firstOp['jmpfroms'][0];
  1280. $tryRange = array($range[0], $catchFirst - 1);
  1281. // search for XC_CATCH
  1282. for ($i = $catchFirst; $i <= $range[1]; ) {
  1283. if ($opcodes[$i]['opcode'] == XC_CATCH) {
  1284. $catchOpLine = $i;
  1285. $this->removeJmpInfo($catchFirst);
  1286. $catchNext = $opcodes[$catchOpLine]['extended_value'];
  1287. $catchBodyLast = $catchNext - 1;
  1288. if ($opcodes[$catchBodyLast]['opcode'] == XC_JMP) {
  1289. --$catchBodyLast;
  1290. }
  1291. $catchBlocks[$catchFirst] = array($catchOpLine, $catchBodyLast);
  1292. $i = $catchFirst = $catchNext;
  1293. }
  1294. else {
  1295. ++$i;
  1296. }
  1297. }
  1298. if ($opcodes[$tryRange[1]]['opcode'] == XC_JMP) {
  1299. --$tryRange[1];
  1300. }
  1301. $this->output->beginComplexBlock();
  1302. $this->output->writeln("try {");
  1303. $this->output->beginScope();
  1304. $this->recognizeAndDecompileClosedBlocks($tryRange);
  1305. $this->output->endScope();
  1306. $this->output->writeln("}");
  1307. if (!$catchBlocks) {
  1308. printBacktrace();
  1309. assert($catchBlocks);
  1310. }
  1311. foreach ($catchBlocks as $catchFirst => $catchInfo) {
  1312. list($catchOpLine, $catchBodyLast) = $catchInfo;
  1313. $catchBodyFirst = $catchOpLine + 1;
  1314. $this->dasmBasicBlock(array($catchFirst, $catchOpLine));
  1315. $catchOp = &$opcodes[$catchOpLine];
  1316. $this->output->writeln("catch ("
  1317. , $this->stripNamespace(isset($catchOp['op1']['constant']) ? $catchOp['op1']['constant'] : $this->getOpVal($catchOp['op1']))
  1318. , ' '
  1319. , isset($catchOp['op2']['constant']) ? '$' . $catchOp['op2']['constant'] : $this->getOpVal($catchOp['op2'])
  1320. , ") {"
  1321. );
  1322. unset($catchOp);
  1323. $this->output->beginScope();
  1324. $this->recognizeAndDecompileClosedBlocks(array($catchBodyFirst, $catchBodyLast));
  1325. $this->output->endScope();
  1326. $this->output->writeln("}");
  1327. }
  1328. $this->output->endComplexBlock();
  1329. return true;
  1330. }
  1331. }
  1332. // }}}
  1333. function decompile_switch($range, &$opcodes, &$firstOp, &$lastOp) // {{{
  1334. {
  1335. if ($firstOp['opcode'] == XC_CASE && !empty($lastOp['jmptos'])
  1336. || $firstOp['opcode'] == XC_JMP && !empty($firstOp['jmptos']) && $opcodes[$firstOp['jmptos'][0]]['opcode'] == XC_CASE && !empty($lastOp['jmptos'])
  1337. ) {
  1338. $this->clearJmpInfo_brk_cont($range);
  1339. $cases = array();
  1340. $caseDefault = null;
  1341. $caseOp = null;
  1342. for ($i = $range[0]; $i <= $range[1]; ) {
  1343. $op = $opcodes[$i];
  1344. if ($op['opcode'] == XC_CASE) {
  1345. if (!isset($caseOp)) {
  1346. $caseOp = $op;
  1347. }
  1348. $jmpz = $opcodes[$i + 1];
  1349. assert('$jmpz["opcode"] == XC_JMPZ');
  1350. $caseNext = $jmpz['jmptos'][0];
  1351. $cases[$i] = $caseNext - 1;
  1352. $i = $caseNext;
  1353. }
  1354. else if ($op['opcode'] == XC_JMP && $op['jmptos'][0] >= $i) {
  1355. // default
  1356. $caseNext = $op['jmptos'][0];
  1357. $caseDefault = $i;
  1358. $cases[$i] = $caseNext - 1;
  1359. $i = $caseNext;
  1360. }
  1361. else {
  1362. ++$i;
  1363. }
  1364. }
  1365. $this->output->beginComplexBlock();
  1366. $this->output->writeln('switch (', $this->getOpVal($caseOp['op1'], true), ") {");
  1367. $caseIsOut = false;
  1368. $caseExpressionBegin = $range[0];
  1369. foreach ($cases as $caseFirst => $caseLast) {
  1370. if ($caseExpressionBegin < $caseFirst) {
  1371. $this->recognizeAndDecompileClosedBlocks(array($caseExpressionBegin, $caseFirst - 1));
  1372. }
  1373. $caseExpressionBegin = $caseLast + 1;
  1374. if ($caseIsOut && empty($lastCaseFall)) {
  1375. echo PHP_EOL;
  1376. }
  1377. $caseOp = $opcodes[$caseFirst];
  1378. if ($caseOp['opcode'] == XC_CASE) {
  1379. $this->output->writeln('case ', $this->getOpVal($caseOp['op2']), ':');
  1380. $this->removeJmpInfo($caseFirst);
  1381. ++$caseFirst;
  1382. assert('$opcodes[$caseFirst]["opcode"] == XC_JMPZ');
  1383. $this->removeJmpInfo($caseFirst);
  1384. ++$caseFirst;
  1385. }
  1386. else {
  1387. $this->output->writeln('default:');
  1388. assert('$opcodes[$caseFirst]["opcode"] == XC_JMP');
  1389. $this->removeJmpInfo($caseFirst);
  1390. ++$caseFirst;
  1391. }
  1392. assert('$opcodes[$caseLast]["opcode"] == XC_JMP');
  1393. $this->removeJmpInfo($caseLast);
  1394. --$caseLast;
  1395. switch ($opcodes[$caseLast]['opcode']) {
  1396. case XC_BRK:
  1397. case XC_CONT:
  1398. case XC_GOTO:
  1399. $lastCaseFall = false;
  1400. break;
  1401. default:
  1402. $lastCaseFall = true;
  1403. }
  1404. $this->output->beginScope();
  1405. $this->recognizeAndDecompileClosedBlocks(array($caseFirst, $caseLast));
  1406. $this->output->endScope();
  1407. $caseIsOut = true;
  1408. }
  1409. $this->output->writeln('}');
  1410. $this->output->endComplexBlock();
  1411. return true;
  1412. }
  1413. }
  1414. // }}}
  1415. function decompile_doWhile($range, &$opcodes, &$firstOp, &$lastOp) // {{{
  1416. {
  1417. if ($lastOp['opcode'] == XC_JMPNZ && !empty($lastOp['jmptos'])
  1418. && $lastOp['jmptos'][0] == $range[0]) {
  1419. $this->removeJmpInfo($range[1]);
  1420. $this->clearJmpInfo_brk_cont($range);
  1421. $this->output->beginComplexBlock();
  1422. $this->output->writeln("do {");
  1423. $this->output->beginScope();
  1424. $this->recognizeAndDecompileClosedBlocks($range);
  1425. $this->output->endScope();
  1426. $this->output->writeln("} while (", $this->getOpVal($lastOp['op1']), ');');
  1427. $this->output->endComplexBlock();
  1428. return true;
  1429. }
  1430. }
  1431. // }}}
  1432. function decompile_while($range, &$opcodes, &$firstOp, &$lastOp, &$firstJmpOp) // {{{
  1433. {
  1434. if ($firstJmpOp['opcode'] == XC_JMPZ
  1435. && $firstJmpOp['jmptos'][0] > $range[1]
  1436. && $lastOp['opcode'] == XC_JMP
  1437. && !empty($lastOp['jmptos']) && $lastOp['jmptos'][0] == $range[0]) {
  1438. $this->removeJmpInfo($firstJmpOp['line']);
  1439. $this->removeJmpInfo($range[1]);
  1440. $this->output->beginComplexBlock();
  1441. ob_start();
  1442. $this->output->beginScope();
  1443. $this->recognizeAndDecompileClosedBlocks($range);
  1444. $this->output->endScope();
  1445. $body = ob_get_clean();
  1446. $this->output->writeln("while (", $this->getOpVal($firstJmpOp['op1']), ") {");
  1447. echo $body;
  1448. $this->output->writeln('}');
  1449. $this->output->endComplexBlock();
  1450. return true;
  1451. }
  1452. }
  1453. // }}}
  1454. function decompile_foreach($range, &$opcodes, &$firstOp, &$lastOp, &$firstJmpOp, &$lastJmpOp) // {{{
  1455. {
  1456. if ($firstJmpOp['opcode'] == XC_FE_FETCH
  1457. && !empty($firstJmpOp['jmptos']) && $firstJmpOp['jmptos'][0] > $lastJmpOp['line']
  1458. && isset($lastJmpOp)
  1459. && $lastJmpOp['opcode'] == XC_JMP
  1460. && !empty($lastJmpOp['jmptos']) && $lastJmpOp['jmptos'][0] == $firstJmpOp['line']) {
  1461. $this->removeJmpInfo($firstJmpOp['line']);
  1462. $this->removeJmpInfo($lastJmpOp['line']);
  1463. $this->clearJmpInfo_brk_cont($range);
  1464. $this->output->beginComplexBlock();
  1465. ob_start();
  1466. $this->output->beginScope();
  1467. $this->recognizeAndDecompileClosedBlocks($range);
  1468. $this->output->endScope();
  1469. $body = ob_get_clean();
  1470. $as = $firstJmpOp['fe_as'];
  1471. if (isset($firstJmpOp['fe_key'])) {
  1472. $as = array($firstJmpOp['fe_key'], ' => ', $as);
  1473. }
  1474. $this->output->writeln("foreach (", $firstJmpOp['fe_src'], " as ", $as, ") {");
  1475. echo $body;
  1476. $this->output->writeln('}');
  1477. $this->output->endComplexBlock();
  1478. return true;
  1479. }
  1480. }
  1481. // }}}
  1482. function recognizeAndDecompileClosedBlocks($range) // {{{ decompile in a tree way
  1483. {
  1484. $opcodes = &$this->EX['opcodes'];
  1485. if (count($opcodes) > 1000) {
  1486. $total = 70;
  1487. static $spinChars = array(
  1488. '-', '\\', '|', '/'
  1489. );
  1490. if (!isset($this->EX['bar'])) {
  1491. $this->EX['bar'] = str_repeat(' ', $total);
  1492. $this->EX['barX'] = 0;
  1493. }
  1494. $left = $total;
  1495. $bar = '';
  1496. $width = floor($total * $range[0] / count($opcodes));
  1497. $left -= $width;
  1498. $bar .= str_repeat('>', $width);
  1499. $width = ceil($total * ($range[1] - $range[0]) / count($opcodes));
  1500. if ($left && !$width) {
  1501. $width = 1;
  1502. }
  1503. $left -= $width;
  1504. $bar .= str_repeat($spinChars[$this->EX['barX']++ % count($spinChars)], $width);
  1505. $bar .= substr($this->EX['bar'], strlen($bar));
  1506. $this->EX['bar'] = $bar;
  1507. fwrite(STDERR, "\r[$bar]");
  1508. }
  1509. $ranges = array();
  1510. $starti = $range[0];
  1511. for ($i = $starti; $i <= $range[1]; ) {
  1512. if (!empty($opcodes[$i]['jmpfroms']) || !empty($opcodes[$i]['jmptos'])) {
  1513. $blockFirst = $i;
  1514. $blockLast = -1;
  1515. $j = $blockFirst;
  1516. do {
  1517. $op = $opcodes[$j];
  1518. if (!empty($op['jmpfroms'])) {
  1519. // care about jumping from blocks behind, not before
  1520. foreach ($op['jmpfroms'] as $oplineNumber) {
  1521. if ($oplineNumber <= $range[1] && $blockLast < $oplineNumber) {
  1522. $blockLast = $oplineNumber;
  1523. }
  1524. }
  1525. }
  1526. if (!empty($op['jmptos'])) {
  1527. $blockLast = max($blockLast, max($op['jmptos']) - 1);
  1528. }
  1529. ++$j;
  1530. } while ($j <= $blockLast);
  1531. if ($blockLast > $range[1]) {
  1532. fprintf(STDERR, "%d: \$blockLast(%d) > \$range[1](%d)\n", __LINE__, $blockLast, $range[1]);
  1533. assert('$blockLast <= $range[1]');
  1534. printBacktrace();
  1535. $this->dumpRange($range);
  1536. }
  1537. if ($blockLast >= $blockFirst) {
  1538. if ($blockFirst > $starti) {
  1539. $this->decompileBasicBlock(array($starti, $blockFirst - 1));
  1540. }
  1541. $this->decompileComplexBlock(array($blockFirst, $blockLast));
  1542. $starti = $blockLast + 1;
  1543. $i = $starti;
  1544. }
  1545. else {
  1546. ++$i;
  1547. }
  1548. }
  1549. else {
  1550. ++$i;
  1551. }
  1552. }
  1553. if ($starti <= $range[1]) {
  1554. $this->decompileBasicBlock(array($starti, $range[1]));
  1555. }
  1556. }
  1557. // }}}
  1558. function buildJmpInfo($range) // {{{ build jmpfroms/jmptos to op_array
  1559. {
  1560. $op_array = &$this->EX['op_array'];
  1561. $opcodes = &$this->EX['opcodes'];
  1562. for ($i = $range[0]; $i <= $range[1]; $i++) {
  1563. $op = &$opcodes[$i];
  1564. switch ($op['opcode']) {
  1565. case XC_CONT:
  1566. case XC_BRK:
  1567. $jmpTo = null;
  1568. if ($op['op2']['op_type'] == XC_IS_CONST && is_int($op['op2']['constant'])) {
  1569. $nestedLevel = $op['op2']['constant'];
  1570. $arrayOffset = $op['op1']['opline_num'];
  1571. // zend_brk_cont
  1572. while ($nestedLevel-- > 0) {
  1573. if ($arrayOffset == -1) {
  1574. $jmpTo = null;
  1575. break;
  1576. }
  1577. if (!isset($op_array['brk_cont_array'][$arrayOffset])) {
  1578. fprintf(STDERR, "%d: brk/cont not found at #$i\n", __LINE__);
  1579. break;
  1580. }
  1581. $jmpTo = $op_array['brk_cont_array'][$arrayOffset];
  1582. $arrayOffset = $jmpTo['parent'];
  1583. }
  1584. }
  1585. $op['jmptos'] = array();
  1586. if (isset($jmpTo)) {
  1587. $jmpTo = $jmpTo[$op['opcode'] == XC_CONT ? 'cont' : 'brk'];
  1588. $op['jmptos'][] = $jmpTo;
  1589. $opcodes[$jmpTo]['jmpfroms'][] = $i;
  1590. }
  1591. break;
  1592. case XC_GOTO:
  1593. $target = $op['op1']['var'];
  1594. if (!isset($opcodes[$target])) {
  1595. fprintf(STDERR, "%d: missing jump target at #$i" . PHP_EOL, __LINE__);
  1596. break;
  1597. }
  1598. $op['goto'] = $target;
  1599. $opcodes[$target]['gofrom'][] = $i;
  1600. break;
  1601. case XC_JMP:
  1602. $target = $op['op1']['var'];
  1603. if (!isset($opcodes[$target])) {
  1604. fprintf(STDERR, "%d: missing jump target at #$i" . PHP_EOL, __LINE__);
  1605. break;
  1606. }
  1607. $op['jmptos'] = array($target);
  1608. $opcodes[$target]['jmpfroms'][] = $i;
  1609. break;
  1610. case XC_JMPZNZ:
  1611. $jmpz = $op['op2']['opline_num'];
  1612. $jmpnz = $op['extended_value'];
  1613. if (!isset($opcodes[$jmpz])) {
  1614. fprintf(STDERR, "%d: missing jump target at #$i" . PHP_EOL, __LINE__);
  1615. break;
  1616. }
  1617. if (!isset($opcodes[$jmpnz])) {
  1618. fprintf(STDERR, "%d: missing jump target at #$i" . PHP_EOL, __LINE__);
  1619. break;
  1620. }
  1621. $op['jmptos'] = array($jmpz, $jmpnz);
  1622. $opcodes[$jmpz]['jmpfroms'][] = $i;
  1623. $opcodes[$jmpnz]['jmpfroms'][] = $i;
  1624. break;
  1625. case XC_JMPZ:
  1626. case XC_JMPNZ:
  1627. case XC_JMPZ_EX:
  1628. case XC_JMPNZ_EX:
  1629. // case XC_JMP_SET:
  1630. // case XC_JMP_SET_VAR:
  1631. // case XC_FE_RESET:
  1632. case XC_FE_FETCH:
  1633. // case XC_JMP_NO_CTOR:
  1634. $target = $op['op2']['opline_num'];
  1635. if (!isset($opcodes[$target])) {
  1636. fprintf(STDERR, "%d: missing jump target at #$i" . PHP_EOL, __LINE__);
  1637. break;
  1638. }
  1639. $op['jmptos'] = array($target);
  1640. $opcodes[$target]['jmpfroms'][] = $i;
  1641. break;
  1642. /*
  1643. case XC_RETURN:
  1644. $op['jmptos'] = array();
  1645. break;
  1646. */
  1647. case XC_CASE:
  1648. // just to link together
  1649. $op['jmptos'] = array($i + 2);
  1650. $opcodes[$i + 2]['jmpfroms'][] = $i;
  1651. break;
  1652. case XC_CATCH:
  1653. $catchNext = $op['extended_value'];
  1654. $catchBegin = $opcodes[$i - 1]['opcode'] == XC_FETCH_CLASS ? $i - 1 : $i;
  1655. $opcodes[$catchBegin]['jmptos'] = array($catchNext);
  1656. $opcodes[$catchNext]['jmpfroms'][] = $catchBegin;
  1657. break;
  1658. }
  1659. /*
  1660. if (!empty($op['jmptos']) || !empty($op['jmpfroms'])) {
  1661. echo $i, "\t", xcache_get_opcode($op['opcode']), PHP_EOL;
  1662. }
  1663. // */
  1664. }
  1665. unset($op);
  1666. if (isset($op_array['try_catch_array'])) {
  1667. foreach ($op_array['try_catch_array'] as $try_catch_element) {
  1668. $catch_op = $try_catch_element['catch_op'];
  1669. $opcodes[$catch_op]['isCatchBegin'] = true;
  1670. }
  1671. foreach ($op_array['try_catch_array'] as $try_catch_element) {
  1672. $catch_op = $try_catch_element['catch_op'];
  1673. $try_op = $try_catch_element['try_op'];
  1674. do {
  1675. $opcodes[$try_op]['jmpfroms'][] = $catch_op;
  1676. $opcodes[$catch_op]['jmptos'][] = $try_op;
  1677. if ($opcodes[$catch_op]['opcode'] == XC_CATCH) {
  1678. $catch_op = $opcodes[$catch_op]['extended_value'];
  1679. }
  1680. else if ($catch_op + 1 <= $range[1] && $opcodes[$catch_op + 1]['opcode'] == XC_CATCH) {
  1681. $catch_op = $opcodes[$catch_op + 1]['extended_value'];
  1682. }
  1683. else {
  1684. break;
  1685. }
  1686. } while ($catch_op <= $range[1] && empty($opcodes[$catch_op]['isCatchBegin']));
  1687. }
  1688. }
  1689. }
  1690. // }}}
  1691. function clearJmpInfo_brk_cont($range) // {{{ clear jmpfroms/jmptos for BRK/CONT relative to this range only
  1692. {
  1693. $opcodes = &$this->EX['opcodes'];
  1694. for ($i = $range[0]; $i <= $range[1]; $i++) {
  1695. $op = &$opcodes[$i];
  1696. switch ($op['opcode']) {
  1697. case XC_CONT:
  1698. case XC_BRK:
  1699. if (!empty($op['jmptos'])) {
  1700. if ($op['jmptos'][0] == $range[0]
  1701. || $op['jmptos'][0] == $range[1] + 1) {
  1702. $this->removeJmpInfo($i);
  1703. }
  1704. }
  1705. break;
  1706. }
  1707. }
  1708. unset($op);
  1709. }
  1710. // }}}
  1711. function &dop_array($op_array, $isFunction = false) // {{{
  1712. {
  1713. $this->fixOpCode($op_array['opcodes'], true, $isFunction ? null : 1);
  1714. $opcodes = &$op_array['opcodes'];
  1715. $EX = array();
  1716. $this->EX = &$EX;
  1717. $EX['Ts'] = $this->outputPhp ? array() : null;
  1718. $EX['op_array'] = &$op_array;
  1719. $EX['opcodes'] = &$opcodes;
  1720. // func call
  1721. $EX['object'] = null;
  1722. $EX['called_scope'] = null;
  1723. $EX['fbc'] = null;
  1724. $EX['argstack'] = array();
  1725. $EX['arg_types_stack'] = array();
  1726. $EX['silence'] = 0;
  1727. $EX['recvs'] = array();
  1728. $EX['uses'] = array();
  1729. $EX['value2constant'] = array();
  1730. if (isset($this->activeMethod)) {
  1731. $EX['value2constant'][$this->activeMethod] = '__METHOD__';
  1732. }
  1733. if (isset($this->activeFunction)) {
  1734. $EX['value2constant'][$this->activeFunction] = '__FUNCTION__';
  1735. }
  1736. $range = array(0, count($opcodes) - 1);
  1737. for ($i = $range[0]; $i <= $range[1]; $i++) {
  1738. $opcodes[$i]['line'] = $i;
  1739. }
  1740. $this->buildJmpInfo($range);
  1741. if ($this->outputOpcode) {
  1742. $this->keepTs = true;
  1743. $this->dumpRange($range);
  1744. $this->keepTs = false;
  1745. }
  1746. if ($this->outputPhp) {
  1747. // decompile in a tree way
  1748. $this->recognizeAndDecompileClosedBlocks($range);
  1749. }
  1750. unset($this->EX);
  1751. return $EX;
  1752. }
  1753. // }}}
  1754. function dasmBasicBlock($range) // {{{
  1755. {
  1756. $T = &$this->EX['Ts'];
  1757. $opcodes = &$this->EX['opcodes'];
  1758. $lastphpop = null;
  1759. for ($i = $range[0]; $i <= $range[1]; $i++) {
  1760. // {{{ prepair
  1761. $op = &$opcodes[$i];
  1762. $opc = $op['opcode'];
  1763. if ($opc == XC_NOP) {
  1764. $this->usedOps[$opc] = true;
  1765. continue;
  1766. }
  1767. $op1 = $op['op1'];
  1768. $op2 = $op['op2'];
  1769. $res = $op['result'];
  1770. $ext = $op['extended_value'];
  1771. $opname = xcache_get_opcode($opc);
  1772. if ($opname == 'UNDEF' || !isset($opname)) {
  1773. echo '// UNDEF OP:';
  1774. $this->dumpOp($op);
  1775. continue;
  1776. }
  1777. // echo $i, ' '; $this->dumpOp($op); //var_dump($op);
  1778. $resvar = null;
  1779. unset($curResVar);
  1780. if (array_key_exists($res['var'], $T)) {
  1781. $curResVar = &$T[$res['var']];
  1782. }
  1783. if ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) {
  1784. $istmpres = false;
  1785. }
  1786. else {
  1787. $istmpres = true;
  1788. }
  1789. // }}}
  1790. // echo $opname, PHP_EOL;
  1791. $notHandled = false;
  1792. switch ($opc) {
  1793. case XC_NEW: // {{{
  1794. array_push($this->EX['arg_types_stack'], array($this->EX['fbc'], $this->EX['object'], $this->EX['called_scope']));
  1795. $this->EX['object'] = $istmpres ? (int) $res['var'] : null;
  1796. $this->EX['called_scope'] = null;
  1797. $this->EX['fbc'] = new Decompiler_UnaryOp($this, $opc, $this->getOpVal($op1, false, true));
  1798. break;
  1799. // }}}
  1800. case XC_CATCH: // {{{
  1801. break;
  1802. // }}}
  1803. case XC_INSTANCEOF: // {{{
  1804. $resvar = new Decompiler_BinaryOp($this, $this->getOpVal($op1), $opc, $this->stripNamespace($this->getOpVal($op2)));
  1805. break;
  1806. // }}}
  1807. case XC_FETCH_CLASS: // {{{
  1808. if ($op2['op_type'] == XC_IS_UNUSED) {
  1809. switch (($ext & (defined('ZEND_FETCH_CLASS_MASK') ? ZEND_FETCH_CLASS_MASK : 0xFF))) {
  1810. case ZEND_FETCH_CLASS_SELF:
  1811. $class = 'self';
  1812. break;
  1813. case ZEND_FETCH_CLASS_PARENT:
  1814. $class = 'parent';
  1815. break;
  1816. case ZEND_FETCH_CLASS_STATIC:
  1817. $class = 'static';
  1818. break;
  1819. }
  1820. $istmpres = true;
  1821. }
  1822. else {
  1823. $class = $this->getOpVal($op2, true, true);
  1824. }
  1825. $resvar = $class;
  1826. break;
  1827. // }}}
  1828. case XC_FETCH_CONSTANT: // {{{
  1829. if ($op1['op_type'] == XC_IS_UNUSED) {
  1830. $resvar = $this->stripNamespace($op2['constant']);
  1831. break;
  1832. }
  1833. if ($op1['op_type'] == XC_IS_CONST) {
  1834. if (!ZEND_ENGINE_2) {
  1835. $resvar = $op1['constant'];
  1836. break;
  1837. }
  1838. $resvar = $this->stripNamespace($op1['constant']);
  1839. }
  1840. else {
  1841. $resvar = $this->getOpVal($op1);
  1842. }
  1843. $resvar = new Decompiler_Code($this, array($resvar, '::', unquoteName($this->getOpVal($op2))));
  1844. break;
  1845. // }}}
  1846. // {{{ case FETCH_*
  1847. case XC_FETCH_R:
  1848. case XC_FETCH_W:
  1849. case XC_FETCH_RW:
  1850. case XC_FETCH_FUNC_ARG:
  1851. case XC_FETCH_UNSET:
  1852. case XC_FETCH_IS:
  1853. $fetchType = defined('ZEND_FETCH_TYPE_MASK') ? ($ext & ZEND_FETCH_TYPE_MASK) : $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type'];
  1854. $name = isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1);
  1855. if ($fetchType == ZEND_FETCH_STATIC_MEMBER) {
  1856. $class = $this->getOpVal($op2, false, true);
  1857. }
  1858. else {
  1859. $class = null;
  1860. }
  1861. $rvalue = new Decompiler_Fetch($this, $class, $name, $fetchType);
  1862. if ($res['op_type'] != XC_IS_UNUSED) {
  1863. $resvar = $rvalue;
  1864. }
  1865. break;
  1866. // }}}
  1867. case XC_UNSET_VAR: // {{{
  1868. $fetchType = defined('ZEND_FETCH_TYPE_MASK') ? ($ext & ZEND_FETCH_TYPE_MASK) : $op2['EA.type'];
  1869. if ($fetchType == ZEND_FETCH_STATIC_MEMBER) {
  1870. $class = isset($op2['constant']) ? $op2['constant'] /* PHP5.3- */ : $this->getOpVal($op2);
  1871. $rvalue = $this->stripNamespace($class) . '::$' . $op1['constant'];
  1872. }
  1873. else {
  1874. $rvalue = isset($op1['constant']) ? '$' . $op1['constant'] /* PHP5.1- */ : $this->getOpVal($op1);
  1875. }
  1876. $op['php'] = array("unset(", $rvalue, ")");
  1877. $lastphpop = &$op;
  1878. break;
  1879. // }}}
  1880. // {{{ case FETCH_DIM_*
  1881. case XC_FETCH_DIM_TMP_VAR:
  1882. case XC_FETCH_DIM_R:
  1883. case XC_FETCH_DIM_W:
  1884. case XC_FETCH_DIM_RW:
  1885. case XC_FETCH_DIM_FUNC_ARG:
  1886. case XC_FETCH_DIM_UNSET:
  1887. case XC_FETCH_DIM_IS:
  1888. case XC_ASSIGN_DIM:
  1889. case XC_UNSET_DIM:
  1890. case XC_UNSET_DIM_OBJ:
  1891. case XC_UNSET_OBJ:
  1892. $src = $this->getOpVal($op1);
  1893. if (is_a($src, "Decompiler_ForeachBox")) {
  1894. assert($opc == XC_FETCH_DIM_TMP_VAR);
  1895. if (ZEND_ENGINE_2) {
  1896. $src = clone($src);
  1897. }
  1898. else {
  1899. $src = new Decompiler_ForeachBox($this, $src->obj);
  1900. }
  1901. $src->iskey = $op2['constant'];
  1902. $resvar = $src;
  1903. break;
  1904. }
  1905. if (is_a($src, "Decompiler_DimBox")) {
  1906. $dimbox = $src;
  1907. }
  1908. else {
  1909. if (!is_a($src, "Decompiler_ListBox")) {
  1910. $op1val = $this->getOpVal($op1);
  1911. $list = new Decompiler_List($this, isset($op1val) ? $op1val : '$this');
  1912. $src = new Decompiler_ListBox($this, $list);
  1913. if (!isset($op1['var'])) {
  1914. $this->dumpOp($op);
  1915. var_dump($op);
  1916. die('missing var');
  1917. }
  1918. $T[$op1['var']] = $src;
  1919. unset($list);
  1920. }
  1921. $dim = new Decompiler_Dim($this, $src);
  1922. $src->obj->dims[] = &$dim;
  1923. $dimbox = new Decompiler_DimBox($this, $dim);
  1924. }
  1925. $dim = &$dimbox->obj;
  1926. $dim->offsets[] = $this->getOpVal($op2);
  1927. /* TODO: use type mask */
  1928. if ($ext == ZEND_FETCH_ADD_LOCK) {
  1929. $src->obj->everLocked = true;
  1930. }
  1931. else if ($ext == ZEND_FETCH_STANDARD) {
  1932. $dim->isLast = true;
  1933. }
  1934. if ($opc == XC_UNSET_OBJ) {
  1935. $dim->isObject = true;
  1936. }
  1937. else if ($opc == XC_UNSET_DIM_OBJ) {
  1938. $dim->isObject = ZEND_ENGINE_2 ? $ext == ZEND_UNSET_OBJ : false /* cannot distingue */;
  1939. }
  1940. unset($dim);
  1941. $rvalue = $dimbox;
  1942. unset($dimbox);
  1943. if ($opc == XC_ASSIGN_DIM) {
  1944. $lvalue = $rvalue;
  1945. ++ $i;
  1946. $rvalue = $this->getOpVal($opcodes[$i]['op1']);
  1947. $resvar = new Decompiler_BinaryOp($this, $lvalue, $opc, $rvalue);
  1948. }
  1949. else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ || $opc == XC_UNSET_DIM_OBJ) {
  1950. $op['php'] = array("unset(", $rvalue, ")");
  1951. $lastphpop = &$op;
  1952. }
  1953. else if ($res['op_type'] != XC_IS_UNUSED) {
  1954. $resvar = $rvalue;
  1955. }
  1956. break;
  1957. // }}}
  1958. case XC_ASSIGN: // {{{
  1959. $lvalue = $this->getOpVal($op1);
  1960. $rvalue = $this->getOpVal($op2);
  1961. if (is_a($rvalue, 'Decompiler_ForeachBox')) {
  1962. $type = $rvalue->iskey ? 'fe_key' : 'fe_as';
  1963. $rvalue->obj[$type] = $lvalue;
  1964. unset($T[$op2['var']]);
  1965. break;
  1966. }
  1967. if (is_a($rvalue, "Decompiler_DimBox")) {
  1968. $dim = &$rvalue->obj;
  1969. $dim->assign = $lvalue;
  1970. if ($dim->isLast) {
  1971. $resvar = $dim->value;
  1972. }
  1973. unset($dim);
  1974. break;
  1975. }
  1976. if (is_a($lvalue, 'Decompiler_Fetch') && is_a($rvalue, 'Decompiler_Fetch')) {
  1977. if ($lvalue->name == $rvalue->name) {
  1978. switch ($rvalue->fetchType) {
  1979. case ZEND_FETCH_STATIC:
  1980. $statics = &$this->EX['op_array']['static_variables'];
  1981. if ((xcache_get_type($statics[$rvalue->name]) & IS_LEXICAL_VAR)) {
  1982. $this->EX['uses'][] = $lvalue;
  1983. unset($statics);
  1984. break 2;
  1985. }
  1986. unset($statics);
  1987. }
  1988. }
  1989. }
  1990. $resvar = new Decompiler_BinaryOp($this, $lvalue, XC_ASSIGN, $rvalue);
  1991. break;
  1992. // }}}
  1993. case XC_ASSIGN_REF: // {{{
  1994. $lvalue = $this->getOpVal($op1);
  1995. $rvalue = $this->getOpVal($op2);
  1996. if (is_a($lvalue, 'Decompiler_Fetch') && is_a($rvalue, 'Decompiler_Fetch')) {
  1997. if ($lvalue->name == $rvalue->name) {
  1998. switch ($rvalue->fetchType) {
  1999. case ZEND_FETCH_GLOBAL:
  2000. case ZEND_FETCH_GLOBAL_LOCK:
  2001. $resvar = new Decompiler_Code($this, array('global ', $lvalue));
  2002. break 2;
  2003. case ZEND_FETCH_STATIC:
  2004. $statics = &$this->EX['op_array']['static_variables'];
  2005. if ((xcache_get_type($statics[$rvalue->name]) & IS_LEXICAL_REF)) {
  2006. $this->EX['uses'][] = array('&', $lvalue);
  2007. unset($statics);
  2008. break 2;
  2009. }
  2010. $resvar = array();
  2011. $resvar[] = 'static ';
  2012. $resvar[] = $lvalue;
  2013. if (isset($statics[$rvalue->name])) {
  2014. $var = $statics[$rvalue->name];
  2015. $resvar[] = ' = ';
  2016. $resvar[] = value($var);
  2017. }
  2018. $resvar = new Decompiler_Code($this, $resvar);
  2019. unset($statics);
  2020. break 2;
  2021. default:
  2022. }
  2023. }
  2024. }
  2025. // TODO: PHP_6 global
  2026. $resvar = new Decompiler_BinaryOp($this, $lvalue, XC_ASSIGN_REF, $rvalue);
  2027. break;
  2028. // }}}
  2029. // {{{ case FETCH_OBJ_*
  2030. case XC_FETCH_OBJ_R:
  2031. case XC_FETCH_OBJ_W:
  2032. case XC_FETCH_OBJ_RW:
  2033. case XC_FETCH_OBJ_FUNC_ARG:
  2034. case XC_FETCH_OBJ_UNSET:
  2035. case XC_FETCH_OBJ_IS:
  2036. case XC_ASSIGN_OBJ:
  2037. $obj = $this->getOpVal($op1);
  2038. if (!isset($obj)) {
  2039. $obj = '$this';
  2040. }
  2041. $name = isset($op2['constant']) ? new Decompiler_Value($this, $op2['constant']) : $this->getOpVal($op2);
  2042. if ($res['op_type'] != XC_IS_UNUSED) {
  2043. $resvar = new Decompiler_Fetch($this, $obj, $name->value, XC_FETCH_PROPERTY);
  2044. }
  2045. if ($opc == XC_ASSIGN_OBJ) {
  2046. ++ $i;
  2047. $lvalue = $rvalue;
  2048. $rvalue = $this->getOpVal($opcodes[$i]['op1']);
  2049. $resvar = new Decompiler_BinaryOp($this, $lvalue, $opc, $rvalue);
  2050. }
  2051. break;
  2052. // }}}
  2053. case XC_ISSET_ISEMPTY_DIM_OBJ:
  2054. case XC_ISSET_ISEMPTY_PROP_OBJ:
  2055. case XC_ISSET_ISEMPTY:
  2056. case XC_ISSET_ISEMPTY_VAR: // {{{
  2057. if ($opc == XC_ISSET_ISEMPTY_VAR) {
  2058. $rvalue = $this->getOpVal($op1);
  2059. // for < PHP_5_3
  2060. if ($op1['op_type'] == XC_IS_CONST) {
  2061. $rvalue = '$' . unquoteVariableName($this->getOpVal($op1));
  2062. }
  2063. $fetchtype = defined('ZEND_FETCH_TYPE_MASK') ? ($ext & ZEND_FETCH_TYPE_MASK) : $op2['EA.type'];
  2064. if ($fetchtype == ZEND_FETCH_STATIC_MEMBER) {
  2065. $class = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2);
  2066. $rvalue = $this->stripNamespace($class) . '::' . unquoteName($rvalue, $this->EX);
  2067. }
  2068. }
  2069. else if ($opc == XC_ISSET_ISEMPTY) {
  2070. $rvalue = $this->getOpVal($op1);
  2071. }
  2072. else {
  2073. $container = $this->getOpVal($op1);
  2074. $dim = $this->getOpVal($op2);
  2075. if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) {
  2076. if (!isset($container)) {
  2077. $container = '$this';
  2078. }
  2079. $rvalue = array($container, "->", unquoteVariableName($dim));
  2080. }
  2081. else {
  2082. $rvalue = array($container, '[', $dim, ']');
  2083. }
  2084. }
  2085. switch (((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & ZEND_ISSET_ISEMPTY_MASK)) {
  2086. case ZEND_ISSET:
  2087. $rvalue = array("isset(", $rvalue, ")");
  2088. break;
  2089. case ZEND_ISEMPTY:
  2090. $rvalue = array("empty(", $rvalue, ")");
  2091. break;
  2092. }
  2093. $resvar = new Decompiler_Code($this, $rvalue);
  2094. break;
  2095. // }}}
  2096. case XC_SEND_VAR_NO_REF:
  2097. case XC_SEND_VAL:
  2098. case XC_SEND_REF:
  2099. case XC_SEND_VAR: // {{{
  2100. $ref = (!ZEND_ENGINE_2_4 && $opc == XC_SEND_REF ? '&' : '');
  2101. $this->EX['argstack'][] = array($ref, $this->getOpVal($op1));
  2102. break;
  2103. // }}}
  2104. case XC_INIT_STATIC_METHOD_CALL:
  2105. case XC_INIT_METHOD_CALL: // {{{
  2106. array_push($this->EX['arg_types_stack'], array($this->EX['fbc'], $this->EX['object'], $this->EX['called_scope']));
  2107. if ($opc == XC_INIT_STATIC_METHOD_CALL) {
  2108. $this->EX['object'] = null;
  2109. $this->EX['called_scope'] = $this->getOpVal($op1, false, true);
  2110. }
  2111. else {
  2112. $obj = $this->getOpVal($op1);
  2113. if (!isset($obj)) {
  2114. $obj = '$this';
  2115. }
  2116. $this->EX['object'] = $obj;
  2117. $this->EX['called_scope'] = null;
  2118. }
  2119. if ($res['op_type'] != XC_IS_UNUSED) {
  2120. $resvar = '$obj call$';
  2121. }
  2122. $this->EX['fbc'] = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2);
  2123. if (!isset($this->EX['fbc'])) {
  2124. $this->EX['fbc'] = '__construct';
  2125. }
  2126. break;
  2127. // }}}
  2128. case XC_INIT_NS_FCALL_BY_NAME:
  2129. case XC_INIT_FCALL_BY_NAME: // {{{
  2130. if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) {
  2131. break;
  2132. }
  2133. array_push($this->EX['arg_types_stack'], array($this->EX['fbc'], $this->EX['object'], $this->EX['called_scope']));
  2134. if (!ZEND_ENGINE_2 && ($ext & ZEND_MEMBER_FUNC_CALL)) {
  2135. if (isset($op1['constant'])) {
  2136. $this->EX['object'] = null;
  2137. $this->EX['called_scope'] = $this->stripNamespace($op1['constant']);
  2138. }
  2139. else {
  2140. $this->EX['object'] = $this->getOpVal($op1);
  2141. $this->EX['called_scope'] = null;
  2142. }
  2143. }
  2144. else {
  2145. $this->EX['object'] = null;
  2146. $this->EX['called_scope'] = null;
  2147. }
  2148. $this->EX['fbc'] = $this->getOpVal($op2, true, true);
  2149. break;
  2150. // }}}
  2151. case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4?
  2152. $this->EX['object'] = null;
  2153. $this->EX['called_scope'] = null;
  2154. $which = $op1['var'];
  2155. $this->EX['fbc'] = $this->EX['op_array']['funcs'][$which]['name'];
  2156. break;
  2157. // }}}
  2158. case XC_DO_FCALL_BY_FUNC:
  2159. $which = $op1['var'];
  2160. $fname = $this->EX['op_array']['funcs'][$which]['name'];
  2161. $args = $this->popargs($ext);
  2162. $resvar = new Decompiler_Code($this, array($fname, "(", $args, ")"));
  2163. break;
  2164. case XC_DO_FCALL:
  2165. $fname = unquoteName($this->getOpVal($op1), $this->EX);
  2166. $args = $this->popargs($ext);
  2167. $resvar = new Decompiler_Code($this, array($fname, "(", $args, ")"));
  2168. break;
  2169. case XC_DO_FCALL_BY_NAME: // {{{
  2170. $object = null;
  2171. if (!is_int($this->EX['object'])) {
  2172. $object = $this->EX['object'];
  2173. }
  2174. $code = array();
  2175. if (isset($object)) {
  2176. $code[] = $object;
  2177. $code[] = '->';
  2178. }
  2179. if (isset($this->EX['called_scope'])) {
  2180. $code[] = $this->EX['called_scope'];
  2181. $code[] = '::';
  2182. }
  2183. if (isset($this->EX['fbc'])) {
  2184. $code[] = $this->EX['fbc'];
  2185. }
  2186. $code[] = '(';
  2187. $code[] = $this->popargs($ext);
  2188. $code[] = ')';
  2189. $resvar = new Decompiler_Code($this, $code);
  2190. unset($code);
  2191. if (is_int($this->EX['object'])) {
  2192. $T[$this->EX['object']] = $resvar;
  2193. $resvar = null;
  2194. }
  2195. list($this->EX['fbc'], $this->EX['object'], $this->EX['called_scope']) = array_pop($this->EX['arg_types_stack']);
  2196. break;
  2197. // }}}
  2198. case XC_VERIFY_ABSTRACT_CLASS: // {{{
  2199. //unset($T[$op1['var']]);
  2200. break;
  2201. // }}}
  2202. case XC_DECLARE_CLASS:
  2203. case XC_DECLARE_INHERITED_CLASS:
  2204. case XC_DECLARE_INHERITED_CLASS_DELAYED: // {{{
  2205. $key = $op1['constant'];
  2206. // possible missing tailing \0 (outside of the string)
  2207. $key = substr($key . ".", 0, strlen($key));
  2208. if (!isset($this->dc['class_table'][$key])) {
  2209. echo $this->EX['indent'], "/* class not found: ", $key, ", existing classes are:", PHP_EOL;
  2210. var_dump(array_keys($this->dc['class_table']));
  2211. echo "*/", PHP_EOL;
  2212. break;
  2213. }
  2214. $class = &$this->dc['class_table'][$key];
  2215. $this->detectNamespace($class['name']);
  2216. if (!isset($class['name'])) {
  2217. $class['name'] = unquoteName($this->getOpVal($op2), $this->EX);
  2218. }
  2219. if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) {
  2220. if (ZEND_ENGINE_2_5) {
  2221. $ext = (0xffffffff - $ext + 1) / XC_SIZEOF_TEMP_VARIABLE - 1;
  2222. }
  2223. else {
  2224. $ext /= XC_SIZEOF_TEMP_VARIABLE;
  2225. }
  2226. $class['parent'] = $T[$ext];
  2227. unset($T[$ext]);
  2228. }
  2229. else {
  2230. $class['parent'] = null;
  2231. }
  2232. for (;;) {
  2233. if ($i + 1 <= $range[1]
  2234. && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE
  2235. && $opcodes[$i + 1]['op1']['var'] == $res['var']) {
  2236. // continue
  2237. }
  2238. else if ($i + 2 <= $range[1]
  2239. && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
  2240. && $opcodes[$i + 2]['op1']['var'] == $res['var']
  2241. && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
  2242. // continue
  2243. }
  2244. else {
  2245. break;
  2246. }
  2247. $this->usedOps[XC_ADD_INTERFACE] = true;
  2248. $fetchop = &$opcodes[$i + 1];
  2249. $interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2']), $this->EX));
  2250. $addop = &$opcodes[$i + 2];
  2251. $class['interfaces'][$addop['extended_value']] = $interface;
  2252. unset($fetchop, $addop);
  2253. $i += 2;
  2254. }
  2255. $this->activeClass = $class['name'];
  2256. $oldEX = &$this->EX;
  2257. unset($this->EX);
  2258. $this->dclass($class);
  2259. $this->EX = &$oldEX;
  2260. unset($oldEX);
  2261. $this->activeClass = null;
  2262. unset($class);
  2263. break;
  2264. // }}}
  2265. case XC_INIT_STRING: // {{{
  2266. $resvar = "''";
  2267. break;
  2268. // }}}
  2269. case XC_ADD_CHAR:
  2270. case XC_ADD_STRING:
  2271. case XC_ADD_VAR: // {{{
  2272. $op1val = $this->getOpVal($op1);
  2273. $op2val = $this->getOpVal($op2);
  2274. switch ($opc) {
  2275. case XC_ADD_CHAR:
  2276. $op2val = value(chr($op2val->value));
  2277. break;
  2278. case XC_ADD_STRING:
  2279. break;
  2280. case XC_ADD_VAR:
  2281. break;
  2282. }
  2283. if (!isset($op1val) == "''") {
  2284. $rvalue = $op2val;
  2285. }
  2286. else if (!isset($op2val) == "''") {
  2287. $rvalue = $op1val;
  2288. }
  2289. else {
  2290. $rvalue = new Decompiler_BinaryOp($this, $op1val, XC_CONCAT, $op2val);
  2291. }
  2292. $resvar = $rvalue;
  2293. // }}}
  2294. break;
  2295. case XC_PRINT: // {{{
  2296. $op1val = $this->getOpVal($op1);
  2297. $resvar = new Decompiler_Code($this, array("print(", $op1val, ")"));
  2298. break;
  2299. // }}}
  2300. case XC_ECHO: // {{{
  2301. $op1val = $this->getOpVal($op1);
  2302. $resvar = new Decompiler_Code($this, array("echo ", $op1val));
  2303. break;
  2304. // }}}
  2305. case XC_EXIT: // {{{
  2306. $op1val = $this->getOpVal($op1);
  2307. $resvar = new Decompiler_Code($this, array("exit(", $op1val, ")"));
  2308. break;
  2309. // }}}
  2310. case XC_INIT_ARRAY:
  2311. case XC_ADD_ARRAY_ELEMENT: // {{{
  2312. $rvalue = $this->getOpVal($op1, true);
  2313. $assoc = $this->getOpVal($op2);
  2314. $element = array($assoc, $rvalue, empty($ext) ? '' : '&');
  2315. if ($opc == XC_INIT_ARRAY) {
  2316. $resvar = new Decompiler_Array($this);
  2317. if (isset($rvalue)) {
  2318. $resvar->value[] = $element;
  2319. }
  2320. }
  2321. else {
  2322. $curResVar->value[] = $element;
  2323. }
  2324. unset($element);
  2325. break;
  2326. // }}}
  2327. case XC_QM_ASSIGN:
  2328. case XC_QM_ASSIGN_VAR: // {{{
  2329. if (isset($curResVar) && is_a($curResVar, 'Decompiler_BinaryOp')) {
  2330. $curResVar->op2 = $this->getOpVal($op1);
  2331. }
  2332. else {
  2333. $resvar = $this->getOpVal($op1);
  2334. }
  2335. break;
  2336. // }}}
  2337. case XC_BOOL: // {{{
  2338. $resvar = /*'(bool) ' .*/ $this->getOpVal($op1);
  2339. break;
  2340. // }}}
  2341. case XC_GENERATOR_RETURN:
  2342. case XC_RETURN_BY_REF:
  2343. case XC_RETURN: // {{{
  2344. $resvar = new Decompiler_Code($this, array("return ", $this->getOpVal($op1)));
  2345. break;
  2346. // }}}
  2347. case XC_INCLUDE_OR_EVAL: // {{{
  2348. $type = ZEND_ENGINE_2_4 ? $ext : $op2['var']; // hack
  2349. $keyword = $this->includeTypes[$type];
  2350. $rvalue = $this->getOpVal($op1);
  2351. if ($type == ZEND_EVAL) {
  2352. $resvar = new Decompiler_Code($this, array($keyword, "(", $rvalue, ")"));
  2353. }
  2354. else {
  2355. $resvar = new Decompiler_Code($this, array($keyword, " ", $rvalue));
  2356. }
  2357. break;
  2358. // }}}
  2359. case XC_FE_RESET: // {{{
  2360. $resvar = $this->getOpVal($op1);
  2361. break;
  2362. // }}}
  2363. case XC_FE_FETCH: // {{{
  2364. $op['fe_src'] = $this->getOpVal($op1, true);
  2365. $fe = new Decompiler_ForeachBox($this, $op);
  2366. $fe->iskey = false;
  2367. if (ZEND_ENGINE_2_1) {
  2368. // save current first
  2369. $T[$res['var']] = $fe;
  2370. // move to next opcode
  2371. ++ $i;
  2372. assert($opcodes[$i]['opcode'] == XC_OP_DATA);
  2373. $fe = new Decompiler_ForeachBox($this, $op);
  2374. $fe->iskey = true;
  2375. $res = $opcodes[$i]['result'];
  2376. }
  2377. $resvar = $fe;
  2378. break;
  2379. // }}}
  2380. case XC_YIELD: // {{{
  2381. $resvar = new Decompiler_Code($this, array('yield ', $this->getOpVal($op1)));
  2382. break;
  2383. // }}}
  2384. case XC_SWITCH_FREE: // {{{
  2385. if (isset($T[$op1['var']])) {
  2386. $this->output->beginComplexBlock();
  2387. $this->output->writeln('switch (', $this->getOpVal($op1), ") {");
  2388. $this->output->writeln('}');
  2389. $this->output->endComplexBlock();
  2390. }
  2391. break;
  2392. // }}}
  2393. case XC_FREE: // {{{
  2394. $free = $T[$op1['var']];
  2395. if (!is_a($free, 'Decompiler_Box')) {
  2396. $op['php'] = is_object($free) || is_array($free) ? $free : $this->unquote($free, '(', ')');
  2397. $lastphpop = &$op;
  2398. }
  2399. unset($T[$op1['var']], $free);
  2400. break;
  2401. // }}}
  2402. case XC_JMP_NO_CTOR:
  2403. break;
  2404. case XC_JMPZ_EX: // and
  2405. case XC_JMPNZ_EX: // or
  2406. $resvar = $this->getOpVal($op1);
  2407. break;
  2408. case XC_JMPNZ: // while
  2409. case XC_JMPZNZ: // for
  2410. case XC_JMPZ: // {{{
  2411. break;
  2412. // }}}
  2413. case XC_CONT:
  2414. case XC_BRK:
  2415. $resvar = $opc == XC_CONT ? 'continue' : 'break';
  2416. $count = $this->getOpVal($op2);
  2417. if ($count->value != 1) {
  2418. $resvar .= ' ' . $count->value;
  2419. }
  2420. break;
  2421. case XC_GOTO:
  2422. $resvar = 'goto label' . $op['op1']['var'];
  2423. $istmpres = false;
  2424. break;
  2425. case XC_JMP: // {{{
  2426. break;
  2427. // }}}
  2428. case XC_CASE:
  2429. // $switchValue = $this->getOpVal($op1);
  2430. $caseValue = $this->getOpVal($op2);
  2431. $resvar = $caseValue;
  2432. break;
  2433. case XC_RECV_INIT:
  2434. case XC_RECV:
  2435. $offset = isset($op1['var']) ? $op1['var'] : $op1['constant'];
  2436. $lvalue = $this->getOpVal($op['result']);
  2437. if ($opc == XC_RECV_INIT) {
  2438. $default = value($op['op2']['constant']);
  2439. }
  2440. else {
  2441. $default = null;
  2442. }
  2443. $this->EX['recvs'][$offset] = array($lvalue, $default);
  2444. break;
  2445. case XC_POST_DEC:
  2446. case XC_POST_INC:
  2447. case XC_POST_DEC_OBJ:
  2448. case XC_POST_INC_OBJ:
  2449. case XC_PRE_DEC:
  2450. case XC_PRE_INC:
  2451. case XC_PRE_DEC_OBJ:
  2452. case XC_PRE_INC_OBJ: // {{{
  2453. $flags = array_flip(explode('_', $opname));
  2454. if (isset($flags['OBJ'])) {
  2455. $code = array($this->getOpVal($op1), '->', $op2['constant']);
  2456. }
  2457. else {
  2458. $code = array($this->getOpVal($op1));
  2459. }
  2460. $opstr = isset($flags['DEC']) ? '--' : '++';
  2461. if (isset($flags['POST'])) {
  2462. $code[] = $opstr;
  2463. }
  2464. else {
  2465. array_unshift($code, $opstr);
  2466. }
  2467. $resvar = new Decompiler_Code($this, $code);
  2468. break;
  2469. // }}}
  2470. case XC_BEGIN_SILENCE: // {{{
  2471. $this->EX['silence']++;
  2472. break;
  2473. // }}}
  2474. case XC_END_SILENCE: // {{{
  2475. $this->EX['silence']--;
  2476. $lastresvar = new Decompiler_Code($this, array('@', $lastresvar));
  2477. break;
  2478. // }}}
  2479. case XC_CAST: // {{{
  2480. $type = $ext;
  2481. static $type2cast = array(
  2482. IS_LONG => '(int)',
  2483. IS_DOUBLE => '(double)',
  2484. IS_STRING => '(string)',
  2485. IS_ARRAY => '(array)',
  2486. IS_OBJECT => '(object)',
  2487. IS_BOOL => '(bool)',
  2488. IS_NULL => '(unset)',
  2489. );
  2490. assert(isset($type2cast[$type]));
  2491. $cast = $type2cast[$type];
  2492. $resvar = new Decompiler_Code($this, array($cast, ' ', $this->getOpVal($op1)));
  2493. break;
  2494. // }}}
  2495. case XC_EXT_STMT:
  2496. case XC_EXT_FCALL_BEGIN:
  2497. case XC_EXT_FCALL_END:
  2498. case XC_EXT_NOP:
  2499. case XC_INIT_CTOR_CALL:
  2500. break;
  2501. case XC_DECLARE_FUNCTION:
  2502. $key = $op1['constant'];
  2503. // possible missing tailing \0 (outside of the string)
  2504. $key = substr($key . ".", 0, strlen($key));
  2505. $oldEX = &$this->EX;
  2506. unset($this->EX);
  2507. $this->dfunction($this->dc['function_table'][$key]);
  2508. $this->EX = $oldEX;
  2509. unset($oldEX);
  2510. break;
  2511. case XC_DECLARE_LAMBDA_FUNCTION: // {{{
  2512. ob_start();
  2513. $key = $op1['constant'];
  2514. // possible missing tailing \0 (outside of the string)
  2515. $key = substr($key . ".", 0, strlen($key));
  2516. $oldEX = &$this->EX;
  2517. unset($this->EX);
  2518. $this->dfunction($this->dc['function_table'][$key]);
  2519. $this->EX = &$oldEX;
  2520. unset($oldEX);
  2521. $resvar = ob_get_clean();
  2522. $istmpres = true;
  2523. break;
  2524. // }}}
  2525. case XC_DECLARE_CONST:
  2526. $name = $this->stripNamespace(unquoteName($this->getOpVal($op1), $this->EX));
  2527. $value = $this->getOpVal($op2);
  2528. $resvar = new Decompiler_Code($this, array('const ', $name, ' = ', $value));
  2529. break;
  2530. case XC_DECLARE_FUNCTION_OR_CLASS:
  2531. /* always removed by compiler */
  2532. break;
  2533. case XC_TICKS:
  2534. $lastphpop['ticks'] = ZEND_ENGINE_2_4 ? $ext : ($op1 = $this->getOpVal($op1) ? $op1->value : 0);
  2535. // $this->EX['tickschanged'] = true;
  2536. break;
  2537. case XC_RAISE_ABSTRACT_ERROR:
  2538. // abstract function body is empty, don't need this code
  2539. break;
  2540. case XC_USER_OPCODE:
  2541. echo '// ZEND_USER_OPCODE, impossible to decompile';
  2542. break;
  2543. case XC_OP_DATA:
  2544. break;
  2545. default: // {{{
  2546. $call = array(&$this, $opname);
  2547. if (is_callable($call)) {
  2548. $this->usedOps[$opc] = true;
  2549. $this->{$opname}($op);
  2550. }
  2551. else if (isset($this->binaryOp[$opc])) { // {{{
  2552. $this->usedOps[$opc] = true;
  2553. $op1val = $this->getOpVal($op1);
  2554. $op2val = $this->getOpVal($op2);
  2555. $rvalue = new Decompiler_BinaryOp($this, $op1val, $opc, $op2val);
  2556. $resvar = $rvalue;
  2557. // }}}
  2558. }
  2559. else if (isset($this->unaryOp[$opc])) { // {{{
  2560. $this->usedOps[$opc] = true;
  2561. $op1val = $this->getOpVal($op1);
  2562. $resvar = new Decompiler_UnaryOp($this, $opc, $op1val);
  2563. // }}}
  2564. }
  2565. else {
  2566. $notHandled = true;
  2567. }
  2568. // }}}
  2569. }
  2570. if ($notHandled) {
  2571. fprintf(STDERR, "\x1B[31m%s\x1B[0m", " * TODO " . $opname . PHP_EOL);
  2572. echo $this->EX['indent'], "// TODO: ", $opname, PHP_EOL;
  2573. }
  2574. else {
  2575. $this->usedOps[$opc] = true;
  2576. }
  2577. if (isset($resvar)) {
  2578. if ($istmpres) {
  2579. $T[$res['var']] = $resvar;
  2580. $lastresvar = &$T[$res['var']];
  2581. }
  2582. else {
  2583. $op['php'] = $resvar;
  2584. $lastphpop = &$op;
  2585. $lastresvar = &$op['php'];
  2586. }
  2587. }
  2588. }
  2589. return $T;
  2590. }
  2591. // }}}
  2592. function unquote($str, $st, $ed) // {{{
  2593. {
  2594. $l1 = strlen($st);
  2595. $l2 = strlen($ed);
  2596. if (substr($str, 0, $l1) === $st && substr($str, -$l2) === $ed) {
  2597. $str = substr($str, $l1, -$l2);
  2598. }
  2599. return $str;
  2600. }
  2601. // }}}
  2602. function popargs($n) // {{{
  2603. {
  2604. $args = array();
  2605. for ($i = 0; $i < $n; $i++) {
  2606. $a = array_pop($this->EX['argstack']);
  2607. array_unshift($args, $a);
  2608. }
  2609. return new Decompiler_Statements($this, $args);
  2610. }
  2611. // }}}
  2612. function opValToString__($code) // {{{
  2613. {
  2614. while (is_object($code)) {
  2615. $code = $code->toCode('');
  2616. }
  2617. if (is_array($code)) {
  2618. foreach ($code as $c) {
  2619. $this->opValToString__($c);
  2620. }
  2621. }
  2622. else {
  2623. echo $code;
  2624. }
  2625. }
  2626. // }}}
  2627. function opValToString_($op) // {{{
  2628. {
  2629. ob_start();
  2630. $this->opValToString__($this->getOpVal($op, null));
  2631. return ob_get_clean();
  2632. }
  2633. // }}}
  2634. function opToString($op, $which) // {{{
  2635. {
  2636. switch ($op['op_type']) {
  2637. case XC_IS_UNUSED:
  2638. return '?' . $op['opline_num'];
  2639. case XC_IS_VAR:
  2640. $s = '$' . $op['var'];
  2641. if ($which != 'result' && isset($this->EX['Ts'])) {
  2642. $s .= ':' . $this->opValToString_($op);
  2643. }
  2644. return $s;
  2645. case XC_IS_TMP_VAR:
  2646. $s = '#' . $op['var'];
  2647. if ($which != 'result' && isset($this->EX['Ts'])) {
  2648. $s .= ':' . $this->opValToString_($op);
  2649. }
  2650. return $s;
  2651. case XC_IS_CONST:
  2652. return isset($this->EX['Ts']) ? $this->opValToString_($op) : $op['var'] . ':' . var_export($op['constant'], true);
  2653. default:
  2654. return isset($this->EX['Ts']) ? $this->opValToString_($op) : $op['op_type'] . '?' . $op['var'];
  2655. }
  2656. }
  2657. // }}}
  2658. function dumpOp($op, $padding = 4) // {{{
  2659. {
  2660. assert('isset($op)');
  2661. $this->output->write(str_pad($op['line'], $padding));
  2662. $this->output->write(str_pad($op['lineno'], $padding));
  2663. if (isset($op['oldopcode'])) {
  2664. $name = '//' . xcache_get_opcode($op['oldopcode']);
  2665. }
  2666. else {
  2667. $name = xcache_get_opcode($op['opcode']);
  2668. }
  2669. if (substr($name, 0, 5) == 'ZEND_') {
  2670. $name = substr($name, 5);
  2671. }
  2672. $this->output->write(' ', str_pad($name, 25));
  2673. $types = array('result' => 9, 'op1' => 20, 'op2' => 20);
  2674. $res = $op['result'];
  2675. $resUsed = ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) ? '' : '=';
  2676. foreach ($types as $which => $len) {
  2677. $this->output->write(' ', str_pad($this->opToString($op[$which], $which) . ($which == 'result' ? $resUsed : ''), $len));
  2678. }
  2679. $this->output->write("\t;", $op['extended_value']);
  2680. if (isset($op['isCatchBegin'])) {
  2681. $this->output->write(' CB');
  2682. }
  2683. if (!empty($op['jmptos'])) {
  2684. $this->output->write("\t>>", implode(',', $op['jmptos']));
  2685. }
  2686. if (!empty($op['jmpfroms'])) {
  2687. $this->output->write("\t<<", implode(',', $op['jmpfroms']));
  2688. }
  2689. $this->output->write(PHP_EOL);
  2690. }
  2691. // }}}
  2692. function dumpRange($range, $ts = true) // {{{
  2693. {
  2694. $this->output->beginComment();
  2695. if (!$ts) {
  2696. $Ts = $this->EX['Ts'];
  2697. $this->EX['Ts'] = null;
  2698. }
  2699. $padding = max(strlen($range[1]), strlen($this->EX['opcodes'][$range[1]]['lineno'])) + 1;
  2700. for ($i = $range[0]; $i <= $range[1]; ++$i) {
  2701. $this->output->write($this->output->indent);
  2702. $this->dumpOp($this->EX['opcodes'][$i], $padding);
  2703. }
  2704. if (!$ts) {
  2705. $this->EX['Ts'] = $Ts;
  2706. }
  2707. $this->output->endComment();
  2708. }
  2709. // }}}
  2710. function dargs() // {{{
  2711. {
  2712. $op_array = &$this->EX['op_array'];
  2713. if (isset($op_array['num_args'])) {
  2714. $c = $op_array['num_args'];
  2715. }
  2716. else if (!empty($op_array['arg_types'])) {
  2717. $c = count($op_array['arg_types']);
  2718. }
  2719. else {
  2720. // php4
  2721. $c = count($this->EX['recvs']);
  2722. }
  2723. $refrest = false;
  2724. $args = array();
  2725. for ($i = 0; $i < $c; $i++) {
  2726. $arg = array();
  2727. $recv = isset($this->EX['recvs'][$i + 1]) ? $this->EX['recvs'][$i + 1] : null;
  2728. if (isset($op_array['arg_info'])) {
  2729. $ai = $op_array['arg_info'][$i];
  2730. if (isset($ai['type_hint']) ? ($ai['type_hint'] == IS_CALLABLE || $ai['type_hint'] == IS_OBJECT) : !empty($ai['class_name'])) {
  2731. $arg[] = $this->stripNamespace($ai['class_name']);
  2732. $arg[] = ' ';
  2733. if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
  2734. $arg[] = 'or NULL ';
  2735. }
  2736. }
  2737. else if (isset($ai['type_hint']) ? $ai['type_hint'] == IS_ARRAY : !empty($ai['array_type_hint'])) {
  2738. $arg[] = 'array ';
  2739. if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
  2740. $arg[] = 'or NULL ';
  2741. }
  2742. }
  2743. if ($ai['pass_by_reference']) {
  2744. $arg[] = '&';
  2745. }
  2746. $arg[] = '$';
  2747. $arg[] = $ai['name'];
  2748. }
  2749. else {
  2750. if ($refrest) {
  2751. $arg[] = '&';
  2752. }
  2753. else if (!empty($op_array['arg_types']) && isset($op_array['arg_types'][$i])) {
  2754. switch ($op_array['arg_types'][$i]) {
  2755. case BYREF_FORCE_REST:
  2756. $refrest = true;
  2757. /* fall */
  2758. case BYREF_FORCE:
  2759. $arg[] = '&';
  2760. break;
  2761. case BYREF_NONE:
  2762. case BYREF_ALLOW:
  2763. break;
  2764. default:
  2765. assert(0);
  2766. }
  2767. }
  2768. $arg[] = $recv[0];
  2769. }
  2770. if (isset($recv) && isset($recv[1])) {
  2771. $arg[] = ' = ';
  2772. $arg[] = $recv[1];
  2773. }
  2774. $args[] = $arg;
  2775. }
  2776. return new Decompiler_Statements($this, $args);
  2777. }
  2778. // }}}
  2779. function duses() // {{{
  2780. {
  2781. $code = array();
  2782. if ($this->EX['uses']) {
  2783. $code[] = " use(";
  2784. $code[] = new Decompiler_Statements($this, $this->EX['uses']);
  2785. $code[] = ')';
  2786. }
  2787. return $code;
  2788. }
  2789. // }}}
  2790. function dfunction($func, $decorations = array(), $nobody = false) // {{{
  2791. {
  2792. static $opcode_count = 0;
  2793. $opcode_count += count($func['op_array']['opcodes']);
  2794. $functionName = $this->stripNamespace($func['op_array']['function_name']);
  2795. $this->detectNamespace($functionName);
  2796. $isExpression = false;
  2797. if ($functionName == '{closure}') {
  2798. $functionName = '';
  2799. $isExpression = true;
  2800. }
  2801. if (!$nobody && !$isExpression) {
  2802. $this->output->beginComplexBlock();
  2803. }
  2804. $returnByRef = '';
  2805. if ($nobody) {
  2806. $EX = array();
  2807. $EX['op_array'] = &$func['op_array'];
  2808. $EX['recvs'] = array();
  2809. $EX['uses'] = array();
  2810. }
  2811. else {
  2812. ob_start();
  2813. $this->output->beginScope();
  2814. $EX = &$this->dop_array($func['op_array'], true);
  2815. $this->output->endScope();
  2816. $body = ob_get_clean();
  2817. $hasReturn = false;
  2818. $hasReturnByRef = false;
  2819. foreach ($func['op_array']['opcodes'] as $op) {
  2820. switch ($op['opcode']) {
  2821. case XC_RETURN:
  2822. $hasReturn = true;
  2823. break;
  2824. case XC_RETURN_BY_REF:
  2825. $hasReturnByRef = true;
  2826. break;
  2827. }
  2828. }
  2829. if ($hasReturn && $hasReturnByRef) {
  2830. $this->output->printfError("WARN: both return and return-by-ref present" . PHP_EOL);
  2831. }
  2832. if ($hasReturnByRef) {
  2833. $returnByRef = '&';
  2834. }
  2835. }
  2836. if (!empty($func['op_array']['doc_comment'])) {
  2837. $this->output->writeln($func['op_array']['doc_comment']);
  2838. }
  2839. $functionDeclare = array();
  2840. if ($decorations) {
  2841. $functionDeclare[] = implode(' ', $decorations);
  2842. $functionDeclare[] = ' ';
  2843. }
  2844. $this->EX = &$EX;
  2845. unset($EX);
  2846. $functionDeclare[] = 'function';
  2847. $functionDeclare[] = $functionName ? ' ' . $returnByRef . $functionName : '';
  2848. $functionDeclare[] = '(';
  2849. $functionDeclare[] = $this->dargs();
  2850. $functionDeclare[] = ")";
  2851. $functionDeclare[] = $this->duses();
  2852. unset($this->EX);
  2853. if ($nobody) {
  2854. $functionDeclare[] = ";";
  2855. $this->output->writeln($functionDeclare);
  2856. }
  2857. else {
  2858. if (!$isExpression) {
  2859. $this->output->writeln($functionDeclare);
  2860. $this->output->writeln("{");
  2861. }
  2862. else {
  2863. $this->output->write($functionDeclare);
  2864. $this->output->write(" {");
  2865. $this->output->write(PHP_EOL);
  2866. }
  2867. echo $body;
  2868. if (!$isExpression) {
  2869. $this->output->writeln("}");
  2870. }
  2871. else {
  2872. $this->output->write($this->output->indent);
  2873. $this->output->write("}");
  2874. }
  2875. if (!$isExpression) {
  2876. $this->output->endComplexBlock();
  2877. }
  2878. }
  2879. if ($opcode_count > 10000) {
  2880. $opcode_count = 0;
  2881. if (function_exists("gc_collect_cycles")) {
  2882. gc_collect_cycles();
  2883. }
  2884. }
  2885. }
  2886. // }}}
  2887. function dclass($class) // {{{
  2888. {
  2889. $this->value2constant[$this->activeClass] = '__CLASS__';
  2890. $this->detectNamespace($class['name']);
  2891. // {{{ class decl
  2892. $isInterface = false;
  2893. $decorations = array();
  2894. if (!empty($class['ce_flags'])) {
  2895. if ($class['ce_flags'] & ZEND_ACC_INTERFACE) {
  2896. $isInterface = true;
  2897. }
  2898. else {
  2899. if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
  2900. $decorations[] = "abstract";
  2901. }
  2902. if ($class['ce_flags'] & ZEND_ACC_FINAL_CLASS) {
  2903. $decorations[] = "final";
  2904. }
  2905. }
  2906. }
  2907. $this->output->beginComplexBlock();
  2908. if (!empty($class['doc_comment'])) {
  2909. $this->output->writeln($class['doc_comment']);
  2910. }
  2911. $classDeclare = array();
  2912. if ($decorations) {
  2913. $classDeclare[] = implode(' ', $decorations) . ' ';
  2914. }
  2915. $classDeclare[] = $isInterface ? 'interface ' : 'class ';
  2916. $classDeclare[] = $this->stripNamespace($class['name']);
  2917. if ($class['parent']) {
  2918. $classDeclare[] = ' extends ';
  2919. $classDeclare[] = $this->stripNamespace($class['parent']);
  2920. }
  2921. /* TODO */
  2922. if (!empty($class['interfaces'])) {
  2923. $classDeclare[] = ' implements ';
  2924. $classDeclare[] = implode(', ', $class['interfaces']);
  2925. }
  2926. $this->output->writeln($classDeclare);
  2927. $this->output->writeln("{");
  2928. $this->output->beginScope();
  2929. // }}}
  2930. // {{{ const
  2931. if (!empty($class['constants_table'])) {
  2932. $this->output->beginComplexBlock();
  2933. foreach ($class['constants_table'] as $name => $v) {
  2934. $this->output->writeln('const ', $name, ' = ', value($v), ";");
  2935. }
  2936. $this->output->endComplexBlock();
  2937. }
  2938. // }}}
  2939. // {{{ properties
  2940. if (ZEND_ENGINE_2 && !ZEND_ENGINE_2_4) {
  2941. $default_static_members = $class[ZEND_ENGINE_2_1 ? 'default_static_members' : 'static_members'];
  2942. }
  2943. $member_variables = $class[ZEND_ENGINE_2 ? 'properties_info' : 'default_properties'];
  2944. if ($member_variables) {
  2945. $this->output->beginComplexBlock();
  2946. foreach ($member_variables as $name => $dummy) {
  2947. $info = isset($class['properties_info']) ? $class['properties_info'][$name] : null;
  2948. if (isset($info) && !empty($info['doc_comment'])) {
  2949. $this->output->writeln($info['doc_comment']);
  2950. }
  2951. $variableDeclare = array();
  2952. if (ZEND_ENGINE_2) {
  2953. $static = ($info['flags'] & ZEND_ACC_STATIC);
  2954. if ($static) {
  2955. $variableDeclare[] = "static ";
  2956. }
  2957. }
  2958. $mangleSuffix = '';
  2959. if (!ZEND_ENGINE_2) {
  2960. $variableDeclare[] = 'var ';
  2961. }
  2962. else if (!isset($info)) {
  2963. $variableDeclare[] = 'public ';
  2964. }
  2965. else {
  2966. if ($info['flags'] & ZEND_ACC_SHADOW) {
  2967. continue;
  2968. }
  2969. switch ($info['flags'] & ZEND_ACC_PPP_MASK) {
  2970. case ZEND_ACC_PUBLIC:
  2971. $variableDeclare[] = "public ";
  2972. break;
  2973. case ZEND_ACC_PRIVATE:
  2974. $variableDeclare[] = "private ";
  2975. $mangleSuffix = "\000";
  2976. break;
  2977. case ZEND_ACC_PROTECTED:
  2978. $variableDeclare[] = "protected ";
  2979. $mangleSuffix = "\000";
  2980. break;
  2981. }
  2982. }
  2983. $variableDeclare[] = '$';
  2984. $variableDeclare[] = $name;
  2985. if (ZEND_ENGINE_2_4) {
  2986. $value = $class[$static ? 'default_static_members_table' : 'default_properties_table'][$info['offset']];
  2987. }
  2988. else if (!ZEND_ENGINE_2) {
  2989. $value = $class['default_properties'][$name];
  2990. }
  2991. else {
  2992. $key = $info['name'] . $mangleSuffix;
  2993. if ($static) {
  2994. $value = $default_static_members[$key];
  2995. }
  2996. else {
  2997. $value = $class['default_properties'][$key];
  2998. }
  2999. }
  3000. $value = value($value);
  3001. if (is_a($value, 'Decompiler_Value') && !isset($value->value)) {
  3002. // skip value;
  3003. }
  3004. else {
  3005. $variableDeclare[] = ' = ';
  3006. $variableDeclare[] = $value;
  3007. }
  3008. $variableDeclare[] = ";";
  3009. $this->output->writeln($variableDeclare);
  3010. }
  3011. $this->output->endComplexBlock();
  3012. }
  3013. // }}}
  3014. // {{{ function_table
  3015. if (isset($class['function_table'])) {
  3016. foreach ($class['function_table'] as $func) {
  3017. if (!isset($func['scope']) || $func['scope'] == $class['name']) {
  3018. // TODO: skip shadow here
  3019. $opa = &$func['op_array'];
  3020. $isAbstractMethod = false;
  3021. $decorations = array();
  3022. if (isset($opa['fn_flags'])) {
  3023. if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isInterface) {
  3024. $decorations[] = "abstract";
  3025. $isAbstractMethod = true;
  3026. }
  3027. if ($opa['fn_flags'] & ZEND_ACC_FINAL) {
  3028. $decorations[] = "final";
  3029. }
  3030. if ($opa['fn_flags'] & ZEND_ACC_STATIC) {
  3031. $decorations[] = "static";
  3032. }
  3033. switch ($opa['fn_flags'] & ZEND_ACC_PPP_MASK) {
  3034. case ZEND_ACC_PUBLIC:
  3035. $decorations[] = "public";
  3036. break;
  3037. case ZEND_ACC_PRIVATE:
  3038. $decorations[] = "private";
  3039. break;
  3040. case ZEND_ACC_PROTECTED:
  3041. $decorations[] = "protected";
  3042. break;
  3043. default:
  3044. $decorations[] = "<visibility error>";
  3045. break;
  3046. }
  3047. }
  3048. $this->activeMethod = $this->activeClass . '::' . $opa['function_name'];
  3049. $this->activeFunction = $opa['function_name'];
  3050. $this->dfunction($func, $decorations, $isInterface || $isAbstractMethod);
  3051. $this->activeFunction = null;
  3052. $this->activeMethod = null;
  3053. if ($opa['function_name'] == 'Decompiler') {
  3054. //exit;
  3055. }
  3056. }
  3057. }
  3058. }
  3059. // }}}
  3060. $this->output->endScope();
  3061. $this->output->writeln("}");
  3062. $this->output->endComplexBlock();
  3063. unset($this->value2constant[$this->activeClass]);
  3064. }
  3065. // }}}
  3066. function decompileString($string) // {{{
  3067. {
  3068. $this->dc = xcache_dasm_string($string);
  3069. if ($this->dc === false) {
  3070. echo "error compling string", PHP_EOL;
  3071. return false;
  3072. }
  3073. $this->activeFile = null;
  3074. $this->activeDir = null;
  3075. return true;
  3076. }
  3077. // }}}
  3078. function decompileFile($file) // {{{
  3079. {
  3080. $this->dc = xcache_dasm_file($file);
  3081. if ($this->dc === false) {
  3082. echo "error compling $file", PHP_EOL;
  3083. return false;
  3084. }
  3085. $this->activeFile = realpath($file);
  3086. if (ZEND_ENGINE_2_3) {
  3087. $this->activeDir = dirname($this->activeFile);
  3088. }
  3089. $this->value2constant[$this->activeFile] = '__FILE__';
  3090. $this->value2constant[$this->activeDir] = '__DIR__';
  3091. return true;
  3092. }
  3093. // }}}
  3094. function decompileDasm($content) // {{{
  3095. {
  3096. $this->dc = $content;
  3097. $this->activeFile = null;
  3098. $this->activeDir = null;
  3099. return true;
  3100. }
  3101. // }}}
  3102. function output() // {{{
  3103. {
  3104. $this->output->beginComplexBlock();
  3105. $this->output->writeln("<" . "?php");
  3106. $this->output->endComplexBlock();
  3107. foreach ($this->dc['class_table'] as $key => $class) {
  3108. if ($key{0} != "\0") {
  3109. $this->activeClass = $class['name'];
  3110. $this->dclass($class);
  3111. $this->activeClass = null;
  3112. }
  3113. }
  3114. foreach ($this->dc['function_table'] as $key => $func) {
  3115. if ($key{0} != "\0") {
  3116. $this->activeFunction = $key;
  3117. $this->dfunction($func);
  3118. $this->activeFunction = null;
  3119. }
  3120. }
  3121. $this->dop_array($this->dc['op_array']);
  3122. $this->output->beginComplexBlock();
  3123. $this->output->writeln("?" . ">");
  3124. $this->output->endComplexBlock();
  3125. if (!empty($this->test)) {
  3126. $this->outputUnusedOp();
  3127. }
  3128. return true;
  3129. }
  3130. // }}}
  3131. function outputUnusedOp() // {{{
  3132. {
  3133. for ($i = 0; $opname = xcache_get_opcode($i); $i++) {
  3134. if ($opname == 'UNDEF') {
  3135. continue;
  3136. }
  3137. if (!isset($this->usedOps[$i])) {
  3138. echo "not covered opcode ", $opname, PHP_EOL;
  3139. }
  3140. }
  3141. }
  3142. // }}}
  3143. }
  3144. // {{{ defines
  3145. define('ZEND_ENGINE_2_6', PHP_VERSION >= "5.6");
  3146. define('ZEND_ENGINE_2_5', ZEND_ENGINE_2_6 || PHP_VERSION >= "5.5.");
  3147. define('ZEND_ENGINE_2_4', ZEND_ENGINE_2_5 || PHP_VERSION >= "5.4.");
  3148. define('ZEND_ENGINE_2_3', ZEND_ENGINE_2_4 || PHP_VERSION >= "5.3.");
  3149. define('ZEND_ENGINE_2_2', ZEND_ENGINE_2_3 || PHP_VERSION >= "5.2.");
  3150. define('ZEND_ENGINE_2_1', ZEND_ENGINE_2_2 || PHP_VERSION >= "5.1.");
  3151. define('ZEND_ENGINE_2', ZEND_ENGINE_2_1 || PHP_VERSION >= "5.0.");
  3152. define('ZEND_ACC_STATIC', 0x01);
  3153. define('ZEND_ACC_ABSTRACT', 0x02);
  3154. define('ZEND_ACC_FINAL', 0x04);
  3155. define('ZEND_ACC_IMPLEMENTED_ABSTRACT', 0x08);
  3156. define('ZEND_ACC_IMPLICIT_ABSTRACT_CLASS', 0x10);
  3157. define('ZEND_ACC_EXPLICIT_ABSTRACT_CLASS', 0x20);
  3158. define('ZEND_ACC_FINAL_CLASS', 0x40);
  3159. define('ZEND_ACC_INTERFACE', 0x80);
  3160. if (ZEND_ENGINE_2_4) {
  3161. define('ZEND_ACC_TRAIT', 0x120);
  3162. }
  3163. define('ZEND_ACC_PUBLIC', 0x100);
  3164. define('ZEND_ACC_PROTECTED', 0x200);
  3165. define('ZEND_ACC_PRIVATE', 0x400);
  3166. define('ZEND_ACC_PPP_MASK', (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE));
  3167. define('ZEND_ACC_CHANGED', 0x800);
  3168. define('ZEND_ACC_IMPLICIT_PUBLIC', 0x1000);
  3169. define('ZEND_ACC_CTOR', 0x2000);
  3170. define('ZEND_ACC_DTOR', 0x4000);
  3171. define('ZEND_ACC_CLONE', 0x8000);
  3172. define('ZEND_ACC_ALLOW_STATIC', 0x10000);
  3173. define('ZEND_ACC_SHADOW', 0x2000);
  3174. if (ZEND_ENGINE_2_4) {
  3175. define('ZEND_FETCH_GLOBAL', 0x00000000);
  3176. define('ZEND_FETCH_LOCAL', 0x10000000);
  3177. define('ZEND_FETCH_STATIC', 0x20000000);
  3178. define('ZEND_FETCH_STATIC_MEMBER', 0x30000000);
  3179. define('ZEND_FETCH_GLOBAL_LOCK', 0x40000000);
  3180. define('ZEND_FETCH_LEXICAL', 0x50000000);
  3181. define('ZEND_FETCH_TYPE_MASK', 0x70000000);
  3182. define('ZEND_FETCH_STANDARD', 0x00000000);
  3183. define('ZEND_FETCH_ADD_LOCK', 0x08000000);
  3184. define('ZEND_FETCH_MAKE_REF', 0x04000000);
  3185. }
  3186. else {
  3187. define('ZEND_FETCH_GLOBAL', 0);
  3188. define('ZEND_FETCH_LOCAL', 1);
  3189. define('ZEND_FETCH_STATIC', 2);
  3190. define('ZEND_FETCH_STATIC_MEMBER', 3);
  3191. define('ZEND_FETCH_GLOBAL_LOCK', 4);
  3192. define('ZEND_FETCH_STANDARD', 0);
  3193. define('ZEND_FETCH_ADD_LOCK', 1);
  3194. }
  3195. define('XC_FETCH_PROPERTY', 10);
  3196. if (ZEND_ENGINE_2_4) {
  3197. define('ZEND_ISSET', 0x02000000);
  3198. define('ZEND_ISEMPTY', 0x01000000);
  3199. define('ZEND_ISSET_ISEMPTY_MASK', (ZEND_ISSET | ZEND_ISEMPTY));
  3200. define('ZEND_QUICK_SET', 0x00800000);
  3201. }
  3202. else {
  3203. define('ZEND_ISSET', (1<<0));
  3204. define('ZEND_ISEMPTY', (1<<1));
  3205. define('ZEND_ISSET_ISEMPTY_MASK', (ZEND_ISSET | ZEND_ISEMPTY));
  3206. }
  3207. define('ZEND_FETCH_CLASS_DEFAULT', 0);
  3208. define('ZEND_FETCH_CLASS_SELF', 1);
  3209. define('ZEND_FETCH_CLASS_PARENT', 2);
  3210. define('ZEND_FETCH_CLASS_MAIN', 3);
  3211. define('ZEND_FETCH_CLASS_GLOBAL', 4);
  3212. define('ZEND_FETCH_CLASS_AUTO', 5);
  3213. define('ZEND_FETCH_CLASS_INTERFACE', 6);
  3214. define('ZEND_FETCH_CLASS_STATIC', 7);
  3215. if (ZEND_ENGINE_2_4) {
  3216. define('ZEND_FETCH_CLASS_TRAIT', 14);
  3217. }
  3218. if (ZEND_ENGINE_2_3) {
  3219. define('ZEND_FETCH_CLASS_MASK', 0xF);
  3220. }
  3221. define('ZEND_EVAL', (1<<0));
  3222. define('ZEND_INCLUDE', (1<<1));
  3223. define('ZEND_INCLUDE_ONCE', (1<<2));
  3224. define('ZEND_REQUIRE', (1<<3));
  3225. define('ZEND_REQUIRE_ONCE', (1<<4));
  3226. if (ZEND_ENGINE_2_4) {
  3227. define('EXT_TYPE_UNUSED', (1<<5));
  3228. }
  3229. else {
  3230. define('EXT_TYPE_UNUSED', (1<<0));
  3231. }
  3232. if (ZEND_ENGINE_2_1) {
  3233. define('ZEND_FE_FETCH_BYREF', 1);
  3234. define('ZEND_FE_FETCH_WITH_KEY', 2);
  3235. }
  3236. else {
  3237. define('ZEND_UNSET_DIM', 1);
  3238. define('ZEND_UNSET_OBJ', 2);
  3239. }
  3240. define('ZEND_MEMBER_FUNC_CALL', 1<<0);
  3241. define('ZEND_CTOR_CALL', 1<<1);
  3242. define('ZEND_ARG_SEND_BY_REF', (1<<0));
  3243. define('ZEND_ARG_COMPILE_TIME_BOUND', (1<<1));
  3244. define('ZEND_ARG_SEND_FUNCTION', (1<<2));
  3245. define('BYREF_NONE', 0);
  3246. define('BYREF_FORCE', 1);
  3247. define('BYREF_ALLOW', 2);
  3248. define('BYREF_FORCE_REST', 3);
  3249. define('IS_NULL', 0);
  3250. define('IS_LONG', 1);
  3251. define('IS_DOUBLE', 2);
  3252. define('IS_BOOL', ZEND_ENGINE_2_1 ? 3 : 6);
  3253. define('IS_ARRAY', 4);
  3254. define('IS_OBJECT', 5);
  3255. define('IS_STRING', ZEND_ENGINE_2_1 ? 6 : 3);
  3256. define('IS_RESOURCE', 7);
  3257. define('IS_CONSTANT', 8);
  3258. if (ZEND_ENGINE_2_6) {
  3259. define('IS_CONSTANT_ARRAY', -1);
  3260. define('IS_CONSTANT_AST', 9);
  3261. }
  3262. else {
  3263. define('IS_CONSTANT_ARRAY', 9);
  3264. }
  3265. if (ZEND_ENGINE_2_4) {
  3266. define('IS_CALLABLE', 10);
  3267. }
  3268. /* Ugly hack to support constants as static array indices */
  3269. define('IS_CONSTANT_TYPE_MASK', 0x0f);
  3270. define('IS_CONSTANT_UNQUALIFIED', 0x10);
  3271. define('IS_CONSTANT_INDEX', 0x80);
  3272. define('IS_LEXICAL_VAR', 0x20);
  3273. define('IS_LEXICAL_REF', 0x40);
  3274. if (ZEND_ENGINE_2_6) {
  3275. define('ZEND_CONST', 256);
  3276. define('ZEND_BOOL_AND', 256 + 1);
  3277. define('ZEND_BOOL_OR', 256 + 2);
  3278. define('ZEND_SELECT', 256 + 3);
  3279. define('ZEND_UNARY_PLUS', 256 + 4);
  3280. define('ZEND_UNARY_MINUS', 256 + 5);
  3281. }
  3282. @define('XC_IS_CV', 16);
  3283. if (!defined("PHP_EOL")) {
  3284. define("PHP_EOL", "\n");
  3285. }
  3286. /*
  3287. if (preg_match_all('!XC_[A-Z_]+!', file_get_contents(__FILE__), $ms)) {
  3288. $verdiff = array();
  3289. foreach ($ms[0] as $k) {
  3290. if (!defined($k)) {
  3291. $verdiff[$k] = -1;
  3292. define($k, -1);
  3293. }
  3294. }
  3295. var_export($verdiff);
  3296. exit;
  3297. }
  3298. //*/
  3299. foreach (array(
  3300. 'XC_ADD_INTERFACE' => -1,
  3301. 'XC_ASSIGN_DIM' => -1,
  3302. 'XC_ASSIGN_OBJ' => -1,
  3303. 'XC_ASSIGN_POW' => -1,
  3304. 'XC_CATCH' => -1,
  3305. 'XC_CLONE' => -1,
  3306. 'XC_DECLARE_CLASS' => -1,
  3307. 'XC_DECLARE_CONST' => -1,
  3308. 'XC_DECLARE_FUNCTION' => -1,
  3309. 'XC_DECLARE_FUNCTION_OR_CLASS' => -1,
  3310. 'XC_DECLARE_INHERITED_CLASS' => -1,
  3311. 'XC_DECLARE_INHERITED_CLASS_DELAYED' => -1,
  3312. 'XC_DECLARE_LAMBDA_FUNCTION' => -1,
  3313. 'XC_DO_FCALL_BY_FUNC' => -1,
  3314. 'XC_FETCH_CLASS' => -1,
  3315. 'XC_GENERATOR_RETURN' => -1,
  3316. 'XC_GOTO' => -1,
  3317. 'XC_HANDLE_EXCEPTION' => -1,
  3318. 'XC_INIT_CTOR_CALL' => -1,
  3319. 'XC_INIT_FCALL_BY_FUNC' => -1,
  3320. 'XC_INIT_METHOD_CALL' => -1,
  3321. 'XC_INIT_NS_FCALL_BY_NAME' => -1,
  3322. 'XC_INIT_STATIC_METHOD_CALL' => -1,
  3323. 'XC_INSTANCEOF' => -1,
  3324. 'XC_ISSET_ISEMPTY' => -1,
  3325. 'XC_ISSET_ISEMPTY_DIM_OBJ' => -1,
  3326. 'XC_ISSET_ISEMPTY_PROP_OBJ' => -1,
  3327. 'XC_ISSET_ISEMPTY_VAR' => -1,
  3328. 'XC_JMP_NO_CTOR' => -1,
  3329. 'XC_JMP_SET' => -1,
  3330. 'XC_JMP_SET_VAR' => -1,
  3331. 'XC_OP_DATA' => -1,
  3332. 'XC_POST_DEC_OBJ' => -1,
  3333. 'XC_POST_INC_OBJ' => -1,
  3334. 'XC_POW' => -1,
  3335. 'XC_PRE_DEC_OBJ' => -1,
  3336. 'XC_PRE_INC_OBJ' => -1,
  3337. 'XC_QM_ASSIGN_VAR' => -1,
  3338. 'XC_RAISE_ABSTRACT_ERROR' => -1,
  3339. 'XC_RETURN_BY_REF' => -1,
  3340. 'XC_THROW' => -1,
  3341. 'XC_UNSET_DIM' => -1,
  3342. 'XC_UNSET_DIM_OBJ' => -1,
  3343. 'XC_UNSET_OBJ' => -1,
  3344. 'XC_USER_OPCODE' => -1,
  3345. 'XC_VERIFY_ABSTRACT_CLASS' => -1,
  3346. 'XC_YIELD' => -1,
  3347. ) as $k => $v) {
  3348. if (!defined($k)) {
  3349. define($k, $v);
  3350. }
  3351. }
  3352. // }}}