PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/runtime.php

https://github.com/lucciano/eventerator
PHP | 257 lines | 225 code | 21 blank | 11 comment | 16 complexity | 5a9f18ed96c5a344e41480297541f416 MD5 | raw file
  1. <?php
  2. // Essentially a namespace for some runtime globals, to keep them out of the global
  3. // namespace
  4. class CpsRuntime {
  5. public static $builtin_functions = [];
  6. public static $function_transforms = [];
  7. public static $builtin_methods = [];
  8. public static $stack_depth = 0;
  9. public static function include_file($file, $compiler, $is_require) {
  10. $include_path = get_include_path();
  11. if (!is_readable($file)) {
  12. foreach (explode(PATH_SEPARATOR, $include_path) as $path) {
  13. $candidate = "$path/$file";
  14. if (is_readable($candidate)) {
  15. $file = $candidate;
  16. break;
  17. }
  18. }
  19. }
  20. $file = realpath($file);
  21. $tempfile = sys_get_temp_dir() . '/cps.' . getmyuid() . $file;
  22. if (!is_readable($tempfile) || filemtime($tempfile) < filemtime($file)) {
  23. // recompile if out of date
  24. @mkdir(dirname($tempfile), 0700, true);
  25. exec("$compiler include $file < $file > $tempfile", $output, $return_value);
  26. }
  27. else {
  28. $return_value = 0;
  29. }
  30. if ($return_value == 0) {
  31. return $tempfile;
  32. }
  33. else {
  34. $error = implode("\n", $output) . readfile($tempfile);
  35. unlink($tempfile);
  36. echo $error;
  37. return $tempfile;
  38. }
  39. }
  40. public static function &trampoline($thunk) {
  41. $result_container = null;
  42. $exception_container = null;
  43. $has_exited = false;
  44. $return_continuation = function ($result, &$result_ref = null, $is_ref = false) use (&$result_container, &$has_exited) {
  45. if ($has_exited) {
  46. throw new Exception('Exiting from trampoline a second time. Program will terminate.');
  47. }
  48. $has_exited = true;
  49. $is_ref ? $result_container =& $result_ref : $result_container = $result;
  50. };
  51. $exception_continuation = array(function ($exception) use (&$exception_container, &$is_exited) {
  52. if ($has_exited) {
  53. throw new Exception('Exiting from trampoline a second time. Program will terminate.');
  54. }
  55. $has_exited = true;
  56. $exception_container = $exception;
  57. });
  58. while ($thunk) {
  59. $thunk = $thunk($return_continuation, $exception_continuation);
  60. if ($has_exited) {
  61. if ($exception_container) {
  62. throw $exception_container[0];
  63. }
  64. else {
  65. return $result_container;
  66. }
  67. }
  68. }
  69. throw new Exception('Trampoline exited without return value');
  70. }
  71. static function add_builtin_function($name) {
  72. self::$function_transforms[$name] = function ($function, $args, $state) {
  73. return array(new PHPParser_Node_Stmt_TryCatch(
  74. array(new PHPParser_Node_Stmt_Return(
  75. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Expr_Variable(CONT_NAME), array(
  76. new PHPParser_Node_Expr_FuncCall($function, $args)
  77. ))
  78. )),
  79. array(new PHPParser_Node_Stmt_Catch(
  80. new PHPParser_Node_Name('Exception'),
  81. VALUE_NAME,
  82. array(new PHPParser_Node_Expr_FuncCall(
  83. new PHPParser_Node_Expr_ArrayDimFetch(
  84. new PHPParser_Node_Expr_Variable(EXCEPT_NAME),
  85. new PHPParser_Node_Scalar_LNumber($state->getCatchNum())
  86. ),
  87. array(new PHPParser_Node_Expr_Variable(VALUE_NAME))
  88. ))
  89. ))
  90. ));
  91. };
  92. self::$builtin_functions[$name] = function ($f, $args, $c, $x) {
  93. try {
  94. return $c(call_user_func_array($f, $args));
  95. }
  96. catch (Exception $e) {
  97. return $x[0]($e);
  98. }
  99. };
  100. }
  101. public static function add_builtin_method($class, $method) {
  102. self::$builtin_methods[$class][$method] = true;
  103. }
  104. }
  105. foreach (get_loaded_extensions() as $extension) {
  106. foreach (get_extension_funcs($extension) ?: array() as $name) {
  107. CpsRuntime::add_builtin_function($name);
  108. }
  109. }
  110. CpsRuntime::$function_transforms['func_get_args'] = function ($function, $args, $state) {
  111. return array(new PHPParser_Node_Stmt_Return(
  112. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Expr_Variable(CONT_NAME), array(
  113. new PHPParser_Node_Expr_ArrayDimFetch(
  114. new PHPParser_Node_Expr_Variable(TEMP_NAME),
  115. new PHPParser_Node_Scalar_String(ARGS_TEMP_NAME)
  116. )
  117. ))
  118. ));
  119. };
  120. //TODOCpsRuntime::$builtin_functions['func_get_args'] = function ($f, $args, $c, $x, $t) {
  121. // return $c($t['A']);
  122. //};
  123. // Wrap callback parameters to builtins in their own trampolines.
  124. foreach (array(
  125. 'spl_autoload_register' => array(0),
  126. 'array_map' => array(0),
  127. 'preg_replace_callback' => array(1),
  128. 'array_diff_uassoc' => array(-1),
  129. 'array_diff_ukey' => array(-1),
  130. 'array_filter' => array(1),
  131. 'array_intersect_uassoc' => array(-1),
  132. 'array_intersect_ukey' => array(-1),
  133. 'array_reduce' => array(1),
  134. 'array_udiff_assoc' => array(-1),
  135. 'array_udiff_uassoc' => array(-1, -2),
  136. 'array_udiff' => array(-1),
  137. 'array_uintersect_assoc' => array(-1),
  138. 'array_uintersect_uassoc' => array(-1, -2),
  139. 'array_uintersect' => array(-1),
  140. 'array_walk_recursive' => array(1),
  141. 'array_walk' => array(1),
  142. 'uasort' => array(1),
  143. 'uksort' => array(1),
  144. 'usort' => array(1)
  145. ) as $function_name => $indices)
  146. {
  147. CpsRuntime::$function_transforms[$function_name] = function ($function, $args, $state) use ($indices) {
  148. foreach ($indices as $i) {
  149. if ($i < 0) {
  150. $i += count($args);
  151. }
  152. if (count($args) > $i) {
  153. $args[$i] = new PHPParser_Node_Arg(wrapFunctionForCallback($args[$i]->value, $state));
  154. }
  155. return $GLOBALS['simple_builtin_static_transform']($function, $args, $state);
  156. }
  157. };
  158. // TODO CpsRuntime::$builtin_functions version of this - is it possible?
  159. }
  160. $builtin_classes = array('Exception');
  161. foreach ($builtin_classes as $class) {
  162. $class_obj = new ReflectionClass($class);
  163. $methods = array();
  164. foreach ($class_obj->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED) as $method_obj) {
  165. $methods[$method_obj->name] = true;
  166. }
  167. CpsRuntime::$builtin_methods[$class] = $methods;
  168. }
  169. // normal user function CPS calling convention for dynamic dispatch (could be used for static too at a slight runtime cost)
  170. CpsRuntime::$function_transforms[null] = function ($function, $args, $state) {
  171. array_unshift($args, $state->generateExceptParameter());
  172. array_unshift($args, new PHPParser_Node_Expr_Variable(CONT_NAME));
  173. return array(new PHPParser_Node_Stmt_Return(new PHPParser_Node_Expr_FuncCall($function, $args)));
  174. };
  175. // callcc() function implementation
  176. CpsRuntime::$function_transforms['callcc'] = function ($function, $args, $state) {
  177. $function = $args[0]->value;
  178. return generateFunctionCall($function, array(new PHPParser_Node_Arg(
  179. new PHPParser_Node_Expr_Closure(array(
  180. 'params' => array(
  181. new PHPParser_Node_Param('j'),
  182. new PHPParser_Node_Param('j'),
  183. new PHPParser_Node_Param('v', new PHPParser_Node_Expr_ConstFetch(new PHPParser_Node_Name('null')))
  184. ),
  185. 'uses' => array(
  186. new PHPParser_Node_Expr_ClosureUse(CONT_NAME)
  187. ),
  188. 'stmts' => array(new PHPParser_Node_Stmt_Return(
  189. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Expr_Variable(CONT_NAME), array(
  190. new PHPParser_Node_Arg(new PHPParser_Node_Expr_Variable('v'))
  191. ))
  192. ))
  193. ))
  194. )), $state);
  195. };
  196. CpsRuntime::$builtin_functions['callcc'] = function ($f, $args, $c, $x) { // allows dynamic call to callcc. crazy, right?
  197. return $args[0]($c, $x, function ($jc, $jx, $v = null) use ($c) {
  198. return $c($v);
  199. });
  200. };
  201. CpsRuntime::$function_transforms['call_user_func'] = function ($function, $args, $state) {
  202. $function = array_shift($args)->value;
  203. return generateFunctionCall($function, $args, $state);
  204. };
  205. // TODO This might permit pass by reference, which the real call_user_func (sadly) does not. If so, we might have to tweak it to match behavior of PHP.
  206. CpsRuntime::$builtin_functions['call_user_func'] = function ($f, $args, $c, $x) {
  207. $f = array_shift($args);
  208. array_unshift($args, $x);
  209. array_unshift($args, $c);
  210. return call_user_func_array($f, $args);
  211. };
  212. CpsRuntime::$function_transforms['call_user_func_array'] = function ($function, $args, $state) {
  213. $function = array_shift($args)->value;
  214. $args = array_shift($args)->value;
  215. $stmts = array();
  216. $args = assignToTemp($args, $stmts);
  217. $stmts[] = new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('array_unshift'), array(
  218. $args, $state->generateExceptParameter()
  219. ));
  220. $stmts[] = new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('array_unshift'), array(
  221. $args, new PHPParser_Node_Expr_Variable(CONT_NAME)
  222. ));
  223. $stmts[] = new PHPParser_Node_Stmt_Return(
  224. new PHPParser_Node_Expr_FuncCall(new PHPParser_Node_Name('call_user_func_array'), array(
  225. $function, $args
  226. ))
  227. );
  228. return $stmts;
  229. };
  230. CpsRuntime::$builtin_functions['call_user_func_array'] = function ($f, $args, $c, $x) {
  231. $f = array_shift($args);
  232. $args = array_shift($args);
  233. array_unshift($args, $x);
  234. array_unshift($args, $c);
  235. return call_user_func_array($f, $args);
  236. };