| 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 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 /// followed by the end of the method. | 74 /// followed by the end of the method. |
| 75 /// | 75 /// |
| 76 /// Note on why the [fallthrough] stack should not be used for this: | 76 /// Note on why the [fallthrough] stack should not be used for this: |
| 77 /// Ordinary statements may choose whether to use the [fallthrough] target, | 77 /// Ordinary statements may choose whether to use the [fallthrough] target, |
| 78 /// and the choice to do so may disable an optimization in [visitIf]. | 78 /// and the choice to do so may disable an optimization in [visitIf]. |
| 79 /// But omitting an unreachable 'return' should have lower priority than | 79 /// But omitting an unreachable 'return' should have lower priority than |
| 80 /// the optimizations in [visitIf], so [visitIf] will instead tell the | 80 /// the optimizations in [visitIf], so [visitIf] will instead tell the |
| 81 /// [Unreachable] statements whether they may use fallthrough or not. | 81 /// [Unreachable] statements whether they may use fallthrough or not. |
| 82 List<bool> emitUnreachableAsReturn = <bool>[false]; | 82 List<bool> emitUnreachableAsReturn = <bool>[false]; |
| 83 | 83 |
| 84 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); | 84 final Map<tree_ir.Label, String> labelNames = <tree_ir.Label, String>{}; |
| 85 | 85 |
| 86 List<js.Statement> accumulator = new List<js.Statement>(); | 86 List<js.Statement> accumulator = new List<js.Statement>(); |
| 87 | 87 |
| 88 CodeGenerator(this.glue, this.registry); | 88 CodeGenerator(this.glue, this.registry); |
| 89 | 89 |
| 90 /// Generates JavaScript code for the body of [function]. | 90 /// Generates JavaScript code for the body of [function]. |
| 91 js.Fun buildFunction(tree_ir.FunctionDefinition function) { | 91 js.Fun buildFunction(tree_ir.FunctionDefinition function) { |
| 92 registerDefaultParameterValues(function.element); | 92 registerDefaultParameterValues(function.element); |
| 93 currentFunction = function.element; | 93 currentFunction = function.element; |
| 94 visitStatement(function.body); | 94 visitStatement(function.body); |
| (...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 574 tree_ir.Statement next = fallthrough.target; | 574 tree_ir.Statement next = fallthrough.target; |
| 575 if (node.target.binding == next || | 575 if (node.target.binding == next || |
| 576 next is tree_ir.Continue && node.target == next.target) { | 576 next is tree_ir.Continue && node.target == next.target) { |
| 577 // Fall through to continue target or to equivalent continue. | 577 // Fall through to continue target or to equivalent continue. |
| 578 fallthrough.use(); | 578 fallthrough.use(); |
| 579 } else if (node.target.binding == shortContinue.target) { | 579 } else if (node.target.binding == shortContinue.target) { |
| 580 // The target is the immediately enclosing loop. | 580 // The target is the immediately enclosing loop. |
| 581 shortContinue.use(); | 581 shortContinue.use(); |
| 582 accumulator.add(new js.Continue(null)); | 582 accumulator.add(new js.Continue(null)); |
| 583 } else { | 583 } else { |
| 584 usedLabels.add(node.target); | 584 accumulator.add(new js.Continue(makeLabel(node.target))); |
| 585 accumulator.add(new js.Continue(node.target.name)); | |
| 586 } | 585 } |
| 587 } | 586 } |
| 588 | 587 |
| 589 /// True if [other] is the target of [node] or is a [Break] with the same | 588 /// True if [other] is the target of [node] or is a [Break] with the same |
| 590 /// target. This means jumping to [other] is equivalent to executing [node]. | 589 /// target. This means jumping to [other] is equivalent to executing [node]. |
| 591 bool isEffectiveBreakTarget(tree_ir.Break node, tree_ir.Statement other) { | 590 bool isEffectiveBreakTarget(tree_ir.Break node, tree_ir.Statement other) { |
| 592 return node.target.binding.next == other || | 591 return node.target.binding.next == other || |
| 593 other is tree_ir.Break && node.target == other.target; | 592 other is tree_ir.Break && node.target == other.target; |
| 594 } | 593 } |
| 595 | 594 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 607 fallthrough.use(); | 606 fallthrough.use(); |
| 608 } else if (isEffectiveBreakTarget(node, shortBreak.target)) { | 607 } else if (isEffectiveBreakTarget(node, shortBreak.target)) { |
| 609 // Unlabeled break to the break target or to an equivalent break. | 608 // Unlabeled break to the break target or to an equivalent break. |
| 610 shortBreak.use(); | 609 shortBreak.use(); |
| 611 accumulator.add(new js.Break(null)); | 610 accumulator.add(new js.Break(null)); |
| 612 } else if (isShortContinue(node)) { | 611 } else if (isShortContinue(node)) { |
| 613 // An unlabeled continue is better than a labeled break. | 612 // An unlabeled continue is better than a labeled break. |
| 614 shortContinue.use(); | 613 shortContinue.use(); |
| 615 accumulator.add(new js.Continue(null)); | 614 accumulator.add(new js.Continue(null)); |
| 616 } else { | 615 } else { |
| 617 usedLabels.add(node.target); | 616 accumulator.add(new js.Break(makeLabel(node.target))); |
| 618 accumulator.add(new js.Break(node.target.name)); | |
| 619 } | 617 } |
| 620 } | 618 } |
| 621 | 619 |
| 622 @override | 620 @override |
| 623 void visitExpressionStatement(tree_ir.ExpressionStatement node) { | 621 void visitExpressionStatement(tree_ir.ExpressionStatement node) { |
| 624 js.Expression exp = visitExpression(node.expression); | 622 js.Expression exp = visitExpression(node.expression); |
| 625 if (node.next is tree_ir.Unreachable && emitUnreachableAsReturn.last) { | 623 if (node.next is tree_ir.Unreachable && emitUnreachableAsReturn.last) { |
| 626 // Emit as 'return exp' to assist local analysis in the VM. | 624 // Emit as 'return exp' to assist local analysis in the VM. |
| 627 accumulator.add(new js.Return(exp)); | 625 accumulator.add(new js.Return(exp)); |
| 628 } else { | 626 } else { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 663 | 661 |
| 664 @override | 662 @override |
| 665 void visitLabeledStatement(tree_ir.LabeledStatement node) { | 663 void visitLabeledStatement(tree_ir.LabeledStatement node) { |
| 666 fallthrough.push(node.next); | 664 fallthrough.push(node.next); |
| 667 js.Statement body = buildBodyStatement(node.body); | 665 js.Statement body = buildBodyStatement(node.body); |
| 668 fallthrough.pop(); | 666 fallthrough.pop(); |
| 669 accumulator.add(insertLabel(node.label, body)); | 667 accumulator.add(insertLabel(node.label, body)); |
| 670 visitStatement(node.next); | 668 visitStatement(node.next); |
| 671 } | 669 } |
| 672 | 670 |
| 671 /// Creates a name for [label] if it does not already have one. |
| 672 /// |
| 673 /// This also marks the label as being used. |
| 674 String makeLabel(tree_ir.Label label) { |
| 675 return labelNames.putIfAbsent(label, () => 'L${labelNames.length}'); |
| 676 } |
| 677 |
| 673 /// Wraps a node in a labeled statement unless the label is unused. | 678 /// Wraps a node in a labeled statement unless the label is unused. |
| 674 js.Statement insertLabel(tree_ir.Label label, js.Statement node) { | 679 js.Statement insertLabel(tree_ir.Label label, js.Statement node) { |
| 675 if (usedLabels.remove(label)) { | 680 String name = labelNames[label]; |
| 676 return new js.LabeledStatement(label.name, node); | 681 if (name == null) return node; // Label is unused. |
| 677 } else { | 682 return new js.LabeledStatement(name, node); |
| 678 return node; | |
| 679 } | |
| 680 } | 683 } |
| 681 | 684 |
| 682 /// Returns the current [accumulator] wrapped in a block if neccessary. | 685 /// Returns the current [accumulator] wrapped in a block if neccessary. |
| 683 js.Statement _bodyAsStatement() { | 686 js.Statement _bodyAsStatement() { |
| 684 if (accumulator.length == 0) { | 687 if (accumulator.length == 0) { |
| 685 return new js.EmptyStatement(); | 688 return new js.EmptyStatement(); |
| 686 } | 689 } |
| 687 if (accumulator.length == 1) { | 690 if (accumulator.length == 1) { |
| 688 return accumulator.single; | 691 return accumulator.single; |
| 689 } | 692 } |
| (...skipping 537 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1227 void registerDefaultParameterValues(ExecutableElement element) { | 1230 void registerDefaultParameterValues(ExecutableElement element) { |
| 1228 if (element is! FunctionElement) return; | 1231 if (element is! FunctionElement) return; |
| 1229 FunctionElement function = element; | 1232 FunctionElement function = element; |
| 1230 if (function.isStatic) return; // Defaults are inlined at call sites. | 1233 if (function.isStatic) return; // Defaults are inlined at call sites. |
| 1231 function.functionSignature.forEachOptionalParameter((param) { | 1234 function.functionSignature.forEachOptionalParameter((param) { |
| 1232 ConstantValue constant = glue.getDefaultParameterValue(param); | 1235 ConstantValue constant = glue.getDefaultParameterValue(param); |
| 1233 registry.registerCompileTimeConstant(constant); | 1236 registry.registerCompileTimeConstant(constant); |
| 1234 }); | 1237 }); |
| 1235 } | 1238 } |
| 1236 } | 1239 } |
| OLD | NEW |