PageRenderTime 53ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  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.compiler.ast.MethodNode method
  1475. */
  1476. protected function emitMethod($b, $method) {
  1477. if ($method->extension) {
  1478. $this->scope[0]->addExtension(
  1479. $type= $this->resolveType($method->extension),
  1480. $this->resolveType(new TypeName('self'))->getMethod($method->name)
  1481. );
  1482. $this->metadata[0]['EXT'][$method->name]= $type->literal(); // HACK, this should be accessible in scope
  1483. }
  1484. $b->append(implode(' ', Modifiers::namesOf($method->modifiers)));
  1485. $b->append(' function '.$method->name);
  1486. // Begin
  1487. $this->enter(new MethodScope($method->name));
  1488. if (!Modifiers::isStatic($method->modifiers)) {
  1489. $this->scope[0]->setType(new VariableNode('this'), $this->scope[0]->declarations[0]->name);
  1490. }
  1491. $return= $this->resolveType($method->returns, false);
  1492. $this->metadata[0][1][$method->name]= array(
  1493. DETAIL_ARGUMENTS => array(),
  1494. DETAIL_RETURNS => $return->name(),
  1495. DETAIL_THROWS => array(),
  1496. DETAIL_COMMENT => $method->comment
  1497. ? trim(preg_replace('/\n\s+\* ?/', "\n", "\n ".substr($method->comment, 4, strpos($method->comment, '* @')- 2)))
  1498. : null
  1499. ,
  1500. DETAIL_ANNOTATIONS => array(),
  1501. DETAIL_TARGET_ANNO => array()
  1502. );
  1503. array_unshift($this->method, $method->name);
  1504. $this->emitAnnotations($this->metadata[0][1][$method->name], (array)$method->annotations);
  1505. // Parameters, body
  1506. if (null !== $method->body) {
  1507. $signature= $this->emitParameters($b, (array)$method->parameters, '{');
  1508. $this->emitAll($b, $method->body);
  1509. $b->append('}');
  1510. } else {
  1511. $signature= $this->emitParameters($b, (array)$method->parameters, ';');
  1512. }
  1513. // Finalize
  1514. if ($this->scope[0]->declarations[0]->name->isGeneric() && $this->scope[0]->declarations[0]->name->isPlaceholder($method->returns)) {
  1515. $this->metadata[0][1][$method->name][DETAIL_ANNOTATIONS]['generic']['return']= $method->returns->compoundName();
  1516. }
  1517. array_shift($this->method);
  1518. $this->leave();
  1519. // Register type information
  1520. $m= new Method();
  1521. $m->name= $method->name;
  1522. $m->returns= new TypeName($return->name());
  1523. $m->parameters= $signature;
  1524. $m->modifiers= $method->modifiers;
  1525. $this->types[0]->addMethod($m, $method->extension);
  1526. }
  1527. /**
  1528. * Emit static initializer
  1529. *
  1530. * @param xp.compiler.emit.Buffer b
  1531. * @param xp.compiler.ast.StaticInitializerNode initializer
  1532. */
  1533. protected function emitStaticInitializer($b, $initializer) {
  1534. $this->inits[0][2]= true;
  1535. $b->append('static function __static() {');
  1536. // Static initializations outside of initializer
  1537. if ($this->inits[0][true]) {
  1538. foreach ($this->inits[0][true] as $init) {
  1539. $b->append($init);
  1540. }
  1541. unset($this->inits[0][true]);
  1542. }
  1543. $this->emitAll($b, (array)$initializer->statements);
  1544. $b->append('}');
  1545. }
  1546. /**
  1547. * Emit a constructor
  1548. *
  1549. * @param xp.compiler.emit.Buffer b
  1550. * @param xp.compiler.ast.ConstructorNode constructor
  1551. */
  1552. protected function emitConstructor($b, $constructor) {
  1553. $b->append(implode(' ', Modifiers::namesOf($constructor->modifiers)));
  1554. $b->append(' function __construct');
  1555. // Begin
  1556. $this->enter(new MethodScope('__construct'));
  1557. $this->scope[0]->setType(new VariableNode('this'), $this->scope[0]->declarations[0]->name);
  1558. $this->metadata[0][1]['__construct']= array(
  1559. DETAIL_ARGUMENTS => array(),
  1560. DETAIL_RETURNS => null,
  1561. DETAIL_THROWS => array(),
  1562. DETAIL_COMMENT => preg_replace('/\n\s+\* ?/', "\n ", "\n ".$constructor->comment),
  1563. DETAIL_ANNOTATIONS => array(),
  1564. DETAIL_TARGET_ANNO => array()
  1565. );
  1566. array_unshift($this->method, '__construct');
  1567. $this->emitAnnotations($this->metadata[0][1]['__construct'], (array)$constructor->annotations);
  1568. // Arguments, initializations, body
  1569. if (null !== $constructor->body) {
  1570. $signature= $this->emitParameters($b, (array)$constructor->parameters, '{');
  1571. if ($this->inits[0][false]) {
  1572. foreach ($this->inits[0][false] as $init) {
  1573. $b->append($init);
  1574. }
  1575. unset($this->inits[0][false]);
  1576. }
  1577. $this->emitAll($b, $constructor->body);
  1578. $b->append('}');
  1579. } else {
  1580. $signature= $this->emitParameters($b, (array)$constructor->parameters, ';');
  1581. }
  1582. // Finalize
  1583. array_shift($this->method);
  1584. $this->leave();
  1585. // Register type information
  1586. $c= new Constructor();
  1587. $c->parameters= $signature;
  1588. $c->modifiers= $constructor->modifiers;
  1589. $this->types[0]->constructor= $c;
  1590. }
  1591. /**
  1592. * Emits class registration
  1593. *
  1594. * <code>
  1595. * xp::$cn['class.'.$name]= $qualified;
  1596. * xp::$meta['details.'.$qualified]= $meta;
  1597. * </code>
  1598. *
  1599. * @param xp.compiler.emit.Buffer b
  1600. * @param xp.compiler.ast.TypeDeclarationNode
  1601. * @param string qualified
  1602. */
  1603. protected function registerClass($b, $declaration, $qualified) {
  1604. unset($this->metadata[0]['EXT']);
  1605. // Retain comment
  1606. $this->metadata[0]['class'][DETAIL_COMMENT]= $declaration->comment
  1607. ? trim(preg_replace('/\n\s+\* ?/', "\n", "\n ".substr($declaration->comment, 4, strpos($declaration->comment, '* @')- 2)))
  1608. : null
  1609. ;
  1610. // Copy annotations
  1611. $this->emitAnnotations($this->metadata[0]['class'], (array)$declaration->annotations);
  1612. $b->append('xp::$cn[\''.$declaration->literal.'\']= \''.$qualified.'\';');
  1613. $b->append('xp::$meta[\''.$qualified.'\']= '.var_export($this->metadata[0], true).';');
  1614. // Run static initializer if existant on synthetic types
  1615. if ($declaration->synthetic && $this->inits[0][2]) {
  1616. $b->append($declaration->literal)->append('::__static();');
  1617. }
  1618. }
  1619. /**
  1620. * Emit a class property
  1621. *
  1622. * @param xp.compiler.emit.Buffer b
  1623. * @param xp.compiler.ast.IndexerNode indexer
  1624. */
  1625. protected function emitIndexer($b, $indexer) {
  1626. $params= array($indexer->parameter);
  1627. $defines= array(
  1628. 'get' => array('offsetGet', $params, $indexer->type),
  1629. 'set' => array('offsetSet', array_merge($params, array(array('name' => 'value', 'type' => $indexer->type, 'check' => false))), TypeName::$VOID),
  1630. 'isset' => array('offsetExists', $params, new TypeName('bool')),
  1631. 'unset' => array('offsetUnset', $params, TypeName::$VOID),
  1632. );
  1633. foreach ($indexer->handlers as $name => $statements) {
  1634. $this->emitOne($b, new MethodNode(array(
  1635. 'modifiers' => MODIFIER_PUBLIC,
  1636. 'annotations'=> null,
  1637. 'name' => $defines[$name][0],
  1638. 'returns' => $defines[$name][2],
  1639. 'parameters' => $defines[$name][1],
  1640. 'throws' => null,
  1641. 'body' => $statements,
  1642. 'comment' => '(Generated)'
  1643. )));
  1644. }
  1645. // Register type information
  1646. $i= new Indexer();
  1647. $i->type= new TypeName($this->resolveType($indexer->type)->name());
  1648. $i->parameter= new TypeName($this->resolveType($indexer->parameter['type'])->name());
  1649. $i->modifiers= $indexer->modifiers;
  1650. $this->types[0]->indexer= $i;
  1651. }
  1652. /**
  1653. * Emit a class property
  1654. *
  1655. * @param xp.compiler.emit.Buffer b
  1656. * @param xp.compiler.ast.PropertyNode property
  1657. */
  1658. protected function emitProperty($b, $property) {
  1659. foreach ($property->handlers as $name => $statements) {
  1660. $this->properties[0][$name][$property->name]= array($property->type, $statements);
  1661. }
  1662. $type= $this->resolveType($property->type);
  1663. $this->metadata[0][0][$property->name]= array(
  1664. DETAIL_ANNOTATIONS => array('type' => $type->name()),
  1665. DETAIL_PROPERTY => $property->modifiers
  1666. );
  1667. // Register type information
  1668. $p= new Property();
  1669. $p->name= $property->name;
  1670. $p->type= new TypeName($type->name());
  1671. $p->modifiers= $property->modifiers;
  1672. $this->types[0]->addProperty($p);
  1673. }
  1674. /**
  1675. * Emit class properties.
  1676. *
  1677. * Creates the equivalent of the following:
  1678. * <code>
  1679. * public function __get($name) {
  1680. * if ('length' === $name) {
  1681. * return $this->_length;
  1682. * } else if ('chars' === $name) {
  1683. * return str_split($this->buffer);
  1684. * }
  1685. * }
  1686. * </code>
  1687. *
  1688. * @param xp.compiler.emit.Buffer b
  1689. * @param array<string, array<string, xp.compiler.ast.Node[]>> properties
  1690. */
  1691. protected function emitProperties($b, array $properties) {
  1692. static $mangled= 'ииname';
  1693. $auto= array();
  1694. if (!empty($properties['get'])) {
  1695. $b->append('function __get($'.$mangled.') {');
  1696. $this->enter(new MethodScope('__get'));
  1697. $this->scope[0]->setType(new VariableNode('this'), $this->scope[0]->declarations[0]->name);
  1698. foreach ($properties['get'] as $name => $definition) {
  1699. $b->append('if (\''.$name.'\' === $'.$mangled.') {');
  1700. if (null === $definition[1]) {
  1701. $b->append('return $this->__и'.$name.';');
  1702. $auto[$name]= true;
  1703. } else {
  1704. $this->emitAll($b, $definition[1]);
  1705. }
  1706. $b->append('} else ');
  1707. }
  1708. $b->append('return parent::__get($'.$mangled.'); }');
  1709. $this->leave();
  1710. }
  1711. if (!empty($properties['set'])) {
  1712. $b->append('function __set($'.$mangled.', $value) {');
  1713. $this->enter(new MethodScope('__set'));
  1714. $this->scope[0]->setType(new VariableNode('this'), $this->scope[0]->declarations[0]->name);
  1715. foreach ($properties['set'] as $name => $definition) {
  1716. $this->scope[0]->setType(new VariableNode('value'), $definition[0]);
  1717. $b->append('if (\''.$name.'\' === $'.$mangled.') {');
  1718. if (null === $definition[1]) {
  1719. $b->append('$this->__и'.$name.'= $value;');
  1720. $auto[$name]= true;
  1721. } else {
  1722. $this->emitAll($b, $definition[1]);
  1723. }
  1724. $b->append('} else ');
  1725. }
  1726. $b->append('parent::__set($'.$mangled.', $value); }');
  1727. $this->leave();
  1728. }
  1729. // Declare auto-properties as private with null as initial value. Declare a
  1730. // public static member with all properties' names and types as hashmap.
  1731. foreach ($auto as $name => $none) $b->append('private $__и'.$name.'= null;');
  1732. }
  1733. /**
  1734. * Emit an enum member
  1735. *
  1736. * @param xp.compiler.emit.Buffer b
  1737. * @param xp.compiler.ast.EnumMemberNode member
  1738. */
  1739. protected function emitEnumMember($b, $member) {
  1740. $b->append('public static $'.$member->name.';');
  1741. // Add field metadata (type, stored in @type annotation, see
  1742. // lang.reflect.Field and lang.XPClass::detailsForField())
  1743. $type= $this->resolveType(new TypeName('self'));
  1744. $this->metadata[0][0][$member->name]= array(
  1745. DETAIL_ANNOTATIONS => array('type' => $type->name())
  1746. );
  1747. // Register type information
  1748. $f= new Field();
  1749. $f->name= $member->name;
  1750. $f->type= new TypeName($type->name());
  1751. $f->modifiers= MODIFIER_PUBLIC | MODIFIER_STATIC;
  1752. $this->types[0]->addField($f);
  1753. }
  1754. /**
  1755. * Emit a class constant
  1756. *
  1757. * @param xp.compiler.emit.Buffer b
  1758. * @param xp.compiler.ast.ClassConstantNode const
  1759. */
  1760. protected function emitClassConstant($b, $const) {
  1761. $b->append('const ')->append($const->name)->append('=');
  1762. $this->emitOne($b, $const->value);
  1763. $b->append(';');
  1764. // Register type information.
  1765. $c= new Constant();
  1766. $c->type= new TypeName($this->resolveType($const->type)->name());
  1767. $c->name= $const->name;
  1768. $c->value= $const->value instanceof Resolveable ? $const->value->resolve() : $const->value;
  1769. $this->types[0]->addConstant($c);
  1770. }
  1771. /**
  1772. * Emit a class field
  1773. *
  1774. * @param xp.compiler.emit.Buffer b
  1775. * @param xp.compiler.ast.FieldNode field
  1776. */
  1777. protected function emitField($b, $field) {
  1778. $static= Modifiers::isStatic($field->modifiers);
  1779. // See whether an initialization is necessary
  1780. $initializable= false;
  1781. if ($field->initialization) {
  1782. if ($field->initialization instanceof Resolveable) {
  1783. try {
  1784. $init= $field->initialization->resolve();
  1785. $initializable= true;
  1786. } catch (\lang\IllegalStateException $e) {
  1787. $this->warn('R100', $e->getMessage(), $field->initialization);
  1788. $initializable= false;
  1789. }
  1790. }
  1791. if (!$initializable) {
  1792. $init= new Buffer('', $b->line);
  1793. $this->enter(new MethodScope('<init>'));
  1794. if ($static) {
  1795. $variable= new StaticMemberAccessNode(new TypeName('self'), $field->name);
  1796. } else {
  1797. $variable= new MemberAccessNode(new VariableNode('this'), $field->name);
  1798. $this->scope[0]->setType(new VariableNode('this'), $this->scope[0]->declarations[0]->name);
  1799. }
  1800. $this->emitOne($init, new AssignmentNode(array(
  1801. 'variable' => $variable,
  1802. 'expression' => $field->initialization,
  1803. 'op' => '=',
  1804. )));
  1805. $init->append(';');
  1806. $type= $this->scope[0]->typeOf($variable);
  1807. $this->leave();
  1808. $this->inits[0][$static][]= $init;
  1809. $this->scope[0]->setType($field->initialization, $type);
  1810. }
  1811. // If the field is "var" and we have an initialization, determine
  1812. // the type from there
  1813. if ($field->type->isVariable()) {
  1814. $field->type= $this->scope[0]->typeOf($field->initialization);
  1815. }
  1816. }
  1817. switch ($field->modifiers & (MODIFIER_PUBLIC | MODIFIER_PROTECTED | MODIFIER_PRIVATE)) {
  1818. case MODIFIER_PRIVATE: $b->append('private '); break;
  1819. case MODIFIER_PROTECTED: $b->append('protected '); break;
  1820. default: $b->append('public '); break;
  1821. }
  1822. $static && $b->append('static ');
  1823. $b->append('$'.$field->name);
  1824. $initializable && $b->append('= ')->append(var_export($init, true));
  1825. $b->append(';');
  1826. // Copy annotations
  1827. $this->metadata[0][0][$field->name]= array(DETAIL_ANNOTATIONS => array());
  1828. $this->emitAnnotations($this->metadata[0][0][$field->name], (array)$field->annotations);
  1829. // Add field metadata (type, stored in @type annotation, see
  1830. // lang.reflect.Field and lang.XPClass::detailsForField()).
  1831. $type= $this->resolveType($field->type);
  1832. $this->metadata[0][0][$field->name][DETAIL_ANNOTATIONS]['type']= $type->name();
  1833. // Register type information
  1834. $f= new Field();
  1835. $f->name= $field->name;
  1836. $f->type= new TypeName($type->name());
  1837. $f->modifiers= $field->modifiers;
  1838. $this->types[0]->addField($f);
  1839. }
  1840. /**
  1841. * Emit type name and modifiers
  1842. *
  1843. * @param xp.compiler.emit.Buffer b
  1844. * @param string type
  1845. * @param xp.compiler.ast.TypeDeclarationNode declaration
  1846. */
  1847. protected function emitTypeName($b, $type, TypeDeclarationNode $declaration) {
  1848. $this->metadata[0]['class']= array();
  1849. // Check whether class needs to be fully qualified
  1850. if ($declaration->modifiers & MODIFIER_PACKAGE) {
  1851. $b->append('$package= \'')->append($this->scope[0]->package->name)->append("';");
  1852. $declaration->literal= strtr($this->scope[0]->package->name, '.', 'и').'и'.$declaration->name->name;
  1853. } else {
  1854. $declaration->literal= $declaration->name->name;
  1855. }
  1856. // Emit abstract and final modifiers
  1857. if (Modifiers::isAbstract($declaration->modifiers)) {
  1858. $b->append('abstract ');
  1859. } else if (Modifiers::isFinal($declaration->modifiers)) {
  1860. $b->append('final ');
  1861. }
  1862. // Emit declaration
  1863. $b->append(' ')->append($type)->append(' ')->append($declaration->literal);
  1864. }
  1865. /**
  1866. * Emit an enum declaration
  1867. *
  1868. * Basic form:
  1869. * <code>
  1870. * public enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
  1871. * </code>
  1872. *
  1873. * With values:
  1874. * <code>
  1875. * public enum Coin { penny(1), nickel(2), dime(10), quarter(25) }
  1876. * </code>
  1877. *
  1878. * Abstract:
  1879. * <code>
  1880. * public abstract enum Operation {
  1881. * plus {
  1882. * public int evaluate(int $x, int $y) { return $x + $y; }
  1883. * },
  1884. * minus {
  1885. * public int evaluate(int $x, int $y) { return $x - $y; }
  1886. * };
  1887. *
  1888. * public abstract int evaluate(int $x, int $y);
  1889. * }
  1890. * </code>
  1891. *
  1892. * @param xp.compiler.emit.Buffer b
  1893. * @param xp.compiler.ast.EnumNode declaration
  1894. */
  1895. protected function emitEnum($b, $declaration) {
  1896. $parent= $declaration->parent ?: new TypeName('lang.Enum');
  1897. $parentType= $this->resolveType($parent);
  1898. $thisType= new TypeDeclaration(new ParseTree($this->scope[0]->package, array(), $declaration), $parentType);
  1899. $this->scope[0]->addResolved('self', $thisType);
  1900. $this->scope[0]->addResolved('parent', $parentType);
  1901. // FIXME: ???
  1902. $this->scope[0]->addResolved($declaration->name->name, $thisType);
  1903. $this->scope[0]->imports[$declaration->name->name]= $declaration->name->name;
  1904. $this->enter(new TypeDeclarationScope());
  1905. // Ensure parent class and interfaces are loaded
  1906. $this->emitTypeName($b, 'class', $declaration);
  1907. $b->append(' extends '.$parentType->literal(true));
  1908. array_unshift($this->metadata, array(array(), array()));
  1909. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]= array();
  1910. array_unshift($this->properties, array('get' => array(), 'set' => array()));
  1911. $abstract= Modifiers::isAbstract($declaration->modifiers);
  1912. // Generics
  1913. if ($declaration->name->isGeneric()) {
  1914. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['self']= $this->genericComponentAsMetadata($declaration->name);
  1915. }
  1916. if ($parent->isGeneric()) {
  1917. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['parent']= $this->genericComponentAsMetadata($parent);
  1918. }
  1919. // Interfaces
  1920. if ($declaration->implements) {
  1921. $b->append(' implements ');
  1922. $s= sizeof($declaration->implements)- 1;
  1923. foreach ($declaration->implements as $i => $type) {
  1924. if ($type->isGeneric()) {
  1925. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['implements'][$i]= $this->genericComponentAsMetadata($type);
  1926. }
  1927. $b->append($this->resolveType($type)->literal(true));
  1928. $i < $s && $b->append(', ');
  1929. }
  1930. }
  1931. // Member declaration
  1932. $b->append(' {');
  1933. // public static self[] values() { return parent::membersOf(__CLASS__) }
  1934. $declaration->body[]= new MethodNode(array(
  1935. 'modifiers' => MODIFIER_PUBLIC | MODIFIER_STATIC,
  1936. 'annotations'=> null,
  1937. 'name' => 'values',
  1938. 'returns' => new TypeName('self[]'),
  1939. 'parameters' => null,
  1940. 'throws' => null,
  1941. 'body' => array(
  1942. new ReturnNode(new StaticMethodCallNode(
  1943. new TypeName('parent'),
  1944. 'membersOf',
  1945. array(new StringNode($thisType->literal()))
  1946. ))
  1947. ),
  1948. 'comment' => '(Generated)'
  1949. ));
  1950. // Members
  1951. foreach ((array)$declaration->body as $node) {
  1952. $this->emitOne($b, $node);
  1953. }
  1954. $this->emitProperties($b, $this->properties[0]);
  1955. // Initialization
  1956. $b->append('static function __static() {');
  1957. foreach ($declaration->body as $i => $member) {
  1958. if (!$member instanceof EnumMemberNode) continue;
  1959. $b->append('self::$'.$member->name.'= ');
  1960. if ($member->body) {
  1961. if (!$abstract) {
  1962. $this->error('E403', 'Only abstract enums can contain members with bodies ('.$member->name.')');
  1963. // Continues so declaration is closed
  1964. }
  1965. $unique= new TypeName($declaration->name->name.'ии'.$member->name);
  1966. $decl= new ClassNode(0, null, $unique, $declaration->name, array(), $member->body);
  1967. $decl->synthetic= true;
  1968. $ptr= new TypeDeclaration(new ParseTree(null, array(), $decl), $thisType);
  1969. $this->scope[0]->declarations[]= $decl;
  1970. $b->append('new '.$unique->name.'(');
  1971. } else {
  1972. $b->append('new self(');
  1973. }
  1974. if ($member->value) {
  1975. $this->emitOne($b, $member->value);
  1976. } else {
  1977. $b->append($i);
  1978. }
  1979. $b->append(', \''.$member->name.'\');');
  1980. }
  1981. $b->append('}');
  1982. // Finish
  1983. $b->append('}');
  1984. $this->leave();
  1985. $this->registerClass($b, $declaration, $thisType->name());
  1986. array_shift($this->properties);
  1987. array_shift($this->metadata);
  1988. // Register type info
  1989. $this->types[0]->name= $thisType->name();
  1990. $this->types[0]->kind= Types::ENUM_KIND;
  1991. $this->types[0]->literal= $declaration->literal;
  1992. $this->types[0]->parent= $parentType;
  1993. }
  1994. /**
  1995. * Emit a Interface declaration
  1996. *
  1997. * @param xp.compiler.emit.Buffer b
  1998. * @param xp.compiler.ast.InterfaceNode declaration
  1999. */
  2000. protected function emitInterface($b, $declaration) {
  2001. $thisType= new TypeDeclaration(new ParseTree($this->scope[0]->package, array(), $declaration));
  2002. $this->scope[0]->addResolved('self', $thisType);
  2003. $this->enter(new TypeDeclarationScope());
  2004. $this->emitTypeName($b, 'interface', $declaration);
  2005. array_unshift($this->metadata, array(array(), array()));
  2006. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]= array();
  2007. // Generics
  2008. if ($declaration->name->isGeneric()) {
  2009. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['self']= $this->genericComponentAsMetadata($declaration->name);
  2010. }
  2011. if ($declaration->parents) {
  2012. $b->append(' extends ');
  2013. $s= sizeof($declaration->parents)- 1;
  2014. foreach ((array)$declaration->parents as $i => $type) {
  2015. if ($type->isGeneric()) {
  2016. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['extends'][$i]= $this->genericComponentAsMetadata($type);
  2017. }
  2018. $b->append($this->resolveType($type)->literal(true));
  2019. $i < $s && $b->append(', ');
  2020. }
  2021. }
  2022. $b->append(' {');
  2023. foreach ((array)$declaration->body as $node) {
  2024. $this->emitOne($b, $node);
  2025. }
  2026. $b->append('}');
  2027. $this->leave();
  2028. $this->registerClass($b, $declaration, $thisType->name());
  2029. array_shift($this->metadata);
  2030. // Register type info
  2031. $this->types[0]->name= $thisType->name();
  2032. $this->types[0]->kind= Types::INTERFACE_KIND;
  2033. $this->types[0]->literal= $declaration->literal;
  2034. $this->types[0]->parent= null;
  2035. }
  2036. /**
  2037. * Emit a class declaration
  2038. *
  2039. * @param xp.compiler.emit.Buffer b
  2040. * @param xp.compiler.ast.ClassNode declaration
  2041. */
  2042. protected function emitClass($b, $declaration) {
  2043. $parent= $declaration->parent ?: new TypeName('lang.Object');
  2044. $parentType= $this->resolveType($parent);
  2045. $thisType= new TypeDeclaration(new ParseTree($this->scope[0]->package, array(), $declaration), $parentType);
  2046. $this->scope[0]->addResolved('self', $thisType);
  2047. $this->scope[0]->addResolved('parent', $parentType);
  2048. $this->enter(new TypeDeclarationScope());
  2049. $this->emitTypeName($b, 'class', $declaration);
  2050. $b->append(' extends '.$parentType->literal(true));
  2051. array_unshift($this->metadata, array(array(), array()));
  2052. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]= array();
  2053. array_unshift($this->properties, array());
  2054. array_unshift($this->inits, array(false => array(), true => array(), 2 => false));
  2055. // Generics
  2056. if ($declaration->name->isGeneric()) {
  2057. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['self']= $this->genericComponentAsMetadata($declaration->name);
  2058. }
  2059. if ($parent->isGeneric()) {
  2060. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['parent']= $this->genericComponentAsMetadata($parent);
  2061. }
  2062. // Check if we need to implement ArrayAccess
  2063. foreach ((array)$declaration->body as $node) {
  2064. if ($node instanceof IndexerNode) {
  2065. $declaration->implements[]= 'ArrayAccess';
  2066. }
  2067. }
  2068. // Interfaces
  2069. if ($declaration->implements) {
  2070. $b->append(' implements ');
  2071. $s= sizeof($declaration->implements)- 1;
  2072. foreach ($declaration->implements as $i => $type) {
  2073. if ($type instanceof TypeName) {
  2074. if ($type->isGeneric()) {
  2075. $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['implements'][$i]= $this->genericComponentAsMetadata($type);
  2076. }
  2077. $b->append($this->resolveType($type)->literal(true));
  2078. } else {
  2079. $b->append($type);
  2080. }
  2081. $i < $s && $b->append(', ');
  2082. }
  2083. }
  2084. // Members
  2085. $b->append('{');
  2086. foreach ((array)$declaration->body as $node) {
  2087. $this->emitOne($b, $node);
  2088. }
  2089. $this->emitProperties($b, $this->properties[0]);
  2090. // Generate a constructor if initializations are available.
  2091. // They will have already been emitted if a constructor exists!
  2092. if ($this->inits[0][false]) {
  2093. $arguments= array();
  2094. $parameters= array();
  2095. if ($parentType->hasConstructor()) {
  2096. foreach ($parentType->getConstructor()->parameters as $i => $param) {
  2097. $parameters[]= array('name' => 'ииa'.$i, 'type' => $param->type, 'default' => $param->default);
  2098. $arguments[]= new VariableNode('ииa'.$i);
  2099. }
  2100. $body= array(new StaticMethodCallNode(new TypeName('parent'), '__construct', $arguments));
  2101. } else {
  2102. $body= array();
  2103. }
  2104. $this->emitOne($b, new ConstructorNode(array(
  2105. 'modifiers' => MODIFIER_PUBLIC,
  2106. 'parameters' => $parameters,
  2107. 'annotations' => null,
  2108. 'body' => $body,
  2109. 'comment' => '(Generated)',
  2110. 'position' => $declaration->position
  2111. )));
  2112. }
  2113. // Generate a static initializer if initializations are available.
  2114. // They will have already been emitted if a static initializer exists!
  2115. if ($this->inits[0][true]) {
  2116. $this->emitOne($b, new StaticInitializerNode(null));
  2117. }
  2118. // Create __import
  2119. if (isset($this->metadata[0]['EXT'])) {
  2120. $b->append('static function __import($scope) {');
  2121. foreach ($this->metadata[0]['EXT'] as $method => $type) {
  2122. $b->append('xp::$ext[$scope]["')->append($type)->append('"]= "')->append($thisType->literal())->append('";');
  2123. }
  2124. $b->append('}');
  2125. }
  2126. // Generic instances have {definition-type, null, [argument-type[0..n]]}
  2127. // stored as type names in their details
  2128. if (isset($declaration->generic)) {
  2129. $this->metadata[0]['class'][DETAIL_GENERIC]= $declaration->generic;
  2130. }
  2131. $b->append('}');
  2132. $this->leave();
  2133. $this->registerClass($b, $declaration, $thisType->name());
  2134. array_shift($this->properties);
  2135. array_shift($this->metadata);
  2136. array_shift($this->inits);
  2137. // Register type info
  2138. $this->types[0]->name= $thisType->name();
  2139. $this->types[0]->kind= Types::CLASS_KIND;
  2140. $this->types[0]->literal= $declaration->literal;
  2141. $this->types[0]->parent= $parentType;
  2142. }
  2143. /**
  2144. * Emit dynamic instanceof
  2145. *
  2146. * @param xp.compiler.emit.Buffer b
  2147. * @param xp.compiler.ast.DynamicInstanceOfNode instanceof
  2148. */
  2149. protected function emitDynamicInstanceOf($b, $instanceof) {
  2150. $this->emitOne($b, $instanceof->expression);
  2151. $b->append(' instanceof ')->append('$')->append($instanceof->variable);
  2152. }
  2153. /**
  2154. * Emit instanceof
  2155. *
  2156. * @param xp.compiler.emit.Buffer b
  2157. * @param xp.compiler.ast.InstanceOfNode instanceof
  2158. */
  2159. protected function emitInstanceOf($b, $instanceof) {
  2160. $this->emitOne($b, $instanceof->expression);
  2161. $b->append(' instanceof ')->append($this->resolveType($instanceof->type)->literal());
  2162. }
  2163. /**
  2164. * Emit clone
  2165. *
  2166. * @param xp.compiler.emit.Buffer b
  2167. * @param xp.compiler.ast.CloneNode clone
  2168. */
  2169. protected function emitClone($b, $clone) {
  2170. $b->append('clone ');
  2171. $this->emitOne($b, $clone->expression);
  2172. }
  2173. /**
  2174. * Emit import
  2175. *
  2176. * @param xp.compiler.emit.Buffer b
  2177. * @param xp.compiler.ast.ImportNode import
  2178. */
  2179. protected function emitImport($b, $import) {
  2180. if ('.*' == substr($import->name, -2)) {
  2181. $this->scope[0]->addPackageImport(substr($import->name, 0, -2));
  2182. } else {
  2183. $this->scope[0]->addTypeImport($import->name);
  2184. }
  2185. }
  2186. /**
  2187. * Emit native import
  2188. *
  2189. * @param xp.compiler.emit.Buffer b
  2190. * @param xp.compiler.ast.NativeImportNode import
  2191. */
  2192. protected function emitNativeImport($b, $import) {
  2193. $imported= $this->scope[0]->importer->import($import->name);
  2194. if (0 === ($k= key($imported))) {
  2195. $this->scope[0]->statics[0]= array_merge($this->scope[0]->statics[0], $imported[$k]);
  2196. } else {
  2197. $this->scope[0]->statics[$k]= $imported[$k];
  2198. }
  2199. }
  2200. /**
  2201. * Emit static import
  2202. *
  2203. * Given the following:
  2204. * <code>
  2205. * import static rdbms.criterion.Restrictions.*;
  2206. * </code>
  2207. *
  2208. * A call to lessThanOrEqualTo() "function" then resolves to a static
  2209. * method call to Restrictions::lessThanOrEqualTo()
  2210. *
  2211. * @param xp.compiler.emit.Buffer b
  2212. * @param xp.compiler.ast.StaticImportNode import
  2213. */
  2214. protected function emitStaticImport($b, $import) {
  2215. if ('.*' == substr($import->name, -2)) {
  2216. $this->scope[0]->statics[0][substr($import->name, 0, -2)]= $this->resolveType(new TypeName(substr($import->name, 0, -2)));
  2217. } else {
  2218. $p= strrpos($import->name, '.');
  2219. $method= $this->resolveType(new TypeName(substr($import->name, 0, $p)))->getMethod(substr($import->name, $p+ 1));
  2220. $this->scope[0]->statics[$method->name()]= $method;
  2221. }
  2222. }
  2223. /**
  2224. * Emit a return statement
  2225. * <code>
  2226. * return; // void return
  2227. * return [EXPRESSION]; // returning a value
  2228. * </code>
  2229. *
  2230. * @param xp.compiler.emit.Buffer b
  2231. * @param xp.compiler.ast.ReturnNode new
  2232. */
  2233. protected function emitReturn($b, $return) {
  2234. $this->finalizers[0] && $this->emitOne($b, $this->finalizers[0]);
  2235. if (!$return->expression) {
  2236. $b->append('return');
  2237. } else {
  2238. $b->append('return ');
  2239. $this->emitOne($b, $return->expression);
  2240. }
  2241. }
  2242. /**
  2243. * Emit a silence node
  2244. *
  2245. * @param xp.compiler.emit.Buffer b
  2246. * @param xp.compiler.ast.SilenceOperatorNode silenced
  2247. */
  2248. protected function emitSilenceOperator($b, $silenced) {
  2249. $b->append('@');
  2250. $this->emitOne($b, $silenced->expression);
  2251. $this->scope[0]->setType($silenced, $this->scope[0]->typeOf($silenced->expression));
  2252. }
  2253. /**
  2254. * Emit all given nodes
  2255. *
  2256. * @param xp.compiler.emit.Buffer b
  2257. * @param xp.compiler.ast.Node[] nodes
  2258. */
  2259. protected function emitAll($b, array $nodes) {
  2260. foreach ($nodes as $node) {
  2261. $this->emitOne($b, $node);
  2262. $b->append(';');
  2263. }
  2264. }
  2265. /**
  2266. * Resolve a type, raising an error message if type resolution
  2267. * raises an error and return an unknown type reference in this
  2268. * case.
  2269. *
  2270. * @param xp.compiler.types.TypeName
  2271. * @param bool register default true
  2272. * @return xp.compiler.types.Types
  2273. */
  2274. protected function resolveType(TypeName $t, $register= true) {
  2275. try {
  2276. $ptr= $this->scope[0]->resolveType($t, $register);
  2277. $this->cat && $this->cat->info('Resolve', $t, '=', $ptr);
  2278. return $ptr;
  2279. } catch (ResolveException $e) {
  2280. $this->cat && $this->cat->warn('Resolve', $t, '~', $e->compoundMessage());
  2281. $this->error('R'.$e->getKind(), $e->compoundMessage());
  2282. return new TypeReference($t, Types::UNKNOWN_KIND);
  2283. }
  2284. }
  2285. /**
  2286. * Entry point
  2287. *
  2288. * @param xp.compiler.ast.ParseTree tree
  2289. * @param xp.compiler.types.Scope scope
  2290. * @return xp.compiler.Result
  2291. */
  2292. public function emit(ParseTree $tree, Scope $scope) {
  2293. $bytes= new Buffer('', 1);
  2294. array_unshift($this->local, array());
  2295. array_unshift($this->temp, 0);
  2296. array_unshift($this->scope, $scope->enter(new CompilationUnitScope()));
  2297. $this->scope[0]->importer= new NativeImporter();
  2298. $this->scope[0]->declarations= array($tree->declaration);
  2299. $this->scope[0]->package= $tree->package;
  2300. // Functions from lang.base.php
  2301. $this->scope[0]->statics= array(
  2302. 0 => array(),
  2303. 'newinstance' => true,
  2304. 'with' => true,
  2305. 'create' => true,
  2306. 'raise' => true,
  2307. 'delete' => true,
  2308. 'cast' => true,
  2309. 'is' => true,
  2310. 'this' => true,
  2311. 'isset' => true,
  2312. 'unset' => true,
  2313. 'empty' => true,
  2314. 'eval' => true,
  2315. 'include' => true,
  2316. 'require' => true,
  2317. 'include_once'=> true,
  2318. 'require_once'=> true,
  2319. );
  2320. $this->cat && $this->cat->infof('== Enter %s ==', basename($tree->origin));
  2321. // Import and declarations
  2322. $t= null;
  2323. $this->scope[0]->addResolved('self', new TypeDeclaration($tree)); // FIXME: for import self
  2324. $this->emitAll($bytes, (array)$tree->imports);
  2325. while ($this->scope[0]->declarations) {
  2326. array_unshift($this->types, new CompiledType());
  2327. $decl= current($this->scope[0]->declarations);
  2328. $this->local[0][$decl->name->name]= true;
  2329. $this->emitOne($bytes, $decl);
  2330. array_shift($this->scope[0]->declarations);
  2331. $t || $t= $this->types[0];
  2332. }
  2333. // Load used classes
  2334. $this->emitUses($bytes, $this->scope[0]->used);
  2335. // Leave scope
  2336. array_shift($this->local);
  2337. $this->leave();
  2338. // Check on errors
  2339. $this->cat && $this->cat->infof(
  2340. '== %s: %d error(s), %d warning(s) ==',
  2341. basename($tree->origin),
  2342. sizeof($this->messages['errors']),
  2343. sizeof($this->messages['warnings'])
  2344. );
  2345. if ($this->messages['errors']) {
  2346. throw new \lang\FormatException('Errors emitting '.$tree->origin.': '.\xp::stringOf($this->messages));
  2347. }
  2348. // Finalize
  2349. return new Result($t, $bytes);
  2350. }
  2351. }