PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/php/xp/compiler/emit/source/Emitter.class.php

https://github.com/thekid/compiler
PHP | 2586 lines | 2085 code | 108 blank | 393 comment | 109 complexity | d6964c528df701ffae665557872e23c3 MD5 | raw file

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

  1. <?php namespace xp\compiler\emit\source;
  2. use xp\compiler\types\CompiledType;
  3. use xp\compiler\types\TypeDeclaration;
  4. use xp\compiler\types\TypeInstance;
  5. use xp\compiler\types\TypeName;
  6. use xp\compiler\types\Types;
  7. use xp\compiler\types\Scope;
  8. use xp\compiler\types\CompilationUnitScope;
  9. use xp\compiler\types\TypeDeclarationScope;
  10. use xp\compiler\types\MethodScope;
  11. use xp\compiler\types\Method;
  12. use xp\compiler\types\Field;
  13. use xp\compiler\types\Constructor;
  14. use xp\compiler\types\Property;
  15. use xp\compiler\types\Operator;
  16. use xp\compiler\types\Indexer;
  17. use xp\compiler\types\Constant;
  18. use xp\compiler\types\Parameter;
  19. use xp\compiler\ast\ParseTree;
  20. use xp\compiler\ast\VariableNode;
  21. use xp\compiler\ast\TypeDeclarationNode;
  22. use xp\compiler\ast\Resolveable;
  23. use xp\compiler\ast\ArrayAccessNode;
  24. use xp\compiler\ast\StaticMemberAccessNode;
  25. use xp\compiler\ast\MethodCallNode;
  26. use xp\compiler\ast\MemberAccessNode;
  27. use xp\compiler\ast\StaticMethodCallNode;
  28. use xp\compiler\ast\FinallyNode;
  29. use xp\compiler\ast\CatchNode;
  30. use xp\compiler\ast\ThrowNode;
  31. use xp\compiler\ast\ClassNode;
  32. use xp\compiler\ast\AssignmentNode;
  33. use xp\compiler\ast\ArrayNode;
  34. use xp\compiler\ast\FieldNode;
  35. use xp\compiler\ast\ConstructorNode;
  36. use xp\compiler\ast\MethodNode;
  37. use xp\compiler\ast\ReturnNode;
  38. use xp\compiler\ast\StringNode;
  39. use xp\compiler\ast\EnumMemberNode;
  40. use xp\compiler\ast\IndexerNode;
  41. use xp\compiler\ast\StaticInitializerNode;
  42. use xp\compiler\ast\LocalsToMemberPromoter;
  43. use xp\compiler\emit\Buffer;
  44. use lang\reflect\Modifiers;
  45. use lang\Throwable;
  46. /**
  47. * Emits sourcecode using PHP sourcecode
  48. *
  49. * @test xp://net.xp_lang.tests.execution.source.AnnotationTest
  50. * @test xp://net.xp_lang.tests.execution.source.ArrayTest
  51. * @test xp://net.xp_lang.tests.execution.source.AssignmentTest
  52. * @test xp://net.xp_lang.tests.execution.source.AutoPropertiesTest
  53. * @test xp://net.xp_lang.tests.execution.source.CastingTest
  54. * @test xp://net.xp_lang.tests.execution.source.CatchTest
  55. * @test xp://net.xp_lang.tests.execution.source.ChainingTest
  56. * @test xp://net.xp_lang.tests.execution.source.ClassDeclarationTest
  57. * @test xp://net.xp_lang.tests.execution.source.ComparisonTest
  58. * @test xp://net.xp_lang.tests.execution.source.ConcatTest
  59. * @test xp://net.xp_lang.tests.execution.source.DefaultArgsTest
  60. * @test xp://net.xp_lang.tests.execution.source.EnumDeclarationTest
  61. * @test xp://net.xp_lang.tests.execution.source.ExecutionTest
  62. * @test xp://net.xp_lang.tests.execution.source.ExtensionMethodsTest
  63. * @test xp://net.xp_lang.tests.execution.source.Filter
  64. * @test xp://net.xp_lang.tests.execution.source.FinallyTest
  65. * @test xp://net.xp_lang.tests.execution.source.Functions
  66. * @test xp://net.xp_lang.tests.execution.source.InstanceCreationTest
  67. * @test xp://net.xp_lang.tests.execution.source.InterfaceDeclarationTest
  68. * @test xp://net.xp_lang.tests.execution.source.LambdaTest
  69. * @test xp://net.xp_lang.tests.execution.source.LoopExecutionTest
  70. * @test xp://net.xp_lang.tests.execution.source.MathTest
  71. * @test xp://net.xp_lang.tests.execution.source.MemberInitTest
  72. * @test xp://net.xp_lang.tests.execution.source.MethodOverloadingTest
  73. * @test xp://net.xp_lang.tests.execution.source.MultiCatchTest
  74. * @test xp://net.xp_lang.tests.execution.source.NativeClassUsageTest
  75. * @test xp://net.xp_lang.tests.execution.source.NavigationOperatorTest
  76. * @test xp://net.xp_lang.tests.execution.source.OperatorOverloadingTest
  77. * @test xp://net.xp_lang.tests.execution.source.OperatorTest
  78. * @test xp://net.xp_lang.tests.execution.source.PropertiesTest
  79. * @test xp://net.xp_lang.tests.execution.source.StaticImportTest
  80. * @test xp://net.xp_lang.tests.execution.source.StringBuffer
  81. * @test xp://net.xp_lang.tests.execution.source.TernaryOperatorTest
  82. * @test xp://net.xp_lang.tests.execution.source.VarArgsTest
  83. * @test xp://net.xp_lang.tests.execution.source.VariablesTest
  84. * @test xp://net.xp_lang.tests.execution.source.WithTest
  85. * @test xp://net.xp_lang.tests.compilation.TypeTest
  86. * @see xp://xp.compiler.ast.ParseTree
  87. */
  88. class Emitter extends \xp\compiler\emit\Emitter {
  89. protected
  90. $temp = array(null),
  91. $method = array(null),
  92. $finalizers = array(null),
  93. $metadata = array(null),
  94. $properties = array(null),
  95. $inits = array(null),
  96. $local = array(null),
  97. $types = array(null);
  98. /**
  99. * Returns a new temporary variable
  100. *
  101. * @return string
  102. */
  103. protected function tempVar() {
  104. return '$T'.($this->temp[0]++);
  105. }
  106. /**
  107. * Check whether a node is writeable - that is: can be the left-hand
  108. * side of an assignment
  109. *
  110. * @param xp.compiler.ast.Node node
  111. * @return bool
  112. */
  113. protected function isWriteable($node) {
  114. if ($node instanceof VariableNode || $node instanceof ArrayAccessNode) {
  115. return true;
  116. } else if ($node instanceof MemberAccessNode || $node instanceof StaticMemberAccessNode) {
  117. return true; // TODO: Check for private, protected
  118. }
  119. return false;
  120. }
  121. /**
  122. * Creates a generic component string for use in meta data
  123. *
  124. * @param xp.compiler.types.TypeName type
  125. * @return string
  126. */
  127. protected function genericComponentAsMetadata($type) {
  128. $s= '';
  129. foreach ($type->components as $component) {
  130. $s.= ', '.$component->name;
  131. }
  132. return substr($s, 2);
  133. }
  134. /**
  135. * Emit uses statements for a given list of types
  136. *
  137. * @param xp.compiler.emit.Buffer b
  138. * @param [:bool] types
  139. */
  140. protected function emitUses($b, array $types) {
  141. static $bootstrap= array(
  142. 'lang.Object' => true,
  143. 'lang.StackTraceElement' => true,
  144. 'lang.Throwable' => true,
  145. 'lang.Error' => true,
  146. 'lang.XPException' => true,
  147. 'lang.Type' => true,
  148. 'lang.Primitive' => true,
  149. 'lang.types.Character' => true,
  150. 'lang.types.Number' => true,
  151. 'lang.types.Byte' => true,
  152. 'lang.types.Bytes' => true,
  153. 'lang.types.String' => true,
  154. 'lang.types.Integer' => true,
  155. 'lang.types.Double' => true,
  156. 'lang.types.Boolean' => true,
  157. 'lang.types.ArrayListIterator' => true,
  158. 'lang.types.ArrayList' => true,
  159. 'lang.ArrayType' => true,
  160. 'lang.MapType' => true,
  161. 'lang.reflect.Routine' => true,
  162. 'lang.reflect.Parameter' => true,
  163. 'lang.reflect.TargetInvocationException' => true,
  164. 'lang.reflect.Method' => true,
  165. 'lang.reflect.Field' => true,
  166. 'lang.reflect.Constructor' => true,
  167. 'lang.reflect.Modifiers' => true,
  168. 'lang.reflect.Package' => true,
  169. 'lang.XPClass' => true,
  170. 'lang.NullPointerException' => true,
  171. 'lang.IllegalAccessException' => true,
  172. 'lang.IllegalArgumentException' => true,
  173. 'lang.IllegalStateException' => true,
  174. 'lang.FormatException' => true,
  175. 'lang.ClassNotFoundException' => true,
  176. 'lang.AbstractClassLoader' => true,
  177. 'lang.FileSystemClassLoader' => true,
  178. 'lang.DynamicClassLoader' => true,
  179. 'lang.archive.ArchiveClassLoader' => true,
  180. 'lang.ClassLoader' => true,
  181. );
  182. // Do not add uses() entries for:
  183. // * Types emitted inside the same sourcefile
  184. // * Native classes
  185. // * Bootstrap classes
  186. $this->cat && $this->cat->debug('uses(', $types, ')');
  187. $uses= array();
  188. foreach ($types as $type => $used) {
  189. if (isset($this->local[0][$type]) || 'php.' === substr($type, 0, 4) || isset($bootstrap[$type])) continue;
  190. // TODO: Find out why this would make a difference, $type should already be fully-qualified
  191. // @net.xp_lang.tests.execution.source.PropertiesOverloadingTest
  192. // @net.xp_lang.tests.integration.CircularDependencyTest
  193. try {
  194. $uses[]= $this->resolveType(new TypeName($type), false)->name();
  195. } catch (Throwable $e) {
  196. $this->error('0424', $e->toString());
  197. }
  198. }
  199. $uses && $b->insert('uses(\''.implode("', '", $uses).'\');', 0);
  200. }
  201. /**
  202. * Emit parameters
  203. *
  204. * @param xp.compiler.emit.Buffer b
  205. * @param xp.compiler.ast.Node[] params
  206. * @param bool brackets
  207. * @return int
  208. */
  209. protected function emitInvocationArguments($b, array $params, $brackets= true) {
  210. $brackets && $b->append('(');
  211. $s= sizeof($params)- 1;
  212. foreach ($params as $i => $param) {
  213. $this->emitOne($b, $param);
  214. $i < $s && $b->append(',');
  215. }
  216. $brackets && $b->append(')');
  217. return sizeof($params);
  218. }
  219. /**
  220. * Emit invocations
  221. *
  222. * @param xp.compiler.emit.Buffer b
  223. * @param xp.compiler.ast.InvocationNode inv
  224. */
  225. protected function emitInvocation($b, $inv) {
  226. if (!isset($this->scope[0]->statics[$inv->name])) {
  227. if (!($resolved= $this->scope[0]->resolveStatic($inv->name))) {
  228. $this->error('T501', 'Cannot resolve '.$inv->name.'()', $inv);
  229. return;
  230. }
  231. $this->scope[0]->statics[$inv->name]= $resolved; // Cache information
  232. }
  233. $ptr= $this->scope[0]->statics[$inv->name];
  234. // Static method call vs. function call
  235. if (true === $ptr) {
  236. $b->append($inv->name);
  237. $this->emitInvocationArguments($b, (array)$inv->arguments);
  238. $this->scope[0]->setType($inv, TypeName::$VAR);
  239. } else {
  240. $b->append($ptr->holder->literal().'::'.$ptr->name());
  241. $this->emitInvocationArguments($b, (array)$inv->arguments);
  242. $this->scope[0]->setType($inv, $ptr->returns);
  243. }
  244. }
  245. /**
  246. * Emit strings
  247. *
  248. * @param xp.compiler.emit.Buffer b
  249. * @param xp.compiler.ast.StringNode str
  250. */
  251. protected function emitString($b, $str) {
  252. $b->append("'");
  253. $b->append(strtr($str->resolve(), array(
  254. "'" => "\'",
  255. '\\' => '\\\\'
  256. )));
  257. $b->append("'");
  258. }
  259. /**
  260. * Emit an array (a sequence of elements with a zero-based index)
  261. *
  262. * @param xp.compiler.emit.Buffer b
  263. * @param xp.compiler.ast.ArrayNode arr
  264. */
  265. protected function emitArray($b, $arr) {
  266. $b->append('array(');
  267. foreach ((array)$arr->values as $value) {
  268. $this->emitOne($b, $value);
  269. $b->append(',');
  270. }
  271. $b->append(')');
  272. }
  273. /**
  274. * Emit a map (a key/value pair dictionary)
  275. *
  276. * @param xp.compiler.emit.Buffer b
  277. * @param xp.compiler.ast.MapNode map
  278. */
  279. protected function emitMap($b, $map) {
  280. $b->append('array(');
  281. foreach ((array)$map->elements as $pair) {
  282. $this->emitOne($b, $pair[0]);
  283. $b->append(' => ');
  284. $this->emitOne($b, $pair[1]);
  285. $b->append(',');
  286. }
  287. $b->append(')');
  288. }
  289. /**
  290. * Emit booleans
  291. *
  292. * @param xp.compiler.emit.Buffer b
  293. * @param xp.compiler.ast.BooleanNode const
  294. */
  295. protected function emitBoolean($b, $const) {
  296. $b->append($const->resolve() ? 'TRUE' : 'FALSE');
  297. }
  298. /**
  299. * Emit null
  300. *
  301. * @param xp.compiler.emit.Buffer b
  302. * @param xp.compiler.ast.NullNode const
  303. */
  304. protected function emitNull($b, $const) {
  305. $b->append('NULL');
  306. }
  307. /**
  308. * Emit constants
  309. *
  310. * @param xp.compiler.emit.Buffer b
  311. * @param xp.compiler.ast.ConstantNode const
  312. */
  313. protected function emitConstant($b, $const) {
  314. if ($constant= $this->scope[0]->resolveConstant($const->name)) {
  315. $b->append(var_export($constant->value, true));
  316. return;
  317. }
  318. $b->append($const->name);
  319. }
  320. /**
  321. * Emit \casts
  322. *
  323. * @param xp.compiler.emit.Buffer b
  324. * @param xp.compiler.ast.CastNode cast
  325. */
  326. protected function emitCast($b, $cast) {
  327. static $primitives= array(
  328. 'int' => '(int)',
  329. 'double' => '(double)',
  330. 'string' => '(string)',
  331. 'array' => '(array)',
  332. 'bool' => '(bool)',
  333. // Missing intentionally: object and unset \casts
  334. );
  335. if (!$cast->check) {
  336. $this->emitOne($b, $cast->expression);
  337. } else if ($cast->type->isPrimitive()) {
  338. $b->append($primitives[$cast->type->name]);
  339. $this->emitOne($b, $cast->expression);
  340. } else if ($cast->type->isArray() || $cast->type->isMap()) {
  341. $b->append('(array)');
  342. $this->emitOne($b, $cast->expression);
  343. } else {
  344. $b->append('cast(');
  345. $this->emitOne($b, $cast->expression);
  346. $b->append(', \'')->append($this->resolveType($cast->type)->name())->append('\')');
  347. }
  348. $this->scope[0]->setType($cast, $cast->type);
  349. }
  350. /**
  351. * Emit integers
  352. *
  353. * @param xp.compiler.emit.Buffer b
  354. * @param xp.compiler.ast.IntegerNode num
  355. */
  356. protected function emitInteger($b, $num) {
  357. $b->append($num->resolve());
  358. }
  359. /**
  360. * Emit decimals
  361. *
  362. * @param xp.compiler.emit.Buffer b
  363. * @param xp.compiler.ast.DecimalNode num
  364. */
  365. protected function emitDecimal($b, $num) {
  366. $res= $num->resolve();
  367. $b->append($res);
  368. // Prevent float(2) being dumped as "2" and thus an int literal
  369. strstr($res, '.') || $b->append('.0');
  370. }
  371. /**
  372. * Emit hex numbers
  373. *
  374. * @param xp.compiler.emit.Buffer b
  375. * @param xp.compiler.ast.HexNode num
  376. */
  377. protected function emitHex($b, $num) {
  378. $b->append($num->resolve());
  379. }
  380. /**
  381. * Emit octal numbers
  382. *
  383. * @param xp.compiler.emit.Buffer b
  384. * @param xp.compiler.ast.OctalNode num
  385. */
  386. protected function emitOctal($b, $num) {
  387. $b->append($num->resolve());
  388. }
  389. /**
  390. * Emit a variable
  391. *
  392. * @param xp.compiler.emit.Buffer b
  393. * @param xp.compiler.ast.VariableNode var
  394. */
  395. protected function emitVariable($b, $var) {
  396. $b->append('$'.$var->name);
  397. }
  398. /**
  399. * Emit a member access. Helper to emitChain()
  400. *
  401. * @param xp.compiler.emit.Buffer b
  402. * @param xp.compiler.ast.DynamicVariableReferenceNode access
  403. */
  404. public function emitDynamicMemberAccess($b, $access) {
  405. $this->emitOne($b, $call->target);
  406. $b->append('->{');
  407. $this->emitOne($b, $access->expression);
  408. $b->append('}');
  409. $this->scope[0]->setType($call, TypeName::$VAR);
  410. }
  411. /**
  412. * Emit static method call
  413. *
  414. * @param xp.compiler.emit.Buffer b
  415. * @param xp.compiler.ast.StaticMethodCallNode call
  416. */
  417. public function emitStaticMethodCall($b, $call) {
  418. $ptr= $this->resolveType($call->type);
  419. $b->append($ptr->literal().'::'.$call->name);
  420. $this->emitInvocationArguments($b, (array)$call->arguments);
  421. // Record type
  422. $this->scope[0]->setType($call, $ptr->hasMethod($call->name) ? $ptr->getMethod($call->name)->returns : TypeName::$VAR);
  423. }
  424. /**
  425. * Emit instance call
  426. *
  427. * @param xp.compiler.emit.Buffer b
  428. * @param xp.compiler.ast.InstanceCallNode call
  429. */
  430. public function emitInstanceCall($b, $call) {
  431. // Navigation operator
  432. if ($call->nav) {
  433. $var= $this->tempVar();
  434. $b->append('(null === ('.$var.'=');
  435. $this->emitOne($b, $call->target);
  436. $b->append(') ? null : call_user_func(')->append($var);
  437. if ($call->arguments) {
  438. $b->append(', ');
  439. $this->emitInvocationArguments($b, $call->arguments, false);
  440. }
  441. $b->append('))');
  442. } else {
  443. $b->append('call_user_func(');
  444. $this->emitOne($b, $call->target);
  445. if ($call->arguments) {
  446. $b->append(', ');
  447. $this->emitInvocationArguments($b, $call->arguments, false);
  448. }
  449. $b->append(')');
  450. }
  451. }
  452. /**
  453. * Emit method call
  454. *
  455. * @param xp.compiler.emit.Buffer b
  456. * @param xp.compiler.ast.MethodCallNode call
  457. */
  458. public function emitMethodCall($b, $call) {
  459. $mark= $b->mark();
  460. $this->emitOne($b, $call->target);
  461. // Check for extension methods
  462. $ptr= new TypeInstance($this->resolveType($this->scope[0]->typeOf($call->target), false));
  463. if (null !== ($ext= $this->scope[0]->getExtension($ptr, $call->name))) {
  464. $b->insert($ext->holder->literal().'::'.$call->name.'(', $mark);
  465. if ($call->arguments) {
  466. $b->append(', ');
  467. $this->emitInvocationArguments($b, $call->arguments, false);
  468. }
  469. $b->append(')');
  470. $this->scope[0]->setType($call, $ext->returns);
  471. return;
  472. }
  473. // Manually verify as we can then rely on call target type being available
  474. if (!$this->checks->verify($call, $this->scope[0], $this, true)) return;
  475. if ($call->nav) {
  476. $var= $this->tempVar();
  477. $b->insert('(NULL === ('.$var.'=', $mark);
  478. $b->append(') ? NULL : ')->append($var)->append('->');
  479. $b->append($call->name);
  480. $this->emitInvocationArguments($b, (array)$call->arguments);
  481. $b->append(')');
  482. } else {
  483. // Rewrite for unsupported syntax
  484. // - new Date().toString() to create(new Date()).toString()
  485. // - (<expr>).toString to create(<expr>).toString()
  486. if (
  487. !$call->target instanceof ArrayAccessNode &&
  488. !$call->target instanceof MethodCallNode &&
  489. !$call->target instanceof MemberAccessNode &&
  490. !$call->target instanceof VariableNode &&
  491. !$call->target instanceof StaticMemberAccessNode &&
  492. !$call->target instanceof StaticMethodCallNode
  493. ) {
  494. $b->insert('create(', $mark);
  495. $b->append(')');
  496. }
  497. $b->append('->'.$call->name);
  498. $this->emitInvocationArguments($b, (array)$call->arguments);
  499. }
  500. // Record type
  501. $this->scope[0]->setType($call, $ptr->hasMethod($call->name) ? $ptr->getMethod($call->name)->returns : TypeName::$VAR);
  502. }
  503. /**
  504. * Emit member access
  505. *
  506. * @param xp.compiler.emit.Buffer b
  507. * @param xp.compiler.ast.StaticMemberAccessNode access
  508. */
  509. public function emitStaticMemberAccess($b, $access) {
  510. $ptr= $this->resolveType($access->type);
  511. $b->append($ptr->literal().'::$'.$access->name);
  512. // Record type
  513. $this->scope[0]->setType($access, $ptr->hasField($access->name) ? $ptr->getField($access->name)->type : TypeName::$VAR);
  514. }
  515. /**
  516. * Emit member access
  517. *
  518. * @param xp.compiler.emit.Buffer b
  519. * @param xp.compiler.ast.MemberAccessNode access
  520. */
  521. public function emitMemberAccess($b, $access) {
  522. $mark= $b->mark();
  523. $this->emitOne($b, $access->target);
  524. $type= $this->scope[0]->typeOf($access->target);
  525. // Overload [...].length
  526. if ($type->isArray() && 'length' === $access->name) {
  527. $b->insert('sizeof(', $mark);
  528. $b->append(')');
  529. $this->scope[0]->setType($access, new TypeName('int'));
  530. return;
  531. }
  532. // Manually verify as we can then rely on call target type being available
  533. if (!$this->checks->verify($access, $this->scope[0], $this, true)) return;
  534. // Navigation operator
  535. if ($access->nav) {
  536. $var= $this->tempVar();
  537. $b->insert('(NULL === ('.$var.'=', $mark);
  538. $b->append(') ? NULL : ')->append($var)->append('->');
  539. $b->append($access->name);
  540. $b->append(')');
  541. } else {
  542. // Rewrite for unsupported syntax
  543. // - new Person().name to create(new Person()).name
  544. // - (<expr>).name to create(<expr>).name
  545. if (
  546. !$access->target instanceof ArrayAccessNode &&
  547. !$access->target instanceof MethodCallNode &&
  548. !$access->target instanceof MemberAccessNode &&
  549. !$access->target instanceof VariableNode &&
  550. !$access->target instanceof StaticMemberAccessNode &&
  551. !$access->target instanceof StaticMethodCallNode
  552. ) {
  553. $b->insert('create(', $mark);
  554. $b->append(')');
  555. }
  556. $b->append('->'.$access->name);
  557. }
  558. // Record type
  559. $ptr= new TypeInstance($this->resolveType($type));
  560. if ($ptr->hasField($access->name)) {
  561. $result= $ptr->getField($access->name)->type;
  562. } else if ($ptr->hasProperty($access->name)) {
  563. $result= $ptr->getProperty($access->name)->type;
  564. } else {
  565. $result= TypeName::$VAR;
  566. }
  567. $this->scope[0]->setType($access, $result);
  568. }
  569. /**
  570. * Emit array access
  571. *
  572. * @param xp.compiler.emit.Buffer b
  573. * @param xp.compiler.ast.ArrayAccessNode access
  574. */
  575. public function emitArrayAccess($b, $access) {
  576. $mark= $b->mark();
  577. $this->emitOne($b, $access->target);
  578. // Manually verify as we can then rely on call target type being available
  579. if (!$this->checks->verify($access, $this->scope[0], $this, true)) return;
  580. // Rewrite for unsupported syntax
  581. // - $a.getMethods()[2] to this($a.getMethods(), 2)
  582. // - T::asList()[2] to this(T::asList(), 2)
  583. // - new int[]{5, 6, 7}[2] to this(array(5, 6, 7), 2)
  584. if (
  585. !$access->target instanceof ArrayAccessNode &&
  586. !$access->target instanceof MemberAccessNode &&
  587. !$access->target instanceof VariableNode &&
  588. !$access->target instanceof StaticMemberAccessNode
  589. ) {
  590. $b->insert('this(', $mark);
  591. $b->append(',');
  592. $this->emitOne($b, $access->offset);
  593. $b->append(')');
  594. } else {
  595. $b->append('[');
  596. $access->offset && $this->emitOne($b, $access->offset);
  597. $b->append(']');
  598. }
  599. }
  600. /**
  601. * Emit constant access
  602. *
  603. * @param xp.compiler.emit.Buffer b
  604. * @param xp.compiler.ast.ConstantAccessNode access
  605. */
  606. public function emitConstantAccess($b, $access) {
  607. $ptr= $this->resolveType($access->type);
  608. $b->append($ptr->literal().'::'.$access->name);
  609. // Record type
  610. $this->scope[0]->setType($access, $ptr->hasConstant($access->name) ? $ptr->getConstant($access->name)->type : TypeName::$VAR);
  611. }
  612. /**
  613. * Emit class access
  614. *
  615. * @param xp.compiler.emit.Buffer b
  616. * @param xp.compiler.ast.ClassAccessNode access
  617. */
  618. public function emitClassAccess($b, $access) {
  619. $ptr= $this->resolveType($access->type);
  620. $b->append('XPClass::forName(\''.$ptr->name().'\')');
  621. // Record type
  622. $this->scope[0]->setType($access, new TypeName('lang.XPClass'));
  623. }
  624. /**
  625. * Emit a braced expression
  626. *
  627. * @param xp.compiler.emit.Buffer b
  628. * @param xp.compiler.ast.BracedExpressionNode const
  629. */
  630. protected function emitBracedExpression($b, $braced) {
  631. $b->append('(');
  632. $this->emitOne($b, $braced->expression);
  633. $b->append(')');
  634. }
  635. /**
  636. * Emit binary operation node
  637. *
  638. * @param xp.compiler.emit.Buffer b
  639. * @param xp.compiler.ast.BinaryOpNode bin
  640. */
  641. protected function emitBinaryOp($b, $bin) {
  642. static $ops= array(
  643. '~' => array(true, '.'),
  644. '-' => array(true, '-'),
  645. '+' => array(true, '+'),
  646. '*' => array(true, '*'),
  647. '/' => array(true, '/'),
  648. '%' => array(true, '%'),
  649. '|' => array(true, '|'),
  650. '&' => array(true, '&'),
  651. '^' => array(true, '^'),
  652. '&&' => array(true, '&&'),
  653. '||' => array(true, '||'),
  654. '>>' => array(true, '>>'),
  655. '<<' => array(true, '<<'),
  656. '**' => array(false, 'pow')
  657. );
  658. static $ovl= array(
  659. '~' => 'concat',
  660. '-' => 'minus',
  661. '+' => 'plus',
  662. '*' => 'times',
  663. '/' => 'div',
  664. '%' => 'mod',
  665. );
  666. $t= $this->scope[0]->typeOf($bin->lhs);
  667. if ($t->isClass()) {
  668. $ptr= $this->resolveType($t);
  669. if ($ptr->hasOperator($bin->op)) {
  670. $o= $ptr->getOperator($bin->op);
  671. $b->append($ptr->literal());
  672. $b->append('::operatorии')->append($ovl[$bin->op])->append('(');
  673. $this->emitOne($b, $bin->lhs);
  674. $b->append(',');
  675. $this->emitOne($b, $bin->rhs);
  676. $b->append(')');
  677. $this->scope[0]->setType($bin, $o->returns);
  678. return;
  679. }
  680. }
  681. $o= $ops[$bin->op];
  682. if ($o[0]) { // infix
  683. $this->emitOne($b, $bin->lhs);
  684. $b->append($o[1]);
  685. $this->emitOne($b, $bin->rhs);
  686. } else {
  687. $b->append($o[1])->append('(');
  688. $this->emitOne($b, $bin->lhs);
  689. $b->append(',');
  690. $this->emitOne($b, $bin->rhs);
  691. $b->append(')');
  692. }
  693. }
  694. /**
  695. * Emit unary operation node
  696. *
  697. * @param xp.compiler.emit.Buffer b
  698. * @param xp.compiler.ast.UnaryOpNode un
  699. */
  700. protected function emitUnaryOp($b, $un) {
  701. static $ops= array(
  702. '++' => '++',
  703. '--' => '--',
  704. );
  705. if ('!' === $un->op) { // FIXME: Use NotNode for this?
  706. $b->append('!');
  707. $this->emitOne($b, $un->expression);
  708. return;
  709. } else if ('-' === $un->op) {
  710. $b->append('-');
  711. $this->emitOne($b, $un->expression);
  712. return;
  713. } else if ('~' === $un->op) {
  714. $b->append('~');
  715. $this->emitOne($b, $un->expression);
  716. return;
  717. } else if (!$this->isWriteable($un->expression)) {
  718. $this->error('U400', 'Cannot perform unary '.$un->op.' on '.$un->expression->getClassName(), $un);
  719. return;
  720. }
  721. if ($un->postfix) {
  722. $this->emitOne($b, $un->expression);
  723. $b->append($ops[$un->op]);
  724. } else {
  725. $b->append($ops[$un->op]);
  726. $this->emitOne($b, $un->expression);
  727. }
  728. }
  729. /**
  730. * Emit ternary operator node
  731. *
  732. * Note: The following two are equivalent:
  733. * <code>
  734. * $a= $b ?: $c;
  735. * $a= $b ? $b : $c;
  736. * </code>
  737. *
  738. * @param xp.compiler.emit.Buffer b
  739. * @param xp.compiler.ast.TernaryNode ternary
  740. */
  741. protected function emitTernary($b, $ternary) {
  742. $this->emitOne($b, $ternary->condition);
  743. $b->append('?');
  744. $this->emitOne($b, $ternary->expression ?: $ternary->condition);
  745. $b->append(':');
  746. $this->emitOne($b, $ternary->conditional);
  747. }
  748. /**
  749. * Emit comparison node
  750. *
  751. * @param xp.compiler.emit.Buffer b
  752. * @param xp.compiler.ast.ComparisonNode cmp
  753. */
  754. protected function emitComparison($b, $cmp) {
  755. static $ops= array(
  756. '==' => '==',
  757. '===' => '===',
  758. '!=' => '!=',
  759. '!==' => '!==',
  760. '<=' => '<=',
  761. '<' => '<',
  762. '>=' => '>=',
  763. '>' => '>',
  764. );
  765. $this->emitOne($b, $cmp->lhs);
  766. $b->append(' '.$ops[$cmp->op].' ');
  767. $this->emitOne($b, $cmp->rhs);
  768. }
  769. /**
  770. * Emit continue statement
  771. *
  772. * @param xp.compiler.emit.Buffer b
  773. * @param xp.compiler.ast.ContinueNode statement
  774. */
  775. protected function emitContinue($b, $statement) {
  776. $b->append('continue');
  777. }
  778. /**
  779. * Emit break statement
  780. *
  781. * @param xp.compiler.emit.Buffer b
  782. * @param xp.compiler.ast.BreakNode statement
  783. */
  784. protected function emitBreak($b, $statement) {
  785. $b->append('break');
  786. }
  787. /**
  788. * Emit noop
  789. *
  790. * @param xp.compiler.emit.Buffer b
  791. * @param xp.compiler.ast.NoopNode statement
  792. */
  793. protected function emitNoop($b, $statement) {
  794. // NOOP
  795. }
  796. /**
  797. * Emit with statement
  798. *
  799. * @param xp.compiler.emit.Buffer b
  800. * @param xp.compiler.ast.WithNode with
  801. */
  802. protected function emitWith($b, $with) {
  803. $this->emitAll($b, $with->assignments);
  804. $this->emitAll($b, $with->statements);
  805. }
  806. /**
  807. * Emit statements
  808. *
  809. * @param xp.compiler.emit.Buffer b
  810. * @param xp.compiler.ast.StatementsNode statements
  811. */
  812. protected function emitStatements($b, $statements) {
  813. $this->emitAll($b, (array)$statements->list);
  814. }
  815. /**
  816. * Emit foreach loop
  817. *
  818. * @param xp.compiler.emit.Buffer b
  819. * @param xp.compiler.ast.ForeachNode loop
  820. */
  821. protected function emitForeach($b, $loop) {
  822. $b->append('foreach (');
  823. $this->emitOne($b, $loop->expression);
  824. // Assign key and value types by checking for loop expression's type
  825. // * var type may be enumerable
  826. // * any other type may define an overlad
  827. $t= $this->scope[0]->typeOf($loop->expression);
  828. if ($t->isVariable()) {
  829. $this->warn('T203', 'Enumeration of (var)'.$loop->expression->hashCode().' verification deferred until runtime', $loop);
  830. $vt= TypeName::$VAR;
  831. $kt= new TypeName('int');
  832. } else {
  833. $ptr= $this->resolveType($t);
  834. if (!$ptr->isEnumerable()) {
  835. $this->warn('T300', 'Type '.$ptr->name().' is not enumerable in loop expression '.$loop->expression->getClassName().'['.$loop->expression->hashCode().']', $loop);
  836. $vt= TypeName::$VAR;
  837. $kt= new TypeName('int');
  838. } else {
  839. $enum= $ptr->getEnumerator();
  840. $vt= $enum->value;
  841. $kt= $enum->key;
  842. }
  843. }
  844. $b->append(' as ');
  845. if (isset($loop->assignment['key'])) {
  846. $b->append('$'.$loop->assignment['key'].' => ');
  847. $this->scope[0]->setType(new VariableNode($loop->assignment['key']), $kt);
  848. }
  849. $b->append('$'.$loop->assignment['value'].') {');
  850. $this->scope[0]->setType(new VariableNode($loop->assignment['value']), $vt);
  851. $this->emitAll($b, (array)$loop->statements);
  852. $b->append('}');
  853. }
  854. /**
  855. * Emit do ... while loop
  856. *
  857. * @param xp.compiler.emit.Buffer b
  858. * @param xp.compiler.ast.DoNode loop
  859. */
  860. protected function emitDo($b, $loop) {
  861. $b->append('do {');
  862. $this->emitAll($b, (array)$loop->statements);
  863. $b->append('} while (');
  864. $this->emitOne($b, $loop->expression);
  865. $b->append(');');
  866. }
  867. /**
  868. * Emit while loop
  869. *
  870. * @param xp.compiler.emit.Buffer b
  871. * @param xp.compiler.ast.WhileNode loop
  872. */
  873. protected function emitWhile($b, $loop) {
  874. $b->append('while (');
  875. $this->emitOne($b, $loop->expression);
  876. $b->append(') {');
  877. $this->emitAll($b, (array)$loop->statements);
  878. $b->append('}');
  879. }
  880. /**
  881. * Emit components inside a for() statement
  882. *
  883. * @param xp.compiler.emit.Buffer b
  884. * @return xp.compiler.ast.Node[] nodes
  885. */
  886. protected function emitForComponent($b, array $nodes) {
  887. $s= sizeof($nodes)- 1;
  888. foreach ($nodes as $i => $node) {
  889. $this->emitOne($b, $node);
  890. $i < $s && $b->append(', ');
  891. }
  892. }
  893. /**
  894. * Emit for loop
  895. *
  896. * @param xp.compiler.emit.Buffer b
  897. * @param xp.compiler.ast.ForNode loop
  898. */
  899. protected function emitFor($b, $loop) {
  900. $b->append('for (');
  901. $this->emitForComponent($b, (array)$loop->initialization);
  902. $b->append(';');
  903. $this->emitForComponent($b, (array)$loop->condition);
  904. $b->append(';');
  905. $this->emitForComponent($b, (array)$loop->loop);
  906. $b->append(') {');
  907. $this->emitAll($b, (array)$loop->statements);
  908. $b->append('}');
  909. }
  910. /**
  911. * Emit if statement
  912. *
  913. * @param xp.compiler.emit.Buffer b
  914. * @param xp.compiler.ast.IfNode if
  915. */
  916. protected function emitIf($b, $if) {
  917. $b->append('if (');
  918. $this->emitOne($b, $if->condition);
  919. $b->append(') {');
  920. $this->emitAll($b, (array)$if->statements);
  921. $b->append('}');
  922. if ($if->otherwise) {
  923. $b->append('else {');
  924. $this->emitAll($b, (array)$if->otherwise->statements);
  925. $b->append('}');
  926. }
  927. }
  928. /**
  929. * Emit a switch case
  930. *
  931. * @param xp.compiler.emit.Buffer b
  932. * @param xp.compiler.ast.CaseNode case
  933. */
  934. protected function emitCase($b, $case) {
  935. $b->append('case ');
  936. $this->emitOne($b, $case->expression);
  937. $b->append(': ');
  938. $this->emitAll($b, (array)$case->statements);
  939. }
  940. /**
  941. * Emit the switch default case
  942. *
  943. * @param xp.compiler.emit.Buffer b
  944. * @param xp.compiler.ast.DefaultNode default
  945. */
  946. protected function emitDefault($b, $default) {
  947. $b->append('default: ');
  948. $this->emitAll($b, (array)$default->statements);
  949. }
  950. /**
  951. * Emit switch statement
  952. *
  953. * @param xp.compiler.emit.Buffer b
  954. * @param xp.compiler.ast.SwitchNode switch
  955. */
  956. protected function emitSwitch($b, $switch) {
  957. $b->append('switch (');
  958. $this->emitOne($b, $switch->expression);
  959. $b->append(') {');
  960. $this->emitAll($b, (array)$switch->cases);
  961. $b->append('}');
  962. }
  963. /**
  964. * Emit a try / catch block
  965. *
  966. * Simple form:
  967. * <code>
  968. * try {
  969. * // [...statements...]
  970. * } catch (lang.Throwable $e) {
  971. * // [...error handling...]
  972. * }
  973. * </code>
  974. *
  975. * Multiple catches:
  976. * <code>
  977. * try {
  978. * // [...statements...]
  979. * } catch (lang.IllegalArgumentException $e) {
  980. * // [...error handling for IAE...]
  981. * } catch (lang.FormatException $e) {
  982. * // [...error handling for FE...]
  983. * }
  984. * </code>
  985. *
  986. * Try/finally without catch:
  987. * <code>
  988. * try {
  989. * // [...statements...]
  990. * } finally {
  991. * // [...finalizations...]
  992. * }
  993. * </code>
  994. *
  995. * Try/finally with catch:
  996. * <code>
  997. * try {
  998. * // [...statements...]
  999. * } catch (lang.Throwable $e) {
  1000. * // [...error handling...]
  1001. * } finally {
  1002. * // [...finalizations...]
  1003. * }
  1004. * </code>
  1005. *
  1006. * @param xp.compiler.emit.Buffer b
  1007. * @param xp.compiler.ast.TryNode try
  1008. */
  1009. protected function emitTry($b, $try) {
  1010. static $mangled= 'ииe';
  1011. // Check whether a finalization handler is available. If so, because
  1012. // the underlying runtime does not support this, add statements after
  1013. // the try block and to all catch blocks
  1014. $numHandlers= sizeof($try->handling);
  1015. if ($try->handling[$numHandlers- 1] instanceof FinallyNode) {
  1016. array_unshift($this->finalizers, array_pop($try->handling));
  1017. $numHandlers--;
  1018. } else {
  1019. array_unshift($this->finalizers, null);
  1020. }
  1021. // If no handlers are left, create a simple catch-all-and-rethrow
  1022. // handler
  1023. if (0 == $numHandlers) {
  1024. $rethrow= new ThrowNode(array('expression' => new VariableNode($mangled)));
  1025. $first= new CatchNode(array(
  1026. 'type' => new TypeName('lang.Throwable'),
  1027. 'variable' => $mangled,
  1028. 'statements' => $this->finalizers[0] ? array($this->finalizers[0], $rethrow) : array($rethrow)
  1029. ));
  1030. } else {
  1031. $first= $try->handling[0];
  1032. $this->scope[0]->setType(new VariableNode($first->variable), $first->type);
  1033. }
  1034. $b->append('try {'); {
  1035. $this->emitAll($b, (array)$try->statements);
  1036. $this->finalizers[0] && $this->emitOne($b, $this->finalizers[0]);
  1037. }
  1038. // First catch.
  1039. $b->append('} catch('.$this->resolveType($first->type)->literal().' $'.$first->variable.') {'); {
  1040. $this->scope[0]->setType(new VariableNode($first->variable), $first->type);
  1041. $this->emitAll($b, (array)$first->statements);
  1042. $this->finalizers[0] && $this->emitOne($b, $this->finalizers[0]);
  1043. }
  1044. // Additional catches
  1045. for ($i= 1; $i < $numHandlers; $i++) {
  1046. $b->append('} catch('.$this->resolveType($try->handling[$i]->type)->literal().' $'.$try->handling[$i]->variable.') {'); {
  1047. $this->scope[0]->setType(new VariableNode($try->handling[$i]->variable), $try->handling[$i]->type);
  1048. $this->emitAll($b, (array)$try->handling[$i]->statements);
  1049. $this->finalizers[0] && $this->emitOne($b, $this->finalizers[0]);
  1050. }
  1051. }
  1052. $b->append('}');
  1053. array_shift($this->finalizers);
  1054. }
  1055. /**
  1056. * Emit an automatic resource management (ARM) block
  1057. *
  1058. * @param xp.compiler.emit.Buffer b
  1059. * @param xp.compiler.ast.ArmNode arm
  1060. */
  1061. protected function emitArm($b, $arm) {
  1062. static $mangled= 'ииe';
  1063. static $ignored= 'ииi';
  1064. $this->emitAll($b, $arm->initializations);
  1065. // Manually verify as we can then rely on call target type being available
  1066. if (!$this->checks->verify($arm, $this->scope[0], $this, true)) return;
  1067. $b->append('$'.$mangled.'= NULL; try {');
  1068. $this->emitAll($b, (array)$arm->statements);
  1069. $b->append('} catch (Exception $'.$mangled.') {}');
  1070. foreach ($arm->variables as $v) {
  1071. $b->append('try { $')->append($v->name)->append('->close(); } catch (Exception $'.$ignored.') {}');
  1072. }
  1073. $b->append('if ($'.$mangled.') throw $'.$mangled.';');
  1074. }
  1075. /**
  1076. * Emit a throw node
  1077. *
  1078. * @param xp.compiler.emit.Buffer b
  1079. * @param xp.compiler.ast.ThrowNode throw
  1080. */
  1081. protected function emitThrow($b, $throw) {
  1082. $b->append('throw ');
  1083. $this->emitOne($b, $throw->expression);
  1084. }
  1085. /**
  1086. * Emit a finally node
  1087. *
  1088. * @param xp.compiler.emit.Buffer b
  1089. * @param xp.compiler.ast.FinallyNode finally
  1090. */
  1091. protected function emitFinally($b, $finally) {
  1092. $this->emitAll($b, (array)$finally->statements);
  1093. }
  1094. /**
  1095. * Emit a dynamic instance creation node
  1096. *
  1097. * @param xp.compiler.emit.Buffer b
  1098. * @param xp.compiler.ast.DynamicInstanceCreationNode new
  1099. */
  1100. protected function emitDynamicInstanceCreation($b, $new) {
  1101. $b->append('new ')->append('$')->append($new->variable);
  1102. $this->emitInvocationArguments($b, (array)$new->parameters);
  1103. $this->scope[0]->setType($new, new TypeName('lang.Object'));
  1104. }
  1105. /**
  1106. * Emit an instance creation node
  1107. *
  1108. * @param xp.compiler.emit.Buffer b
  1109. * @param xp.compiler.ast.InstanceCreationNode new
  1110. */
  1111. protected function emitInstanceCreation($b, $new) {
  1112. // Anonymous instance creation:
  1113. //
  1114. // - Create unique classname
  1115. // - Extend parent class if type is a class
  1116. // - Implement type and extend lang.Object if it's an interface
  1117. //
  1118. // Do not register type name from new(), it will be added by
  1119. // emitClass() during declaration emittance.
  1120. $generic= null;
  1121. if (isset($new->body)) {
  1122. $parent= $this->resolveType($new->type, false);
  1123. if (Types::INTERFACE_KIND === $parent->kind()) {
  1124. $p= array('parent' => new TypeName('lang.Object'), 'implements' => array($new->type));
  1125. // If the interface we implement is generic, we need to
  1126. // make the generated class a generic instance.
  1127. if ($new->type->components) {
  1128. $components= array();
  1129. foreach ($new->type->components as $component) {
  1130. $components[]= $this->resolveType($component, false)->name();
  1131. }
  1132. $generic= array($parent->name(), null, $components);
  1133. }
  1134. } else if (Types::ENUM_KIND === $parent->kind()) {
  1135. $this->error('C405', 'Cannot create anonymous enums', $new);
  1136. return;
  1137. } else {
  1138. $p= array('parent' => $new->type, 'implements' => null);
  1139. }
  1140. $unique= new TypeName(strtr($parent->literal(), '\\', 'д').'ии'.strtr(uniqid(null, true), '.', 'и'));
  1141. $decl= new ClassNode(0, null, $unique, $p['parent'], $p['implements'], $new->body);
  1142. $decl->synthetic= true;
  1143. $generic && $decl->generic= $generic;
  1144. $ptr= new TypeDeclaration(new ParseTree(null, array(), $decl), $parent);
  1145. $this->scope[0]->declarations[]= $decl;
  1146. $this->scope[0]->setType($new, $unique);
  1147. } else {
  1148. $ptr= $this->resolveType($new->type);
  1149. $this->scope[0]->setType($new, $new->type);
  1150. }
  1151. // If generic instance is created, use the create(spec, args*)
  1152. // core functionality. If this a compiled generic type we may
  1153. // do quite a bit better - but how do we detect this?
  1154. if ($new->type->components && !$generic) {
  1155. $b->append('create(\'new '.$ptr->name().'<');
  1156. $s= sizeof($new->type->components)- 1;
  1157. foreach ($new->type->components as $i => $component) {
  1158. $b->append($this->resolveType($component)->name());
  1159. $i < $s && $b->append(',');
  1160. }
  1161. $b->append('>\'');
  1162. if ($new->parameters) {
  1163. $b->append(',');
  1164. $this->emitInvocationArguments($b, (array)$new->parameters, false);
  1165. }
  1166. $b->append(')');
  1167. } else {
  1168. $b->append('new '.$ptr->literal());
  1169. $this->emitInvocationArguments($b, (array)$new->parameters);
  1170. }
  1171. }
  1172. /**
  1173. * Emit an assignment
  1174. *
  1175. * @param xp.compiler.emit.Buffer b
  1176. * @param xp.compiler.ast.AssignmentNode assign
  1177. */
  1178. protected function emitAssignment($b, $assign) {
  1179. static $ops= array(
  1180. '=' => '=',
  1181. '~=' => '.=',
  1182. '-=' => '-=',
  1183. '+=' => '+=',
  1184. '*=' => '*=',
  1185. '/=' => '/=',
  1186. '%=' => '%=',
  1187. '|=' => '|=',
  1188. '^=' => '^=',
  1189. '&=' => '&=',
  1190. '<<=' => '<<=',
  1191. '>>=' => '>>=',
  1192. );
  1193. static $ovl= array(
  1194. '~=' => 'concat',
  1195. '-=' => 'minus',
  1196. '+=' => 'plus',
  1197. '*=' => 'times',
  1198. '/=' => 'div',
  1199. '%=' => 'mod',
  1200. );
  1201. $t= $this->scope[0]->typeOf($assign->variable);
  1202. if ($t->isClass()) {
  1203. $ptr= $this->resolveType($t);
  1204. if ($ptr->hasOperator($assign->op{0})) {
  1205. $o= $ptr->getOperator($assign->op{0});
  1206. $this->emitOne($b, $assign->variable);
  1207. $b->append('=');
  1208. $b->append($ptr->literal());
  1209. $b->append('::operatorии')->append($ovl[$assign->op])->append('(');
  1210. $this->emitOne($b, $assign->variable);
  1211. $b->append(',');
  1212. $this->emitOne($b, $assign->expression);
  1213. $b->append(')');
  1214. $this->scope[0]->setType($assign, $o->returns);
  1215. return;
  1216. }
  1217. }
  1218. // First set type to void, emit assignment, then overwrite type with
  1219. // right-hand-side's type. This is done in order to guard for checks
  1220. // on uninitialized variables, which is OK during assignment.
  1221. $this->scope[0]->setType($assign->variable, TypeName::$VOID);
  1222. $this->emitOne($b, $assign->variable);
  1223. $b->append($ops[$assign->op]);
  1224. $this->emitOne($b, $assign->expression);
  1225. $this->scope[0]->setType($assign->variable, $this->scope[0]->typeOf($assign->expression));
  1226. }
  1227. /**
  1228. * Emit an operator
  1229. *
  1230. * @param xp.compiler.emit.Buffer b
  1231. * @param xp.compiler.ast.OperatorNode method
  1232. */
  1233. protected function emitOperator($b, $operator) {
  1234. static $ovl= array(
  1235. '~' => 'concat',
  1236. '-' => 'minus',
  1237. '+' => 'plus',
  1238. '*' => 'times',
  1239. '/' => 'div',
  1240. '%' => 'mod',
  1241. );
  1242. $name= 'operatorии'.$ovl[$operator->symbol];
  1243. $this->enter(new MethodScope($name));
  1244. $return= $this->resolveType($operator->returns);
  1245. $this->metadata[0][1][$name]= array(
  1246. DETAIL_ARGUMENTS => array(),
  1247. DETAIL_RETURNS => $return->name(),
  1248. DETAIL_THROWS => array(),
  1249. DETAIL_COMMENT => $operator->comment
  1250. ? trim(preg_replace('/\n\s+\* ?/', "\n", "\n ".substr($operator->comment, 4, strpos($operator->comment, '* @')- 2)))
  1251. : null
  1252. ,
  1253. DETAIL_ANNOTATIONS => array(),
  1254. DETAIL_TARGET_ANNO => array()
  1255. );
  1256. array_unshift($this->method, $name);
  1257. $this->emitAnnotations($this->metadata[0][1][$name], (array)$operator->annotations);
  1258. $b->append('public static function ')->append($name);
  1259. $signature= $this->emitParameters($b, (array)$operator->parameters, '{');
  1260. $this->emitAll($b, (array)$operator->body);
  1261. $b->append('}');
  1262. array_shift($this->method);
  1263. $this->leave();
  1264. // Register type information
  1265. $o= new Operator();
  1266. $o->symbol= $operator->symbol;
  1267. $o->returns= new TypeName($return->name());
  1268. $o->parameters= $signature;
  1269. $o->modifiers= $operator->modifiers;
  1270. $this->types[0]->addOperator($o);
  1271. }
  1272. /**
  1273. * Emit method parameters
  1274. *
  1275. * @param xp.compiler.emit.Buffer b
  1276. * @param array<string, *>[] parameters
  1277. * @param string delim
  1278. * @return xp.compiler.TypeName[] the signature
  1279. */
  1280. protected function emitParameters($b, array $parameters, $delim) {
  1281. $signature= array();
  1282. $b->append('(');
  1283. $s= sizeof($parameters)- 1;
  1284. $defer= array();
  1285. $usesGenerics= false;
  1286. $genericParams= '';
  1287. foreach ($parameters as $i => $param) {
  1288. if (isset($param['assign'])) {
  1289. if (null === ($field= $this->resolveType(new TypeName('self'))->getField($param['assign']))) {
  1290. $this->error('F404', 'Method assignment parameter $this.'.$param['assign'].' references non-existant field');
  1291. $t= TypeName::$VAR;
  1292. } else {
  1293. $t= $field->type;
  1294. }
  1295. $ptr= $this->resolveType($t);
  1296. $param['name']= $param['assign'];
  1297. $defer[]= '$this->'.$param['assign'].'= $'.$param['assign'].';';
  1298. } else if (!$param['type']) {
  1299. $t= TypeName::$VAR;
  1300. $ptr= new TypeReference($t);
  1301. } else {
  1302. if (!$usesGenerics && $this->scope[0]->declarations[0]->name->isPlaceHolder($param['type'])) $usesGenerics= true;
  1303. $t= $param['type'];
  1304. $ptr= $this->resolveType($t);
  1305. if (!$param['check'] || isset($param['vararg'])) {
  1306. // No runtime type checks
  1307. } else if ($t->isArray() || $t->isMap()) {
  1308. $b->append('array ');
  1309. } else if ($t->isClass() && !$this->scope[0]->declarations[0]->name->isPlaceHolder($t)) {
  1310. $b->append($ptr->literal())->append(' ');
  1311. } else if ('{' === $delim) {
  1312. $defer[]= create(new Buffer('', $b->line))
  1313. ->append('if (NULL !== $')->append($param['name'])->append(' && !is("'.$t->name.'", $')
  1314. ->append($param['name'])
  1315. ->append(')) throw new IllegalArgumentException("Argument ')
  1316. ->append($i + 1)
  1317. ->append(' passed to ".__METHOD__." must be of ')
  1318. ->append($t->name)
  1319. ->append(', ".xp::typeOf($')
  1320. ->append($param['name'])
  1321. ->append(')." given");')
  1322. ;
  1323. } else {
  1324. // No checks in interfaces
  1325. }
  1326. }
  1327. $signature[]= new Parameter($param['name'], new TypeName($ptr->name()), isset($param['default']) ? $param['default'] : null);
  1328. $genericParams.= ', '.$t->compoundName();
  1329. $this->metadata[0][1][$this->method[0]][DETAIL_ARGUMENTS][$i]= $ptr->name();
  1330. if (isset($param['vararg'])) {
  1331. $genericParams.= '...';
  1332. if ($i > 0) {
  1333. $defer[]= '$'.$param['name'].'= array_slice(func_get_args(), '.$i.');';
  1334. } else {
  1335. $defer[]= '$'.$param['name'].'= func_get_args();';
  1336. }
  1337. $this->scope[0]->setType(new VariableNode($param['name']), new TypeName($t->name.'[]'));
  1338. break;
  1339. }
  1340. $b->append('$'.$param['name']);
  1341. if (isset($param['default'])) {
  1342. $b->append('= ');
  1343. $resolveable= false;
  1344. if ($param['default'] instanceof Resolveable) {
  1345. try {
  1346. $init= $param['default']->resolve();
  1347. $b->append(var_export($init, true));
  1348. $resolveable= true;
  1349. } catch (\lang\IllegalStateException $e) {
  1350. }
  1351. }
  1352. if (!$resolveable) {
  1353. $b->append('NULL');
  1354. $init= new Buffer('', $b->line);
  1355. $init->append('if (func_num_args() < ')->append($i + 1)->append(') { ');
  1356. $init->append('$')->append($param['name'])->append('= ');
  1357. $this->emitOne($init, $param['default']);
  1358. $init->append('; }');
  1359. $defer[]= $init;
  1360. }
  1361. }
  1362. $i < $s && !isset($parameters[$i+ 1]['vararg']) && $b->append(',');
  1363. $this->scope[0]->setType(new VariableNode($param['name']), $t);
  1364. }
  1365. $b->append(')');
  1366. $b->append($delim);
  1367. foreach ($defer as $src) {
  1368. $b->append($src);
  1369. }
  1370. if ($usesGenerics) {
  1371. $this->metadata[0][1][$this->method[0]][DETAIL_ANNOTATIONS]['generic']['params']= substr($genericParams, 2);
  1372. }
  1373. return $signature;
  1374. }
  1375. /**
  1376. * Emit annotations
  1377. *
  1378. * @param &var meta
  1379. * @param xp.compiler.ast.AnnotationNode[] annotations
  1380. */
  1381. protected function emitAnnotations(&$meta, $annotations) {
  1382. foreach ($annotations as $annotation) {
  1383. $this->emitAnnotation($meta, $annotation);
  1384. }
  1385. }
  1386. /**
  1387. * Emit annotation
  1388. *
  1389. * @param &var meta
  1390. * @param xp.compiler.ast.AnnotationNode lambda
  1391. */
  1392. protected function emitAnnotation(&$meta, $annotation) {
  1393. $params= array();
  1394. foreach ((array)$annotation->parameters as $name => $value) {
  1395. if ($value instanceof ClassAccessNode) { // class literal
  1396. $params[$name]= $this->resolveType($value->class)->name();
  1397. } else if ($value instanceof Resolveable) {
  1398. $params[$name]= $value->resolve();
  1399. } else if ($value instanceof ArrayNode) {
  1400. $params[$name]= array();
  1401. foreach ($value->values as $element) {
  1402. $element instanceof Resolveable && $params[$name][]= $element->resolve();
  1403. }
  1404. }
  1405. }
  1406. // Sort out where annotations should go
  1407. if (isset($annotation->target)) {
  1408. $ptr= &$meta[DETAIL_TARGET_ANNO][$annotation->target];
  1409. } else {
  1410. $ptr= &$meta[DETAIL_ANNOTATIONS];
  1411. }
  1412. // Set annotation value
  1413. if (!$annotation->parameters) {
  1414. $ptr[$annotation->type]= null;
  1415. } else if (isset($annotation->parameters['default'])) {
  1416. $ptr[$annotation->type]= $params['default'];
  1417. } else {
  1418. $ptr[$annotation->type]= $params;
  1419. }
  1420. }
  1421. /**
  1422. * Emit a lambda
  1423. *
  1424. * @param xp.compiler.emit.Buffer b
  1425. * @param xp.compiler.ast.LambdaNode lambda
  1426. * @see http://cr.openjdk.java.net/~mcimadamore/lambda_trans.pdf
  1427. */
  1428. protected function emitLambda($b, $lambda) {
  1429. $unique= new TypeName('Lambdaии'.strtr(uniqid('', true), '.', 'и'));
  1430. // Visit all statements, promoting local variable used within tp members
  1431. $promoter= new LocalsToMemberPromoter();
  1432. $parameters= $replaced= array();
  1433. foreach ($lambda->parameters as $parameter) {
  1434. $parameters[]= array('name' => $parameter->name, 'type' => TypeName::$VAR);
  1435. $promoter->exclude($parameter->name);
  1436. }
  1437. $promoted= $promoter->promote($lambda);
  1438. // Generate constructor
  1439. $cparameters= $cstmt= $fields= array();
  1440. foreach ($promoted['replaced'] as $name => $member) {
  1441. $cparameters[]= array('name' => substr($name, 1), 'type' => TypeName::$VAR);
  1442. $cstmt[]= new AssignmentNode(array(
  1443. 'variable' => $member,
  1444. 'expression' => new VariableNode(substr($name, 1)),
  1445. 'op' => '='
  1446. ));
  1447. $fields[]= new FieldNode(array(
  1448. 'name' => substr($name, 1),
  1449. 'type' => TypeName::$VAR)
  1450. );
  1451. }
  1452. // Generate an anonymous lambda class
  1453. $decl= new ClassNode(0, null, $unique, null, null, array_merge($fields, array(
  1454. new ConstructorNode(array(
  1455. 'parameters' => $cparameters,
  1456. 'body' => $cstmt
  1457. )),
  1458. new MethodNode(array(
  1459. 'name' => 'invoke',
  1460. 'parameters' => $parameters,
  1461. 'body' => $promoted['node']->statements,
  1462. 'returns' => TypeName::$VAR
  1463. ))
  1464. )));
  1465. $decl->synthetic= true;
  1466. $this->scope[0]->declarations[]= $decl;
  1467. // Finally emit array(new [UNIQUE]([CAPTURE]), "method")
  1468. $b->append('array(new '.$unique->name.'('.implode(',', array_keys($promoted['replaced'])).'), \'invoke\')');
  1469. }
  1470. /**
  1471. * Emit a method
  1472. *
  1473. * @param xp.compiler.emit.Buffer b
  1474. * @param xp.comp…

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