PageRenderTime 66ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Decompiler.class.php

https://github.com/alepharchives/xcache
PHP | 2817 lines | 2438 code | 174 blank | 205 comment | 561 complexity | 0baf919df9a9d92e2a64709253ecf66c MD5 | raw file
Possible License(s): BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file