| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library code_generator; | 5 library code_generator; |
| 6 | 6 |
| 7 import 'glue.dart'; | 7 import 'glue.dart'; |
| 8 | 8 |
| 9 import '../../closure.dart' show | 9 import '../../closure.dart' show |
| 10 ClosureClassElement; | 10 ClosureClassElement; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 61 Set<String> usedVariableNames = new Set<String>(); | 61 Set<String> usedVariableNames = new Set<String>(); |
| 62 | 62 |
| 63 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); | 63 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); |
| 64 | 64 |
| 65 /// Stacks whose top element is the current target of an unlabeled break | 65 /// Stacks whose top element is the current target of an unlabeled break |
| 66 /// or continue. For continues, this is the loop node itself. | 66 /// or continue. For continues, this is the loop node itself. |
| 67 final tree_ir.FallthroughStack shortBreak = new tree_ir.FallthroughStack(); | 67 final tree_ir.FallthroughStack shortBreak = new tree_ir.FallthroughStack(); |
| 68 final tree_ir.FallthroughStack shortContinue = | 68 final tree_ir.FallthroughStack shortContinue = |
| 69 new tree_ir.FallthroughStack(); | 69 new tree_ir.FallthroughStack(); |
| 70 | 70 |
| 71 /// When the top element is true, [Unreachable] statements will be emitted |
| 72 /// as [Return]s, otherwise they are emitted as empty because they are |
| 73 /// followed by the end of the method. |
| 74 /// |
| 75 /// Note on why the [fallthrough] stack should not be used for this: |
| 76 /// Ordinary statements may choose whether to use the [fallthrough] target, |
| 77 /// and the choice to do so may disable an optimization in [visitIf]. |
| 78 /// But omitting an unreachable 'return' should have lower priority than |
| 79 /// the optimizations in [visitIf], so [visitIf] will instead tell the |
| 80 /// [Unreachable] statements whether they may use fallthrough or not. |
| 81 List<bool> emitUnreachableAsReturn = <bool>[false]; |
| 82 |
| 71 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); | 83 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); |
| 72 | 84 |
| 73 List<js.Statement> accumulator = new List<js.Statement>(); | 85 List<js.Statement> accumulator = new List<js.Statement>(); |
| 74 | 86 |
| 75 CodeGenerator(this.glue, this.registry); | 87 CodeGenerator(this.glue, this.registry); |
| 76 | 88 |
| 77 /// Generates JavaScript code for the body of [function]. | 89 /// Generates JavaScript code for the body of [function]. |
| 78 js.Fun buildFunction(tree_ir.FunctionDefinition function) { | 90 js.Fun buildFunction(tree_ir.FunctionDefinition function) { |
| 79 registerDefaultParameterValues(function.element); | 91 registerDefaultParameterValues(function.element); |
| 80 currentFunction = function.element; | 92 currentFunction = function.element; |
| (...skipping 446 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 shortContinue.use(); | 539 shortContinue.use(); |
| 528 accumulator.add(new js.Continue(null)); | 540 accumulator.add(new js.Continue(null)); |
| 529 } else { | 541 } else { |
| 530 usedLabels.add(node.target); | 542 usedLabels.add(node.target); |
| 531 accumulator.add(new js.Break(node.target.name)); | 543 accumulator.add(new js.Break(node.target.name)); |
| 532 } | 544 } |
| 533 } | 545 } |
| 534 | 546 |
| 535 @override | 547 @override |
| 536 void visitExpressionStatement(tree_ir.ExpressionStatement node) { | 548 void visitExpressionStatement(tree_ir.ExpressionStatement node) { |
| 537 accumulator.add(new js.ExpressionStatement( | 549 js.Expression exp = visitExpression(node.expression); |
| 538 visitExpression(node.expression))); | 550 if (node.next is tree_ir.Unreachable && emitUnreachableAsReturn.last) { |
| 539 visitStatement(node.next); | 551 // Emit as 'return exp' to assist local analysis in the VM. |
| 552 accumulator.add(new js.Return(exp)); |
| 553 } else { |
| 554 accumulator.add(new js.ExpressionStatement(exp)); |
| 555 visitStatement(node.next); |
| 556 } |
| 557 } |
| 558 |
| 559 bool isNullReturn(tree_ir.Statement node) { |
| 560 return node is tree_ir.Return && isNull(node.value); |
| 561 } |
| 562 |
| 563 bool isEndOfMethod(tree_ir.Statement node) { |
| 564 return isNullReturn(node) || |
| 565 node is tree_ir.Break && isNullReturn(node.target.binding.next); |
| 540 } | 566 } |
| 541 | 567 |
| 542 @override | 568 @override |
| 543 void visitIf(tree_ir.If node) { | 569 void visitIf(tree_ir.If node) { |
| 544 js.Expression condition = visitExpression(node.condition); | 570 js.Expression condition = visitExpression(node.condition); |
| 545 int usesBefore = fallthrough.useCount; | 571 int usesBefore = fallthrough.useCount; |
| 572 // Unless the 'else' part ends the method. make sure to terminate any |
| 573 // uncompletable code paths in the 'then' part. |
| 574 emitUnreachableAsReturn.add(!isEndOfMethod(node.elseStatement)); |
| 546 js.Statement thenBody = buildBodyStatement(node.thenStatement); | 575 js.Statement thenBody = buildBodyStatement(node.thenStatement); |
| 576 emitUnreachableAsReturn.removeLast(); |
| 547 bool thenHasFallthrough = (fallthrough.useCount > usesBefore); | 577 bool thenHasFallthrough = (fallthrough.useCount > usesBefore); |
| 548 if (thenHasFallthrough) { | 578 if (thenHasFallthrough) { |
| 549 js.Statement elseBody = buildBodyStatement(node.elseStatement); | 579 js.Statement elseBody = buildBodyStatement(node.elseStatement); |
| 550 accumulator.add(new js.If(condition, thenBody, elseBody)); | 580 accumulator.add(new js.If(condition, thenBody, elseBody)); |
| 551 } else { | 581 } else { |
| 552 // The 'then' body cannot complete normally, so emit a short 'if' | 582 // The 'then' body cannot complete normally, so emit a short 'if' |
| 553 // and put the 'else' body after it. | 583 // and put the 'else' body after it. |
| 554 accumulator.add(new js.If.noElse(condition, thenBody)); | 584 accumulator.add(new js.If.noElse(condition, thenBody)); |
| 555 visitStatement(node.elseStatement); | 585 visitStatement(node.elseStatement); |
| 556 } | 586 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 607 js.Expression makeSequence(List<tree_ir.Expression> list) { | 637 js.Expression makeSequence(List<tree_ir.Expression> list) { |
| 608 return list.map(visitExpression).reduce((x,y) => new js.Binary(',', x, y)); | 638 return list.map(visitExpression).reduce((x,y) => new js.Binary(',', x, y)); |
| 609 } | 639 } |
| 610 | 640 |
| 611 @override | 641 @override |
| 612 void visitFor(tree_ir.For node) { | 642 void visitFor(tree_ir.For node) { |
| 613 js.Expression condition = visitExpression(node.condition); | 643 js.Expression condition = visitExpression(node.condition); |
| 614 shortBreak.push(node.next); | 644 shortBreak.push(node.next); |
| 615 shortContinue.push(node); | 645 shortContinue.push(node); |
| 616 fallthrough.push(node); | 646 fallthrough.push(node); |
| 647 emitUnreachableAsReturn.add(true); |
| 617 js.Statement body = buildBodyStatement(node.body); | 648 js.Statement body = buildBodyStatement(node.body); |
| 649 emitUnreachableAsReturn.removeLast(); |
| 618 fallthrough.pop(); | 650 fallthrough.pop(); |
| 619 shortContinue.pop(); | 651 shortContinue.pop(); |
| 620 shortBreak.pop(); | 652 shortBreak.pop(); |
| 621 js.Statement loopNode; | 653 js.Statement loopNode; |
| 622 if (node.updates.isEmpty) { | 654 if (node.updates.isEmpty) { |
| 623 loopNode = new js.While(condition, body); | 655 loopNode = new js.While(condition, body); |
| 624 } else { // Compile as a for loop. | 656 } else { // Compile as a for loop. |
| 625 js.Expression init; | 657 js.Expression init; |
| 626 if (accumulator.isNotEmpty && | 658 if (accumulator.isNotEmpty && |
| 627 accumulator.last is js.ExpressionStatement) { | 659 accumulator.last is js.ExpressionStatement) { |
| 628 // Take the preceding expression from the accumulator and use | 660 // Take the preceding expression from the accumulator and use |
| 629 // it as the initializer expression. | 661 // it as the initializer expression. |
| 630 js.ExpressionStatement initStmt = accumulator.removeLast(); | 662 js.ExpressionStatement initStmt = accumulator.removeLast(); |
| 631 init = initStmt.expression; | 663 init = initStmt.expression; |
| 632 } | 664 } |
| 633 js.Expression update = makeSequence(node.updates); | 665 js.Expression update = makeSequence(node.updates); |
| 634 loopNode = new js.For(init, condition, update, body); | 666 loopNode = new js.For(init, condition, update, body); |
| 635 } | 667 } |
| 636 accumulator.add(insertLabel(node.label, loopNode)); | 668 accumulator.add(insertLabel(node.label, loopNode)); |
| 637 visitStatement(node.next); | 669 visitStatement(node.next); |
| 638 } | 670 } |
| 639 | 671 |
| 640 @override | 672 @override |
| 641 void visitWhileTrue(tree_ir.WhileTrue node) { | 673 void visitWhileTrue(tree_ir.WhileTrue node) { |
| 642 js.Expression condition = new js.LiteralBool(true); | 674 js.Expression condition = new js.LiteralBool(true); |
| 643 // A short break in the while will jump to the current fallthrough target. | 675 // A short break in the while will jump to the current fallthrough target. |
| 644 shortBreak.push(fallthrough.target); | 676 shortBreak.push(fallthrough.target); |
| 645 shortContinue.push(node); | 677 shortContinue.push(node); |
| 646 fallthrough.push(node); | 678 fallthrough.push(node); |
| 679 emitUnreachableAsReturn.add(true); |
| 647 js.Statement jsBody = buildBodyStatement(node.body); | 680 js.Statement jsBody = buildBodyStatement(node.body); |
| 681 emitUnreachableAsReturn.removeLast(); |
| 648 fallthrough.pop(); | 682 fallthrough.pop(); |
| 649 shortContinue.pop(); | 683 shortContinue.pop(); |
| 650 if (shortBreak.useCount > 0) { | 684 if (shortBreak.useCount > 0) { |
| 651 // Short breaks use the current fallthrough target. | 685 // Short breaks use the current fallthrough target. |
| 652 fallthrough.use(); | 686 fallthrough.use(); |
| 653 } | 687 } |
| 654 shortBreak.pop(); | 688 shortBreak.pop(); |
| 655 accumulator.add(insertLabel(node.label, new js.While(condition, jsBody))); | 689 accumulator.add(insertLabel(node.label, new js.While(condition, jsBody))); |
| 656 } | 690 } |
| 657 | 691 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 676 accumulator.add(new js.Throw(visitExpression(node.value))); | 710 accumulator.add(new js.Throw(visitExpression(node.value))); |
| 677 } | 711 } |
| 678 | 712 |
| 679 @override | 713 @override |
| 680 void visitRethrow(tree_ir.Rethrow node) { | 714 void visitRethrow(tree_ir.Rethrow node) { |
| 681 glue.reportInternalError('rethrow seen in JavaScript output'); | 715 glue.reportInternalError('rethrow seen in JavaScript output'); |
| 682 } | 716 } |
| 683 | 717 |
| 684 @override | 718 @override |
| 685 void visitUnreachable(tree_ir.Unreachable node) { | 719 void visitUnreachable(tree_ir.Unreachable node) { |
| 686 // Output nothing. | 720 if (emitUnreachableAsReturn.last) { |
| 687 // TODO(asgerf): Emit a throw/return to assist local analysis in the VM? | 721 // Emit a return to assist local analysis in the VM. |
| 722 accumulator.add(new js.Return()); |
| 723 } |
| 688 } | 724 } |
| 689 | 725 |
| 690 @override | 726 @override |
| 691 void visitTry(tree_ir.Try node) { | 727 void visitTry(tree_ir.Try node) { |
| 692 js.Block tryBlock = buildBodyBlock(node.tryBody); | 728 js.Block tryBlock = buildBodyBlock(node.tryBody); |
| 693 tree_ir.Variable exceptionVariable = node.catchParameters.first; | 729 tree_ir.Variable exceptionVariable = node.catchParameters.first; |
| 694 js.VariableDeclaration exceptionParameter = | 730 js.VariableDeclaration exceptionParameter = |
| 695 new js.VariableDeclaration(getVariableName(exceptionVariable)); | 731 new js.VariableDeclaration(getVariableName(exceptionVariable)); |
| 696 js.Block catchBlock = buildBodyBlock(node.catchBody); | 732 js.Block catchBlock = buildBodyBlock(node.catchBody); |
| 697 js.Catch catchPart = new js.Catch(exceptionParameter, catchBlock); | 733 js.Catch catchPart = new js.Catch(exceptionParameter, catchBlock); |
| (...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1086 void registerDefaultParameterValues(ExecutableElement element) { | 1122 void registerDefaultParameterValues(ExecutableElement element) { |
| 1087 if (element is! FunctionElement) return; | 1123 if (element is! FunctionElement) return; |
| 1088 FunctionElement function = element; | 1124 FunctionElement function = element; |
| 1089 if (function.isStatic) return; // Defaults are inlined at call sites. | 1125 if (function.isStatic) return; // Defaults are inlined at call sites. |
| 1090 function.functionSignature.forEachOptionalParameter((param) { | 1126 function.functionSignature.forEachOptionalParameter((param) { |
| 1091 ConstantValue constant = glue.getDefaultParameterValue(param); | 1127 ConstantValue constant = glue.getDefaultParameterValue(param); |
| 1092 registry.registerCompileTimeConstant(constant); | 1128 registry.registerCompileTimeConstant(constant); |
| 1093 }); | 1129 }); |
| 1094 } | 1130 } |
| 1095 } | 1131 } |
| OLD | NEW |