PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/cps.php

https://github.com/lucciano/eventerator
PHP | 2013 lines | 1828 code | 138 blank | 47 comment | 140 complexity | 846830efa71fab3193a9f7aa3c2aca99 MD5 | raw file

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

  1. <?php
  2. // TODO
  3. // static variables
  4. // line number mappings in generated code (comments)
  5. // traits
  6. // namespaces
  7. // break and continue by non-const number (include array of basic block IDs in compiled code)
  8. // pass down break and continue stack when including to permit break in top level of an included file
  9. // Continuation passing style transformation for PHP
  10. require_once 'PHP-Parser/lib/bootstrap.php';
  11. require_once 'print.php';
  12. require_once 'runtime.php';
  13. require_once 'compiler_plugins.php';
  14. const TEMP_NAME = 't';
  15. const GLOBALS_TEMP_NAME = 'G';
  16. const ARGS_TEMP_NAME = 'A';
  17. const CONT_NAME = 'c';
  18. const LOCALS_NAME = 'l';
  19. const LABELS_NAME = 'g';
  20. const VALUE_NAME = 'v';
  21. const VALUE_REF_NAME = 'r';
  22. const VALUE_IS_REF_NAME = 'q';
  23. const JUNK_NAME = 'j';
  24. const PARAM_NAME = 'p';
  25. const USES_NAME = 'u';
  26. const EXCEPT_NAME = 'x';
  27. const STATICS_NAME = 's';
  28. $SUPERGLOBALS = ['GLOBALS', '_SERVER', '_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_REQUEST', '_ENV'];
  29. $MAGIC_METHODS = ['__construct', '__set', '__get', '__destruct', '__sleep', '__wakeup', '__toString', '__isset', '__unset', '__call', '__callStatic', '__set_state', '__clone'];
  30. function config($name, $value = null) {
  31. static $configs = [];
  32. if (isset($value)) {
  33. $configs[$name] = $value;
  34. }
  35. else {
  36. if (array_key_exists($name, $configs)) {
  37. return $configs[$name];
  38. }
  39. }
  40. }
  41. config('disable_strict_logical_operator_type', false);
  42. config('disable_func_get_args', false);
  43. config('trampoline_max_stack', 40); // optimum seems to be around 40 or 50
  44. $interpreter = $argv[1];
  45. $compiler = $interpreter . ' ' . __FILE__ . ' "' . $interpreter . '"';
  46. $file = $argv[3];
  47. $source = file_get_contents('php://stdin');
  48. $compiled = compile($source);
  49. switch ($mode = $argv[2]) {
  50. case 'main':
  51. echo "<?php\nrequire_once('" . dirname(__FILE__) . "/runtime.php');\n\$l=&\$GLOBALS;\n\$t=['" . GLOBALS_TEMP_NAME . "'=>&\$GLOBALS];\n\$g=[];\n";
  52. printStatements(generateTrampoline($compiled));
  53. echo "\n";
  54. break;
  55. case 'include':
  56. echo "<?php\n";
  57. printStatements($compiled);
  58. break;
  59. default:
  60. throw new Exception('unknown mode ' . $mode);
  61. break;
  62. }
  63. // A container for a single parser node reference
  64. class NodeReference extends PHPParser_Node_Expr {
  65. function __construct(PHPParser_Node_Expr $node) {
  66. $this->node = $node;
  67. }
  68. function getNode() {
  69. return $this->node;
  70. }
  71. function setNode(PHPParser_Node_Expr $node) {
  72. $this->node = $node;
  73. }
  74. }
  75. // Wrapper for a closure if it represents a return from a function.
  76. // This is used to determine whether a given continuation represents
  77. // a tail call that can be optimized away.
  78. class ReturnClosure {
  79. private $closure;
  80. function __construct($closure) {
  81. $this->closure = $closure;
  82. }
  83. function __invoke() {
  84. return call_user_func_array($this->closure, func_get_args());
  85. }
  86. }
  87. function generateDefaultTemps() {
  88. $temps = [];
  89. $temps[] = new PHPParser_Node_Expr_ArrayItem(
  90. new PHPParser_Node_Expr_Variable('GLOBALS'),
  91. new PHPParser_Node_Scalar_String(GLOBALS_TEMP_NAME),
  92. true
  93. );
  94. if (!config('disable_func_get_args')) {
  95. $temps[] = new PHPParser_Node_Expr_ArrayItem(
  96. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('array_slice'), [
  97. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('func_get_args'), []),
  98. new PHPParser_Node_Scalar_LNumber(2)
  99. ]),
  100. new PHPParser_Node_Scalar_String(ARGS_TEMP_NAME)
  101. );
  102. }
  103. return new PHPParser_Node_Expr_Array($temps);
  104. }
  105. function generateTemp() {
  106. static $temp_counter = 0;
  107. return new PHPParser_Node_Expr_ArrayDimFetch(
  108. new PHPParser_Node_Expr_Variable(TEMP_NAME),
  109. new PHPParser_Node_Scalar_LNumber($temp_counter++)
  110. );
  111. }
  112. // Assign an expression to a temp or inline simple variable names.
  113. // Appends an assignment statement to $stmts if necessary.
  114. // Returns the name by which the expression will be known.
  115. function assignToTemp($expr, &$stmts, $byRef = false) {
  116. $temp = generateTemp();
  117. $assign = $byRef ? 'PHPParser_Node_Expr_AssignRef' : 'PHPParser_Node_Expr_Assign';
  118. $stmts[] = new $assign($temp, $expr);
  119. return $temp;
  120. }
  121. // Generate the expression to convert an expression into && compatible values (true, false, 1, 0, depending on value).
  122. function boolifyLogicalOperator($expr) {
  123. // If one does not depend in a === sense on the result of &&, and, ||, or being true vs. 1 vs. truthy value, then
  124. // we cut down a bit of bloat by configurably turning this into the identity function.
  125. if (config('disable_strict_logical_operator_type')) return $expr;
  126. $temp = generateTemp();
  127. return new PHPParser_Node_Expr_Ternary(
  128. new PHPParser_Node_Expr_FuncCall(
  129. new PHPParser_Node_Name('is_bool'),
  130. [new PHPParser_Node_Expr_Assign($temp, $expr)]
  131. ),
  132. $temp,
  133. new PHPParser_Node_Expr_Ternary(
  134. $temp,
  135. new PHPParser_Node_Scalar_LNumber(1),
  136. new PHPParser_Node_Scalar_LNumber(0)
  137. )
  138. );
  139. }
  140. function generateContinuation($next, $cont, $state) {
  141. if ($next instanceof ReturnClosure) {
  142. return $cont(new PHPParser_Node_Expr_Variable(CONT_NAME), $state);
  143. }
  144. $temp = generateTemp();
  145. return $next($temp, function ($result, $state) use ($cont, $temp) {
  146. $result = array_merge([
  147. new PHPParser_Node_Expr_Ternary(
  148. new PHPParser_Node_Expr_Variable(VALUE_IS_REF_NAME),
  149. new PHPParser_Node_Expr_AssignRef($temp, new PHPParser_Node_Expr_Variable(VALUE_REF_NAME)),
  150. new PHPParser_Node_Expr_Assign($temp, new PHPParser_Node_Expr_Variable(VALUE_NAME))
  151. ),
  152. new PHPParser_Node_Expr_Assign(
  153. new PHPParser_Node_Expr_Variable(LOCALS_NAME),
  154. new PHPParser_Node_Expr_Variable(LOCALS_NAME)
  155. )
  156. ], $result);
  157. return $cont(new PHPParser_Node_Expr_Closure([
  158. 'params' => [
  159. new PHPParser_Node_Param(VALUE_NAME),
  160. new PHPParser_Node_Param(VALUE_REF_NAME, new PHPParser_Node_Expr_ConstFetch(new PHPParser_Node_Name('null')), null, true),
  161. new PHPParser_Node_Param(VALUE_IS_REF_NAME, new PHPParser_Node_Expr_ConstFetch(new PHPParser_Node_Name('false')))
  162. ],
  163. 'uses' => [
  164. new PHPParser_Node_Expr_ClosureUse(CONT_NAME),
  165. new PHPParser_Node_Expr_ClosureUse(EXCEPT_NAME),
  166. new PHPParser_Node_Expr_ClosureUse(LOCALS_NAME, true),
  167. new PHPParser_Node_Expr_ClosureUse(TEMP_NAME, true),
  168. new PHPParser_Node_Expr_ClosureUse(LABELS_NAME, true)
  169. ],
  170. 'stmts' => $result
  171. ]), $state);
  172. }, $state);
  173. }
  174. // When using generateContinuation, the $final parameter for the first traverseNode will
  175. // likely need to be the result of this function.
  176. function generateFinalForContinuation($final, $continuation) {
  177. return function ($result, $state) use ($final, $continuation) {
  178. if (!($continuation instanceof PHPParser_Node_Expr_Variable) || $continuation->name != CONT_NAME) {
  179. array_unshift($result, new PHPParser_Node_Expr_Assign(
  180. new PHPParser_Node_Expr_Variable(CONT_NAME),
  181. $continuation
  182. ));
  183. }
  184. return $final($result, $state);
  185. };
  186. }
  187. function isLValue($expr) {
  188. return ($expr instanceof PHPParser_Node_Expr_Variable ||
  189. $expr instanceof PHPParser_Node_Expr_ArrayDimFetch ||
  190. $expr instanceof PHPParser_Node_Expr_FuncCall ||
  191. $expr instanceof PHPParser_Node_Expr_MethodCall ||
  192. $expr instanceof PHPParser_Node_Expr_PropertyFetch ||
  193. $expr instanceof PHPParser_Node_Expr_StaticCall ||
  194. $expr instanceof PHPParser_Node_Expr_StaticPropertyFetch);
  195. }
  196. // When you need to call the current continuation with a return value, this function
  197. // will generate the return/call to emit.
  198. function generateReturn($value, $state) {
  199. if (!isset($value)) {
  200. $value = new PHPParser_Node_Expr_ConstFetch(new PHPParser_Node_Name('null'));
  201. }
  202. $is_return_by_ref = $state ? $state->getIsReturnByRef() : false;
  203. $is_return_by_ref &= isLValue($value); // Don't try to return rvalue by ref. Simulates PHP behavior on return by reference.
  204. return [generateThunk(
  205. [
  206. new PHPParser_Node_Expr_ClosureUse(CONT_NAME),
  207. new PHPParser_Node_Expr_ClosureUse(LOCALS_NAME, true),
  208. new PHPParser_Node_Expr_ClosureUse(TEMP_NAME, true)
  209. ],
  210. [new PHPParser_Node_Stmt_Return(
  211. new PHPParser_Node_Expr_FuncCall(
  212. new PHPParser_Node_Expr_Variable(CONT_NAME),
  213. $is_return_by_ref ?
  214. [
  215. new PHPParser_Node_Expr_ConstFetch(new PHPParser_Node_Name('null')),
  216. new PHPParser_Node_Arg($value),
  217. new PHPParser_Node_Expr_ConstFetch(new PHPParser_Node_Name('true'))
  218. ]
  219. :
  220. [new PHPParser_Node_Arg($value)]
  221. )
  222. )],
  223. 'return'
  224. )];
  225. }
  226. function generateTryCatchCall($expr, $state) {
  227. return new PHPParser_Node_Stmt_TryCatch(
  228. [new PHPParser_Node_Stmt_Return(
  229. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Expr_Variable(CONT_NAME), [
  230. $expr
  231. ])
  232. )],
  233. [new PHPParser_Node_Stmt_Catch(
  234. new PHPParser_Node_Name('Exception'),
  235. VALUE_NAME,
  236. [new PHPParser_Node_Stmt_Return(
  237. new PHPParser_Node_Expr_FuncCall(
  238. new PHPParser_Node_Expr_ArrayDimFetch(
  239. new PHPParser_Node_Expr_Variable(EXCEPT_NAME),
  240. new PHPParser_Node_Scalar_LNumber($state->getCatchNum())
  241. ),
  242. [new PHPParser_Node_Expr_Variable(VALUE_NAME)]
  243. )
  244. )]
  245. )]
  246. );
  247. }
  248. function generateMethodCall($object, $function, $args, $type, $state) {
  249. $is_builtin_call = in_array($function, $GLOBALS['MAGIC_METHODS']);
  250. $get_class = new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('get_class'), [$object]);
  251. if ($type == PHPParser_Node_Expr_StaticCall) {
  252. if ($object->toString() == 'self') {
  253. $object = new PHPParser_Node_Name($state->getSelf());
  254. }
  255. elseif ($object->toString() == 'parent') {
  256. $object = new PHPParser_Node_Name($state->getParent());
  257. }
  258. $get_class = new PHPParser_Node_Scalar_String($object->toString());
  259. if (isset(CpsRuntime::$builtin_methods[$object->toString()][$function])) {
  260. $is_builtin_call = true;
  261. }
  262. }
  263. if ($is_builtin_call) {
  264. $stmt = generateTryCatchCall(new $type($object, $function, $args), $state);
  265. }
  266. else {
  267. $orig_args = $args;
  268. array_unshift($args, $state->generateExceptParameter());
  269. array_unshift($args, new PHPParser_Node_Expr_Variable(CONT_NAME));
  270. $stmt = new PHPParser_Node_Stmt_If(
  271. new PHPParser_Node_Expr_Isset([
  272. new PHPParser_Node_Expr_ArrayDimFetch(
  273. new PHPParser_Node_Expr_ArrayDimFetch(
  274. new PHPParser_Node_Expr_StaticPropertyFetch(
  275. new PHPParser_Node_Name('CpsRuntime'),
  276. 'builtin_methods'
  277. ),
  278. $get_class
  279. ),
  280. new PHPParser_Node_Scalar_String($function)
  281. )
  282. ]),
  283. [
  284. 'stmts' => [
  285. generateTryCatchCall(new $type($object, $function, $orig_args), $state)
  286. ],
  287. 'else' => new PHPParser_Node_Stmt_Else([
  288. new PHPParser_Node_Stmt_Return(new $type($object, $function, $args))
  289. ])
  290. ]
  291. );
  292. }
  293. return [generateThunk(
  294. [
  295. new PHPParser_Node_Expr_ClosureUse(CONT_NAME),
  296. new PHPParser_Node_Expr_ClosureUse(EXCEPT_NAME),
  297. new PHPParser_Node_Expr_ClosureUse(LOCALS_NAME, true),
  298. new PHPParser_Node_Expr_ClosureUse(TEMP_NAME, true)
  299. ],
  300. [$stmt],
  301. 'call'
  302. )];
  303. }
  304. function wrapFunctionForCallback($function, $state) {
  305. $user_call_stmts = [new PHPParser_Node_Stmt_Return(
  306. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('call_user_func_array'), [
  307. new PHPParser_Node_Expr_Variable(VALUE_NAME),
  308. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('array_merge'), [
  309. new PHPParser_Node_Expr_Array([
  310. new PHPParser_Node_Expr_Variable(CONT_NAME),
  311. new PHPParser_Node_Expr_Variable(EXCEPT_NAME)
  312. ]),
  313. new PHPParser_Node_Expr_ArrayDimFetch(
  314. new PHPParser_Node_Expr_Variable(TEMP_NAME),
  315. new PHPParser_Node_Scalar_String(ARGS_TEMP_NAME)
  316. )
  317. ])
  318. ])
  319. )];
  320. $function_transformer = new PHPParser_Node_Expr_ArrayDimFetch(
  321. new PHPParser_Node_Expr_StaticPropertyFetch(
  322. new PHPParser_Node_Name('CpsRuntime'),
  323. 'builtin_functions'
  324. ),
  325. new PHPParser_Node_Expr_Variable(VALUE_NAME)
  326. );
  327. $builtin_call_stmts = [new PHPParser_Node_Stmt_Return(
  328. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('call_user_func'), [
  329. $function_transformer,
  330. new PHPParser_Node_Expr_Variable(VALUE_NAME),
  331. new PHPParser_Node_Expr_ArrayDimFetch(
  332. new PHPParser_Node_Expr_Variable(TEMP_NAME),
  333. new PHPParser_Node_Scalar_String(ARGS_TEMP_NAME)
  334. ),
  335. new PHPParser_Node_Expr_Variable(CONT_NAME),
  336. $state->generateExceptParameter()
  337. ])
  338. )];
  339. // not a trampoline - this is the actual callback function that wraps the CPS callback - it takes parameters
  340. return new PHPParser_Node_Expr_Closure([
  341. 'uses' => [
  342. new PHPParser_Node_Expr_ClosureUse(LOCALS_NAME, true),
  343. new PHPParser_Node_Expr_ClosureUse(TEMP_NAME)
  344. ],
  345. 'stmts' => array_merge(
  346. [
  347. new PHPParser_Node_Expr_Assign(
  348. new PHPParser_Node_Expr_ArrayDimFetch(
  349. new PHPParser_Node_Expr_Variable(TEMP_NAME),
  350. new PHPParser_Node_Scalar_String(ARGS_TEMP_NAME)
  351. ),
  352. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('func_get_args'))
  353. )
  354. ],
  355. generateTrampoline([
  356. new PHPParser_Node_Expr_Assign(new PHPParser_Node_Expr_Variable(VALUE_NAME), $function),
  357. new PHPParser_Node_Stmt_If(
  358. new PHPParser_Node_Expr_BooleanAnd(
  359. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('is_string'), [new PHPParser_Node_Expr_Variable(VALUE_NAME)]),
  360. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('array_key_exists'), [
  361. new PHPParser_Node_Expr_Variable(VALUE_NAME),
  362. new PHPParser_Node_Expr_StaticPropertyFetch(
  363. new PHPParser_Node_Name('CpsRuntime'),
  364. 'builtin_functions'
  365. )
  366. ])
  367. ),
  368. [
  369. 'stmts' => $builtin_call_stmts,
  370. 'else' => new PHPParser_Node_Stmt_Else($user_call_stmts)
  371. ]
  372. )
  373. ])
  374. )
  375. ]);
  376. }
  377. function generateFunctionCall($function, $args, $state) {
  378. if ($function instanceof PHPParser_Node_Scalar_String) {
  379. $function = new PHPParser_Node_Name($function->value);
  380. }
  381. $function_key = null;
  382. if ($function instanceof PHPParser_Node_Name && array_key_exists($function->toString(), CpsRuntime::$function_transforms)) {
  383. $function_key = $function->toString();
  384. }
  385. if ($function instanceof PHPParser_Node_Name) {
  386. if ($function_key) {
  387. return call_user_func(CpsRuntime::$function_transforms[$function_key], $function, $args, $state);
  388. }
  389. else {
  390. // if we have a null function key that means it's not a true builtin function
  391. // we need to check builtin list at runtime in case it is a "fast" function
  392. $callable_function = $function;
  393. $function = new PHPParser_Node_Scalar_String($function->toString());
  394. $assign_value = null;
  395. }
  396. }
  397. elseif ($function instanceof PHPParser_Node_Expr_Closure) {
  398. $stmts = call_user_func(CpsRuntime::$function_transforms[$function_key], new PHPParser_Node_Expr_Variable(VALUE_NAME), $args, $state);
  399. array_unshift($stmts, new PHPParser_Node_Expr_Assign(
  400. new PHPParser_Node_Expr_Variable(VALUE_NAME),
  401. $function
  402. ));
  403. return $stmts;
  404. }
  405. else {
  406. $assign_value = new PHPParser_Node_Expr_Assign(
  407. new PHPParser_Node_Expr_Variable(VALUE_NAME),
  408. $function
  409. );
  410. $function = new PHPParser_Node_Expr_Variable(VALUE_NAME);
  411. $callable_function = $function;
  412. }
  413. $user_call_stmts = call_user_func(CpsRuntime::$function_transforms[null], $callable_function, $args, $state);
  414. $function_transformer = new PHPParser_Node_Expr_ArrayDimFetch(
  415. new PHPParser_Node_Expr_StaticPropertyFetch(
  416. new PHPParser_Node_Name('CpsRuntime'),
  417. 'builtin_functions'
  418. ),
  419. $function
  420. );
  421. $builtin_call_stmts = [new PHPParser_Node_Stmt_Return(
  422. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('call_user_func'), [
  423. $function_transformer,
  424. $function,
  425. new PHPParser_Node_Expr_Array(array_map(function ($arg) {
  426. return new PHPParser_Node_Expr_ArrayItem($arg->value, null, isLValue($arg->value));
  427. }, $args)),
  428. new PHPParser_Node_Expr_Variable(CONT_NAME),
  429. $state->generateExceptParameter()
  430. ])
  431. )];
  432. $conditions = new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('array_key_exists'),
  433. [
  434. $function,
  435. new PHPParser_Node_Expr_StaticPropertyFetch(
  436. new PHPParser_Node_Name('CpsRuntime'),
  437. 'builtin_functions'
  438. )
  439. ]
  440. );
  441. if (!($function instanceof PHPParser_Node_Scalar_String)) {
  442. $conditions = new PHPParser_Node_Expr_BooleanAnd(
  443. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('is_string'), [$function]),
  444. $conditions
  445. );
  446. }
  447. $stmts = [new PHPParser_Node_Stmt_If($conditions, [
  448. 'stmts' => $builtin_call_stmts,
  449. 'else' => new PHPParser_Node_Stmt_Else($user_call_stmts)
  450. ])];
  451. if ($assign_value) {
  452. array_unshift($stmts, $assign_value);
  453. }
  454. return $stmts;
  455. }
  456. function generateJump($label_num) {
  457. return generateThunk(
  458. [
  459. new PHPParser_Node_Expr_ClosureUse(LOCALS_NAME),
  460. new PHPParser_Node_Expr_ClosureUse(TEMP_NAME, true),
  461. new PHPParser_Node_Expr_ClosureUse(LABELS_NAME, true)
  462. ],
  463. [new PHPParser_Node_Stmt_Return(
  464. new PHPParser_Node_Expr_FuncCall(
  465. new PHPParser_Node_Expr_ArrayDimFetch(
  466. new PHPParser_Node_Expr_Variable(LABELS_NAME),
  467. new PHPParser_Node_Scalar_LNumber($label_num)
  468. ),
  469. [
  470. new PHPParser_Node_Arg(new PHPParser_Node_Expr_Variable(LOCALS_NAME)),
  471. new PHPParser_Node_Arg(new PHPParser_Node_Expr_Variable(TEMP_NAME))
  472. ]
  473. )
  474. )],
  475. 'jump'
  476. );
  477. }
  478. function generateParams($node_params, &$params, &$param_items) {
  479. $param_items = [];
  480. $params = [];
  481. $i = 0;
  482. foreach ($node_params as $param) {
  483. $param_name = PARAM_NAME . ($i++);
  484. $param_items[] = new PHPParser_Node_Expr_ArrayItem(
  485. new PHPParser_Node_Expr_Variable($param_name),
  486. new PHPParser_Node_Scalar_String($param->name),
  487. true
  488. );
  489. $params[] = new PHPParser_Node_Param($param_name, $param->default, $param->type, $param->byRef);
  490. }
  491. }
  492. class FunctionState {
  493. private $is_return_by_ref = false;
  494. private $block_num = 0;
  495. private $basic_blocks = [];
  496. private $basic_block_aliases = [];
  497. private $break_stack = [];
  498. private $continue_stack = [];
  499. private $label_names = [];
  500. private $catch_num = 0;
  501. private $catches = [];
  502. private $self = null;
  503. private $parent = null;
  504. private $builtin_methods = [];
  505. private $is_in_instance_method = false;
  506. function setIsInInstanceMethod($is_in_instance_method) {
  507. $this->is_in_instance_method = $is_in_instance_method;
  508. }
  509. function isInInstanceMethod() {
  510. return $this->is_in_instance_method;
  511. }
  512. function addBuiltinMethod($name) {
  513. $this->builtin_methods[$name] = true;
  514. }
  515. function generateBuiltinMethodDeclarations() {
  516. return [new PHPParser_Node_Expr_Assign(
  517. new PHPParser_Node_Expr_ArrayDimFetch(
  518. new PHPParser_Node_Expr_StaticPropertyFetch(
  519. new PHPParser_Node_Name('CpsRuntime'),
  520. 'builtin_methods'
  521. ),
  522. new PHPParser_Node_Scalar_String($this->getSelf())
  523. ),
  524. new PHPParser_Node_Expr_Array(array_map(
  525. function($method_name) {
  526. return new PHPParser_Node_Expr_ArrayItem(
  527. new PHPParser_Node_Expr_ConstFetch(new PHPParser_Node_Name('true')),
  528. new PHPParser_Node_Scalar_String($method_name)
  529. );
  530. },
  531. array_keys($this->builtin_methods)
  532. ))
  533. )];
  534. }
  535. function setSelf($self) {
  536. $this->self = $self;
  537. }
  538. function getSelf() {
  539. return $this->self;
  540. }
  541. function setParent($parent) {
  542. $this->parent = $parent;
  543. }
  544. function getParent() {
  545. return $this->parent;
  546. }
  547. function setIsReturnByRef($is_return_by_ref = true) {
  548. $this->is_return_by_ref = $is_return_by_ref;
  549. }
  550. function getIsReturnByRef() {
  551. return $this->is_return_by_ref;
  552. }
  553. function addBreakAndContinue($break_num, $continue_num) {
  554. $state = clone $this;
  555. $state->block_num =& $this->block_num;
  556. $state->basic_blocks =& $this->basic_blocks;
  557. $state->basic_block_aliases =& $this->basic_block_aliases;
  558. $state->label_names =& $this->label_names;
  559. $state->catch_num =& $this->catch_num;
  560. $state->catches =& $this->catches;
  561. array_unshift($state->break_stack, $break_num);
  562. array_unshift($state->continue_stack, $continue_num);
  563. return $state;
  564. }
  565. function getCatchNum() {
  566. return $this->catch_num;
  567. }
  568. function generateExceptParameter() {
  569. return new PHPParser_Node_Expr_Array([new PHPParser_Node_Expr_ArrayItem(
  570. new PHPParser_Node_Expr_ArrayDimFetch(
  571. new PHPParser_Node_Expr_Variable(EXCEPT_NAME),
  572. new PHPParser_Node_Scalar_LNumber($this->catch_num)
  573. )
  574. )]);
  575. }
  576. function addCatches($catches) {
  577. $state = clone $this;
  578. $state->block_num =& $this->block_num;
  579. $state->basic_blocks =& $this->basic_blocks;
  580. $state->basic_block_aliases =& $this->basic_block_aliases;
  581. $state->label_names =& $this->label_names;
  582. $state->break_stack =& $this->break_stack;
  583. $state->continue_stack =& $this->continue_stack;
  584. $state->catches =& $this->catches;
  585. $state->catch_num = $this->catch_num + 1;
  586. $state->catches[$state->catch_num] = $catches;
  587. return $state;
  588. }
  589. function generateThrow($exception) {
  590. $stmts = [];
  591. $exception = assignToTemp($exception, $stmts);
  592. // trampoline
  593. $stmts[] = generateThunk(
  594. [
  595. new PHPParser_Node_Expr_ClosureUse(EXCEPT_NAME),
  596. new PHPParser_Node_Expr_ClosureUse(TEMP_NAME)
  597. ],
  598. [
  599. new PHPParser_Node_Stmt_Return(
  600. new PHPParser_Node_Expr_FuncCall(
  601. new PHPParser_Node_Expr_ArrayDimFetch(
  602. new PHPParser_Node_Expr_Variable(EXCEPT_NAME),
  603. new PHPParser_Node_Scalar_LNumber($this->catch_num)
  604. ),
  605. [$exception]
  606. )
  607. )
  608. ],
  609. 'throw'
  610. );
  611. return $stmts;
  612. }
  613. function generateCatches() {
  614. $stmts = [];
  615. foreach ($this->catches as $catch_num => $catches) {
  616. $stmts[] = new PHPParser_Node_Expr_Assign(
  617. new PHPParser_Node_Expr_ArrayDimFetch(new PHPParser_Node_Expr_Variable(EXCEPT_NAME)),
  618. new PHPParser_Node_Expr_Closure([
  619. 'params' => [new PHPParser_Node_Param(VALUE_NAME)],
  620. 'uses' => [
  621. new PHPParser_Node_Expr_ClosureUse(CONT_NAME),
  622. new PHPParser_Node_Expr_ClosureUse(EXCEPT_NAME, true),
  623. new PHPParser_Node_Expr_ClosureUse(LOCALS_NAME, true),
  624. new PHPParser_Node_Expr_ClosureUse(TEMP_NAME, true),
  625. new PHPParser_Node_Expr_ClosureUse(LABELS_NAME, true)
  626. ],
  627. 'stmts' => $catches
  628. ])
  629. );
  630. }
  631. return $stmts;
  632. }
  633. function generateBlockNum() {
  634. return $this->block_num++;
  635. }
  636. function addBasicBlock($block_num, $stmts) {
  637. if (count($stmts) == 1 &&
  638. ($return = $stmts[0]) instanceof PHPParser_Node_Stmt_Return &&
  639. ($closure = $return->expr) instanceof PHPParser_Node_Expr_Closure &&
  640. count($closure->stmts) == 1 &&
  641. ($return = $closure->stmts[0]) instanceof PHPParser_Node_Stmt_Return &&
  642. ($funccall = $return->expr) instanceof PHPParser_Node_Expr_FuncCall &&
  643. ($name = $funccall->name) instanceof PHPParser_Node_Expr_ArrayDimFetch &&
  644. ($var = $name->var) instanceof PHPParser_Node_Expr_Variable &&
  645. $var->name == LABELS_NAME)
  646. {
  647. // If all a basic block does is goto another basic block, point the basic
  648. // block pointer directly at the other block.
  649. $this->basic_block_aliases[$block_num] = $name->dim->value;
  650. }
  651. else {
  652. $this->basic_blocks[$block_num] = $stmts;
  653. }
  654. }
  655. function generateBasicBlocks() {
  656. $assignments = [];
  657. foreach ($this->basic_blocks as $num => $block) {
  658. $assignments[] = new PHPParser_Node_Expr_Assign(
  659. new PHPParser_Node_Expr_ArrayDimFetch(
  660. new PHPParser_Node_Expr_Variable(LABELS_NAME),
  661. new PHPParser_Node_Scalar_LNumber($num)
  662. ),
  663. new PHPParser_Node_Expr_Closure([
  664. 'params' => [
  665. new PHPParser_Node_Param(LOCALS_NAME, null, null, true),
  666. new PHPParser_Node_Param(TEMP_NAME, null, null, true)
  667. ],
  668. 'uses' => [
  669. new PHPParser_Node_Expr_ClosureUse(CONT_NAME),
  670. new PHPParser_Node_Expr_ClosureUse(EXCEPT_NAME, true),
  671. new PHPParser_Node_Expr_ClosureUse(LABELS_NAME, true)
  672. ],
  673. 'stmts' => $block
  674. ])
  675. );
  676. }
  677. foreach ($this->basic_block_aliases as $num => $to) {
  678. while (array_key_exists($to, $this->basic_block_aliases)) {
  679. $to = $this->basic_block_aliases[$to]; // Fully resolve aliases
  680. }
  681. $assignments[] = new PHPParser_Node_Expr_Assign(
  682. new PHPParser_Node_Expr_ArrayDimFetch(
  683. new PHPParser_Node_Expr_Variable(LABELS_NAME),
  684. new PHPParser_Node_Scalar_LNumber($num)
  685. ),
  686. new PHPParser_Node_Expr_ArrayDimFetch(
  687. new PHPParser_Node_Expr_Variable(LABELS_NAME),
  688. new PHPParser_Node_Scalar_LNumber($to)
  689. )
  690. );
  691. }
  692. return $assignments;
  693. }
  694. function addLabel($num, $name) {
  695. $this->label_names[$name] = $num;
  696. }
  697. function getLabel($label) {
  698. if (!array_key_exists($label, $this->label_names)) {
  699. throw new Exception('No such label: ' . $label);
  700. }
  701. return generateJump($this->label_names[$label]);
  702. }
  703. function getBreak($n = 1) {
  704. if (!$n) {
  705. $n = 1;
  706. }
  707. if ($n > count($this->break_stack)) {
  708. throw new Exception('Too many break levels');
  709. }
  710. return generateJump($this->break_stack[$n - 1]);
  711. }
  712. function getContinue($n = 1) {
  713. if (!$n) {
  714. $n = 1;
  715. }
  716. if ($n > count($this->continue_stack)) {
  717. throw new Exception('Too many continue levels');
  718. }
  719. return generateJump($this->continue_stack[$n - 1]);
  720. }
  721. }
  722. function traverseStatements($stmts, $final, $after_stmts, $state, $is_top_level = false) {
  723. if ($stmts === null) { // This is for functions in interfaces
  724. return $final(null, $state);
  725. }
  726. // Hoist functions (and classes, even though PHP doesn't exactly do so)
  727. $functions = [];
  728. $statements = [];
  729. foreach ($stmts as $stmt) {
  730. if ($stmt instanceof PHPParser_Node_Stmt_Function/* ||
  731. $stmt instanceof PHPParser_Node_Stmt_Class ||
  732. $stmt instanceof PHPParser_Node_Stmt_Interface*/)
  733. {
  734. $functions[] = $stmt;
  735. }
  736. else {
  737. $statements[] = $stmt;
  738. }
  739. }
  740. $stmts = array_merge($functions, $statements);
  741. return call_user_func($loop = function ($final, $stmts, $state) use (&$loop, $after_stmts) {
  742. if ($node = array_shift($stmts)) {
  743. return function () use ($node, $final, &$loop, $stmts, $state) {
  744. return traverseNode($node, function ($node_result, $final, $state) use (&$loop, $stmts) {
  745. return $loop(function ($future_result, $state) use ($node_result, $final, $stmts) {
  746. if (!isset($node_result)) {
  747. // do nothing
  748. }
  749. elseif (is_array($node_result)) {
  750. $future_result = array_merge($node_result, $future_result);
  751. }
  752. else {
  753. array_unshift($future_result, $node_result);
  754. }
  755. return $final($future_result, $state);
  756. }, $stmts, $state);
  757. }, $final, $state);
  758. };
  759. }
  760. else {
  761. return $final($after_stmts, $state);
  762. }
  763. }, function ($result, $state) use ($final, $is_top_level) {
  764. if ($is_top_level) {
  765. $result = array_merge($state->generateBasicBlocks(), $state->generateCatches(), $result);
  766. }
  767. return $final($result, $state);
  768. }, $stmts, $state);
  769. }
  770. // $next is a function($node_result, $final, $state), where $node_result is a node or array of nodes and $final is the new final step
  771. // $final is a function($result), where $result is an array of statements
  772. function traverseNode($node, $next, $final, $state) {
  773. if ($node instanceof PHPParser_Node_Scalar_LineConst) {
  774. return $next(new PHPParser_Node_Scalar_LNumber($node->line), $final, $state);
  775. }
  776. elseif ($node instanceof PHPParser_Node_Scalar_FileConst) {
  777. return $next(new PHPParser_Node_Scalar_String($GLOBALS['file']), $final, $state);
  778. }
  779. elseif ($node instanceof PHPParser_Node_Scalar_DirConst) {
  780. return $next(new PHPParser_Node_Scalar_String(dirname($GLOBALS['file'])), $final, $state);
  781. }
  782. elseif ($node instanceof PHPParser_Node_Scalar_Encapsed) {
  783. return call_user_func($loop = function ($parts, $compiled_parts, $final, $state) use (&$loop, $next) {
  784. if (count($parts)) {
  785. $part = array_shift($parts);
  786. return traverseNode($part, function ($part, $final, $state) use (&$loop, $parts, $compiled_parts) {
  787. $compiled_parts[] = $part;
  788. return $loop($parts, $compiled_parts, $final, $state);
  789. }, $final, $state);
  790. }
  791. else {
  792. return $next(new PHPParser_Node_Scalar_Encapsed($compiled_parts), $final, $state);
  793. }
  794. }, $node->parts, [], $final, $state);
  795. }
  796. elseif ($node instanceof PHPParser_Node_Expr_ConstFetch && $node->name->toString() == '__COMPILER_HALT_OFFSET__') {
  797. if (isset($GLOBALS['compiler_halt_offset'])) {
  798. $result = $GLOBALS['compiler_halt_offset'];
  799. }
  800. else {
  801. $result = new NodeReference($node);
  802. $GLOBALS['compiler_halt_offset_nodes'][] = $result;
  803. }
  804. return $next($result, $final, $state);
  805. }
  806. elseif ($node instanceof PHPParser_Node_Stmt_HaltCompiler) {
  807. $offset = new PHPParser_Node_Scalar_LNumber(strlen($GLOBALS['source']) - strlen($node->remaining));
  808. if (isset($GLOBALS['compiler_halt_offset_nodes'])) {
  809. foreach ($GLOBALS['compiler_halt_offset_nodes'] as $offset_node) {
  810. $offset_node->setNode($offset);
  811. }
  812. unset($GLOBALS['compiler_halt_offset_nodes']);
  813. }
  814. $GLOBALS['compiler_halt_offset'] = $offset;
  815. return $final(generateReturn(new PHPParser_Node_Expr_ConstFetch(new PHPParser_Node_Name('null')), $state), $state);
  816. }
  817. elseif ($node instanceof PHPParser_Node_Stmt_Const) {
  818. return call_user_func($loop = function ($consts, $compiled_consts, $final, $state) use (&$loop, $next) {
  819. if ($const = array_shift($consts)) {
  820. return traverseNode($const->value, function ($const_value, $final, $state) use ($const, $consts, &$loop) {
  821. $compiled_consts[] = new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('define'), [
  822. new PHPParser_Node_Scalar_String($const->name),
  823. $const_value
  824. ]);
  825. return function () use (&$loop, $consts, $compiled_consts, $final, $state) {
  826. return $loop($consts, $compiled_consts, $final, $state);
  827. };
  828. }, $final, $state);
  829. }
  830. else {
  831. return $next($compiled_consts, $final, $state);
  832. }
  833. }, $node->consts, [], $final, $state);
  834. }
  835. elseif ($node instanceof PHPParser_Node_Expr_ConstFetch) {
  836. return $next($node, $final, $state);
  837. }
  838. elseif ($node === null || is_string($node) || $node instanceof PHPParser_Node_Scalar || $node instanceof PHPParser_Node_Name || $node instanceof PHPParser_Node_Stmt_InlineHTML) {
  839. return $next($node, $final, $state);
  840. }
  841. elseif ($node instanceof PHPParser_Node_Stmt_While) {
  842. $continue_num = $state->generateBlockNum();
  843. $break_num = $state->generateBlockNum();
  844. return traverseNode($node->cond, function ($cond, $final, $state) use ($node, $next, $continue_num, $break_num) {
  845. return traverseStatements($node->stmts, function ($while_body) use ($cond, $next, $continue_num, $break_num, $final, $state) {
  846. return $next(null, function ($after_while, $state) use ($break_num, $continue_num, $while_body, $cond, $final) {
  847. $state->addBasicBlock($break_num, $after_while);
  848. $while_body = [
  849. new PHPParser_Node_Stmt_If($cond, ['stmts' => $while_body]),
  850. generateJump($break_num)
  851. ];
  852. return $final($while_body, $state);
  853. }, $state);
  854. }, [generateJump($continue_num)], $state->addBreakAndContinue($break_num, $continue_num));
  855. }, function ($while_body, $state) use ($final, $continue_num) {
  856. $state->addBasicBlock($continue_num, $while_body);
  857. return $final([generateJump($continue_num)], $state);
  858. }, $state);
  859. }
  860. elseif ($node instanceof PHPParser_Node_Stmt_Do) {
  861. $continue_num = $state->generateBlockNum();
  862. $break_num = $state->generateBlockNum();
  863. $body_num = $state->generateBlockNum();
  864. return traverseNode($node->cond, function ($cond, $final, $state) use ($node, $next, $continue_num, $break_num, $body_num) {
  865. return traverseStatements($node->stmts, function ($do_body) use ($cond, $next, $continue_num, $break_num, $body_num, $final, $state) {
  866. $state->addBasicBlock($body_num, $do_body);
  867. return $next(null, function ($after_do, $state) use ($break_num, $continue_num, $body_num, $do_body, $cond, $final) {
  868. $state->addBasicBlock($break_num, $after_do);
  869. return $final([
  870. new PHPParser_Node_Stmt_If($cond,
  871. ['stmts' => [generateJump($body_num)]]
  872. ),
  873. generateJump($break_num)
  874. ], $state);
  875. }, $state);
  876. }, [generateJump($continue_num)], $state->addBreakAndContinue($break_num, $continue_num));
  877. }, function ($cond_block, $state) use ($final, $continue_num, $body_num) {
  878. $state->addBasicBlock($continue_num, $cond_block);
  879. return $final([generateJump($body_num)], $state);
  880. }, $state);
  881. }
  882. elseif ($node instanceof PHPParser_Node_Stmt_For) {
  883. $continue_num = $state->generateBlockNum();
  884. $break_num = $state->generateBlockNum();
  885. return traverseStatements($node->init, function ($init) use ($node, $continue_num, $break_num, $state, $next, $final) {
  886. $cond = $node->cond;
  887. $cond_var = generateTemp();
  888. if (count($cond)) {
  889. $cond[] = new PHPParser_Node_Expr_Assign($cond_var, array_pop($cond));
  890. }
  891. return call_user_func($cond_loop = function ($conds, $compiled_conds, $state) use (&$cond_loop, $node, $next, $final, $continue_num, $break_num, $init) {
  892. if ($cond = array_shift($conds)) {
  893. return traverseNode($cond, function ($cond, $final, $state) use (&$cond_loop, $conds, $compiled_conds) {
  894. $compiled_conds[] = $cond;
  895. return function () use (&$cond_loop, $conds, $compiled_conds, $state) {
  896. return $cond_loop($conds, $compiled_conds, $state);
  897. };
  898. }, $final, $state);
  899. }
  900. else {
  901. return traverseStatements($node->loop, function ($loop) use ($node, $state, $break_num, $continue_num, $compiled_conds, $next, $final, $init) {
  902. return traverseStatements($node->stmts, function ($for_body) use ($state, $continue_num, $break_num, $compiled_conds, $next, $final, $init) {
  903. if (count($compiled_conds)) {
  904. $last_cond = array_pop($compiled_conds);
  905. $for_body = array_merge($compiled_conds, [
  906. new PHPParser_Node_Stmt_If($last_cond, [
  907. 'stmts' => $for_body
  908. ])
  909. ]);
  910. }
  911. $for_body[] = generateJump($break_num);
  912. $state->addBasicBlock($continue_num, $for_body);
  913. return $next(null, function ($after_for, $state) use ($break_num, $final, $init) {
  914. $state->addBasicBlock($break_num, $after_for);
  915. return $final($init, $state);
  916. }, $state);
  917. }, $loop, $state->addBreakAndContinue($break_num, $continue_num));
  918. }, [generateJump($continue_num)], $state);
  919. }
  920. }, $node->cond, [], $state);
  921. }, [generateJump($continue_num)], $state);
  922. }
  923. elseif ($node instanceof PHPParser_Node_Stmt_Foreach) {
  924. // TODO handle non-array Traversables
  925. $continue_num = $state->generateBlockNum();
  926. $break_num = $state->generateBlockNum();
  927. return traverseNode($node->expr, function ($expr, $final, $state) use ($continue_num, $break_num, $node, $next) {
  928. $stmts = [];
  929. $temp = assignToTemp($expr, $stmts, $node->byRef);
  930. $stmts[] = new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('reset'), [$temp]);
  931. return traverseStatements($node->stmts, function ($foreach_body) use ($final, $state, $node, $next, $continue_num, $break_num, $stmts, $temp) {
  932. return traverseNode($node->keyVar, function ($keyVar, $final, $state) use ($foreach_body, $node, $next, $break_num, $temp) {
  933. return traverseNode($node->valueVar, function ($valueVar, $final, $state) use ($foreach_body, $keyVar, $next, $break_num, $temp) {
  934. return $next(null, function ($after_foreach, $state) use ($break_num, $foreach_body, $temp, $final, $keyVar, $valueVar) {
  935. $state->addBasicBlock($break_num, $after_foreach);
  936. $keyTemp = generateTemp();
  937. array_unshift($foreach_body, new PHPParser_Node_Expr_AssignRef(
  938. $valueVar,
  939. new PHPParser_Node_Expr_ArrayDimFetch($temp, $keyTemp)
  940. ));
  941. if ($keyVar) {
  942. array_unshift($foreach_body, new PHPParser_Node_Expr_Assign(
  943. $keyVar,
  944. $keyTemp
  945. ));
  946. }
  947. $foreach_body = [
  948. new PHPParser_Node_Stmt_If(
  949. new PHPParser_Node_Expr_AssignList(
  950. [$keyTemp, null],
  951. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('each'), [$temp])
  952. ),
  953. ['stmts' => $foreach_body]
  954. ),
  955. generateJump($break_num)
  956. ];
  957. return $final($foreach_body, $state);
  958. }, $state);
  959. }, $final, $state);
  960. }, function ($foreach_body, $state) use ($final, $continue_num, $stmts) {
  961. $state->addBasicBlock($continue_num, $foreach_body);
  962. $stmts[] = generateJump($continue_num);
  963. return $final($stmts, $state);
  964. }, $state);
  965. }, [generateJump($continue_num)], $state->addBreakAndContinue($break_num, $continue_num));
  966. }, $final, $state);
  967. }
  968. elseif ($node instanceof PHPParser_Node_Stmt_If) {
  969. $done_num = $state->generateBlockNum();
  970. return traverseNode($node->cond, function ($cond, $final, $state) use ($node, $next, $done_num) {
  971. $next_num = $done_num;
  972. if (count($node->elseifs) > 0 || isset($node->else)) {
  973. $next_num = $state->generateBlockNum();
  974. }
  975. return traverseStatements($node->stmts, function ($if_body, $state) use ($cond, $next_num, $done_num, $final, $node) {
  976. $if_body = [
  977. new PHPParser_Node_Stmt_If($cond, ['stmts' => $if_body]),
  978. generateJump($next_num)
  979. ];
  980. // Handle zero or more elseif blocks
  981. $elseifs = $node->elseifs;
  982. $else = $node->else;
  983. return call_user_func($loop = function ($next_num, $elseifs, $final, $state) use (&$loop, $done_num, $else, $if_body) {
  984. if (isset($elseifs) && $elseif = array_shift($elseifs)) {
  985. $elseif_num = $next_num;
  986. $next_num = $done_num;
  987. if (count($elseifs) > 0 || isset($else)) {
  988. $next_num = $state->generateBlockNum();
  989. }
  990. return traverseNode($elseif->cond, function ($cond, $final, $state) use (&$loop, $elseif, $elseif_num, $next_num, $done_num, $elseifs) {
  991. return traverseStatements($elseif->stmts, function ($elseif_body, $state) use (&$loop, $elseif_num, $next_num, $cond, $elseifs, $final) {
  992. $elseif_body = [
  993. new PHPParser_Node_Stmt_If($cond, ['stmts' => $elseif_body]),
  994. generateJump($next_num)
  995. ];
  996. $state->addBasicBlock($elseif_num, $elseif_body);
  997. return function () use (&$loop, $next_num, $elseifs, $final, $state) {
  998. return $loop($next_num, $elseifs, $final, $state);
  999. };
  1000. }, [generateJump($done_num)], $state);
  1001. }, $final, $state);
  1002. }
  1003. elseif (isset($else)) {
  1004. return traverseStatements($else->stmts, function ($else_body, $state) use ($next_num, $if_body, $final) {
  1005. $state->addBasicBlock($next_num, $else_body);
  1006. return $final($if_body, $state);
  1007. }, [generateJump($done_num)], $state);
  1008. }
  1009. else {
  1010. return $final($if_body, $state);
  1011. }
  1012. }, $next_num, $elseifs, $final, $state);
  1013. }, [generateJump($done_num)], $state);
  1014. }, function ($if_body, $state) use ($final, $done_num, $next) {
  1015. return $next(null, function ($after_if, $state) use ($done_num, $final, $if_body) {
  1016. $state->addBasicBlock($done_num, $after_if);
  1017. return $final($if_body, $state);
  1018. }, $state);
  1019. }, $state);
  1020. }
  1021. elseif ($node instanceof PHPParser_Node_Stmt_Break) {
  1022. $num = $node->num->value;
  1023. return $next(null, function ($result, $new_state) use ($final, $state, $num) {
  1024. return $final([$state->getBreak($num)], $new_state);
  1025. }, $state);
  1026. }
  1027. elseif ($node instanceof PHPParser_Node_Stmt_Continue) {
  1028. $num = $node->num->value;
  1029. return $next(null, function ($result, $new_state) use ($final, $state, $num) {
  1030. return $final([$state->getContinue($num)], $new_state);
  1031. }, $state);
  1032. }
  1033. elseif ($node instanceof PHPParser_Node_Stmt_Label) {
  1034. $label_num = $state->generateBlockNum();
  1035. $state->addLabel($label_num, $node->name);
  1036. return $next(null, function ($after_label, $state) use ($label_num, $final) {
  1037. $state->addBasicBlock($label_num, $after_label);
  1038. return $final([generateJump($label_num)], $state);
  1039. }, $state);
  1040. }
  1041. elseif ($node instanceof PHPParser_Node_Stmt_Goto) {
  1042. $name = $node->name;
  1043. return $next(null, function ($after_goto, $state) use ($final, $name) {
  1044. return $final([$state->getLabel($name)], $state);
  1045. }, $state);
  1046. }
  1047. elseif ($node instanceof PHPParser_Node_Expr_Exit) {
  1048. return traverseNode($node->expr, function ($expr, $final, $state) use ($next) {
  1049. return $next(null, function ($stmts_x, $state_x) use ($expr, $state, $final) {
  1050. // Executable statements after exit() are not reached, but we run the compiler over them anyway.
  1051. return $final([new PHPParser_node_Expr_Exit($expr)], $state);
  1052. }, $state);
  1053. }, $final, $state);
  1054. }
  1055. elseif ($node instanceof PHPParser_Node_Stmt_Return) {
  1056. return traverseNode($node->expr, new ReturnClosure(function ($expr, $final, $state) use ($next) {
  1057. return $next(null, function ($stmts_x, $state_x) use ($expr, $state, $final) {
  1058. // Executable statements after a return are not reached, but we run the compiler over them in case doing so has side effects internal to the compiler (like __halt_compiler())
  1059. return $final(generateReturn($expr, $state), $state);
  1060. }, $state);
  1061. }), $final, $state);
  1062. }
  1063. elseif ($node instanceof PHPParser_Node_Expr_FuncCall) {
  1064. return generateContinuation($next, function ($continuation, $state) use ($node, $final) {
  1065. return traverseNode($node->name, funct

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