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 |