| 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 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 84 final Map<tree_ir.Label, String> labelNames = <tree_ir.Label, String>{}; | 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 tree_ir.Statement statement = function.body; |
| 95 while (statement != null) { |
| 96 statement = visitStatement(statement); |
| 97 } |
| 95 | 98 |
| 96 List<js.Parameter> parameters = new List<js.Parameter>(); | 99 List<js.Parameter> parameters = new List<js.Parameter>(); |
| 97 Set<tree_ir.Variable> parameterSet = new Set<tree_ir.Variable>(); | 100 Set<tree_ir.Variable> parameterSet = new Set<tree_ir.Variable>(); |
| 98 Set<String> declaredVariables = new Set<String>(); | 101 Set<String> declaredVariables = new Set<String>(); |
| 99 | 102 |
| 100 for (tree_ir.Variable parameter in function.parameters) { | 103 for (tree_ir.Variable parameter in function.parameters) { |
| 101 String name = getVariableName(parameter); | 104 String name = getVariableName(parameter); |
| 102 parameters.add(new js.Parameter(name)); | 105 parameters.add(new js.Parameter(name)); |
| 103 parameterSet.add(parameter); | 106 parameterSet.add(parameter); |
| 104 declaredVariables.add(name); | 107 declaredVariables.add(name); |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 ++counter; | 214 ++counter; |
| 212 name = '$prefix$counter'; | 215 name = '$prefix$counter'; |
| 213 } | 216 } |
| 214 variableNames[variable] = name; | 217 variableNames[variable] = name; |
| 215 | 218 |
| 216 return name; | 219 return name; |
| 217 } | 220 } |
| 218 | 221 |
| 219 List<js.Expression> visitExpressionList( | 222 List<js.Expression> visitExpressionList( |
| 220 List<tree_ir.Expression> expressions) { | 223 List<tree_ir.Expression> expressions) { |
| 221 return new List<js.Expression>.generate(expressions.length, | 224 List<js.Expression> result = new List<js.Expression>(expressions.length); |
| 222 (int index) => visitExpression(expressions[index]), | 225 for (int i = 0; i < expressions.length; ++i) { |
| 223 growable: false); | 226 result[i] = visitExpression(expressions[i]); |
| 227 } |
| 228 return result; |
| 224 } | 229 } |
| 225 | 230 |
| 226 giveup(tree_ir.Node node, | 231 giveup(tree_ir.Node node, |
| 227 [String reason = 'unimplemented in CodeGenerator']) { | 232 [String reason = 'unimplemented in CodeGenerator']) { |
| 228 throw new CodegenBailout(node, reason); | 233 throw new CodegenBailout(node, reason); |
| 229 } | 234 } |
| 230 | 235 |
| 231 @override | 236 @override |
| 232 js.Expression visitConditional(tree_ir.Conditional node) { | 237 js.Expression visitConditional(tree_ir.Conditional node) { |
| 233 return new js.Conditional( | 238 return new js.Conditional( |
| (...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 } else if (isShortContinue(node)) { | 616 } else if (isShortContinue(node)) { |
| 612 // An unlabeled continue is better than a labeled break. | 617 // An unlabeled continue is better than a labeled break. |
| 613 shortContinue.use(); | 618 shortContinue.use(); |
| 614 accumulator.add(new js.Continue(null)); | 619 accumulator.add(new js.Continue(null)); |
| 615 } else { | 620 } else { |
| 616 accumulator.add(new js.Break(makeLabel(node.target))); | 621 accumulator.add(new js.Break(makeLabel(node.target))); |
| 617 } | 622 } |
| 618 } | 623 } |
| 619 | 624 |
| 620 @override | 625 @override |
| 621 void visitExpressionStatement(tree_ir.ExpressionStatement node) { | 626 visitExpressionStatement(tree_ir.ExpressionStatement node) { |
| 622 js.Expression exp = visitExpression(node.expression); | 627 js.Expression exp = visitExpression(node.expression); |
| 623 if (node.next is tree_ir.Unreachable && emitUnreachableAsReturn.last) { | 628 if (node.next is tree_ir.Unreachable && emitUnreachableAsReturn.last) { |
| 624 // Emit as 'return exp' to assist local analysis in the VM. | 629 // Emit as 'return exp' to assist local analysis in the VM. |
| 625 accumulator.add(new js.Return(exp)); | 630 accumulator.add(new js.Return(exp)); |
| 631 return null; |
| 626 } else { | 632 } else { |
| 627 accumulator.add(new js.ExpressionStatement(exp)); | 633 accumulator.add(new js.ExpressionStatement(exp)); |
| 628 visitStatement(node.next); | 634 return node.next; |
| 629 } | 635 } |
| 630 } | 636 } |
| 631 | 637 |
| 632 bool isNullReturn(tree_ir.Statement node) { | 638 bool isNullReturn(tree_ir.Statement node) { |
| 633 return node is tree_ir.Return && isNull(node.value); | 639 return node is tree_ir.Return && isNull(node.value); |
| 634 } | 640 } |
| 635 | 641 |
| 636 bool isEndOfMethod(tree_ir.Statement node) { | 642 bool isEndOfMethod(tree_ir.Statement node) { |
| 637 return isNullReturn(node) || | 643 return isNullReturn(node) || |
| 638 node is tree_ir.Break && isNullReturn(node.target.binding.next); | 644 node is tree_ir.Break && isNullReturn(node.target.binding.next); |
| 639 } | 645 } |
| 640 | 646 |
| 641 @override | 647 @override |
| 642 void visitIf(tree_ir.If node) { | 648 visitIf(tree_ir.If node) { |
| 643 js.Expression condition = visitExpression(node.condition); | 649 js.Expression condition = visitExpression(node.condition); |
| 644 int usesBefore = fallthrough.useCount; | 650 int usesBefore = fallthrough.useCount; |
| 645 // Unless the 'else' part ends the method. make sure to terminate any | 651 // Unless the 'else' part ends the method. make sure to terminate any |
| 646 // uncompletable code paths in the 'then' part. | 652 // uncompletable code paths in the 'then' part. |
| 647 emitUnreachableAsReturn.add(!isEndOfMethod(node.elseStatement)); | 653 emitUnreachableAsReturn.add(!isEndOfMethod(node.elseStatement)); |
| 648 js.Statement thenBody = buildBodyStatement(node.thenStatement); | 654 js.Statement thenBody = buildBodyStatement(node.thenStatement); |
| 649 emitUnreachableAsReturn.removeLast(); | 655 emitUnreachableAsReturn.removeLast(); |
| 650 bool thenHasFallthrough = (fallthrough.useCount > usesBefore); | 656 bool thenHasFallthrough = (fallthrough.useCount > usesBefore); |
| 651 if (thenHasFallthrough) { | 657 if (thenHasFallthrough) { |
| 652 js.Statement elseBody = buildBodyStatement(node.elseStatement); | 658 js.Statement elseBody = buildBodyStatement(node.elseStatement); |
| 653 accumulator.add(new js.If(condition, thenBody, elseBody)); | 659 accumulator.add(new js.If(condition, thenBody, elseBody)); |
| 660 return null; |
| 654 } else { | 661 } else { |
| 655 // The 'then' body cannot complete normally, so emit a short 'if' | 662 // The 'then' body cannot complete normally, so emit a short 'if' |
| 656 // and put the 'else' body after it. | 663 // and put the 'else' body after it. |
| 657 accumulator.add(new js.If.noElse(condition, thenBody)); | 664 accumulator.add(new js.If.noElse(condition, thenBody)); |
| 658 visitStatement(node.elseStatement); | 665 return node.elseStatement; |
| 659 } | 666 } |
| 660 } | 667 } |
| 661 | 668 |
| 662 @override | 669 @override |
| 663 void visitLabeledStatement(tree_ir.LabeledStatement node) { | 670 visitLabeledStatement(tree_ir.LabeledStatement node) { |
| 664 fallthrough.push(node.next); | 671 fallthrough.push(node.next); |
| 665 js.Statement body = buildBodyStatement(node.body); | 672 js.Statement body = buildBodyStatement(node.body); |
| 666 fallthrough.pop(); | 673 fallthrough.pop(); |
| 667 accumulator.add(insertLabel(node.label, body)); | 674 accumulator.add(insertLabel(node.label, body)); |
| 668 visitStatement(node.next); | 675 return node.next; |
| 669 } | 676 } |
| 670 | 677 |
| 671 /// Creates a name for [label] if it does not already have one. | 678 /// Creates a name for [label] if it does not already have one. |
| 672 /// | 679 /// |
| 673 /// This also marks the label as being used. | 680 /// This also marks the label as being used. |
| 674 String makeLabel(tree_ir.Label label) { | 681 String makeLabel(tree_ir.Label label) { |
| 675 return labelNames.putIfAbsent(label, () => 'L${labelNames.length}'); | 682 return labelNames.putIfAbsent(label, () => 'L${labelNames.length}'); |
| 676 } | 683 } |
| 677 | 684 |
| 678 /// Wraps a node in a labeled statement unless the label is unused. | 685 /// Wraps a node in a labeled statement unless the label is unused. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 690 if (accumulator.length == 1) { | 697 if (accumulator.length == 1) { |
| 691 return accumulator.single; | 698 return accumulator.single; |
| 692 } | 699 } |
| 693 return new js.Block(accumulator); | 700 return new js.Block(accumulator); |
| 694 } | 701 } |
| 695 | 702 |
| 696 /// Builds a nested statement. | 703 /// Builds a nested statement. |
| 697 js.Statement buildBodyStatement(tree_ir.Statement statement) { | 704 js.Statement buildBodyStatement(tree_ir.Statement statement) { |
| 698 List<js.Statement> savedAccumulator = accumulator; | 705 List<js.Statement> savedAccumulator = accumulator; |
| 699 accumulator = <js.Statement>[]; | 706 accumulator = <js.Statement>[]; |
| 700 visitStatement(statement); | 707 while (statement != null) { |
| 708 statement = visitStatement(statement); |
| 709 } |
| 701 js.Statement result = _bodyAsStatement(); | 710 js.Statement result = _bodyAsStatement(); |
| 702 accumulator = savedAccumulator; | 711 accumulator = savedAccumulator; |
| 703 return result; | 712 return result; |
| 704 } | 713 } |
| 705 | 714 |
| 706 js.Block buildBodyBlock(tree_ir.Statement statement) { | 715 js.Block buildBodyBlock(tree_ir.Statement statement) { |
| 707 List<js.Statement> savedAccumulator = accumulator; | 716 List<js.Statement> savedAccumulator = accumulator; |
| 708 accumulator = <js.Statement>[]; | 717 accumulator = <js.Statement>[]; |
| 709 visitStatement(statement); | 718 while (statement != null) { |
| 719 statement = visitStatement(statement); |
| 720 } |
| 710 js.Statement result = new js.Block(accumulator); | 721 js.Statement result = new js.Block(accumulator); |
| 711 accumulator = savedAccumulator; | 722 accumulator = savedAccumulator; |
| 712 return result; | 723 return result; |
| 713 } | 724 } |
| 714 | 725 |
| 715 js.Expression makeSequence(List<tree_ir.Expression> list) { | 726 js.Expression makeSequence(List<tree_ir.Expression> list) { |
| 716 return list.map(visitExpression).reduce((x,y) => new js.Binary(',', x, y)); | 727 return list.map(visitExpression).reduce((x,y) => new js.Binary(',', x, y)); |
| 717 } | 728 } |
| 718 | 729 |
| 719 @override | 730 @override |
| 720 void visitFor(tree_ir.For node) { | 731 visitFor(tree_ir.For node) { |
| 721 js.Expression condition = visitExpression(node.condition); | 732 js.Expression condition = visitExpression(node.condition); |
| 722 shortBreak.push(node.next); | 733 shortBreak.push(node.next); |
| 723 shortContinue.push(node); | 734 shortContinue.push(node); |
| 724 fallthrough.push(node); | 735 fallthrough.push(node); |
| 725 emitUnreachableAsReturn.add(true); | 736 emitUnreachableAsReturn.add(true); |
| 726 js.Statement body = buildBodyStatement(node.body); | 737 js.Statement body = buildBodyStatement(node.body); |
| 727 emitUnreachableAsReturn.removeLast(); | 738 emitUnreachableAsReturn.removeLast(); |
| 728 fallthrough.pop(); | 739 fallthrough.pop(); |
| 729 shortContinue.pop(); | 740 shortContinue.pop(); |
| 730 shortBreak.pop(); | 741 shortBreak.pop(); |
| 731 js.Statement loopNode; | 742 js.Statement loopNode; |
| 732 if (node.updates.isEmpty) { | 743 if (node.updates.isEmpty) { |
| 733 loopNode = new js.While(condition, body); | 744 loopNode = new js.While(condition, body); |
| 734 } else { // Compile as a for loop. | 745 } else { // Compile as a for loop. |
| 735 js.Expression init; | 746 js.Expression init; |
| 736 if (accumulator.isNotEmpty && | 747 if (accumulator.isNotEmpty && |
| 737 accumulator.last is js.ExpressionStatement) { | 748 accumulator.last is js.ExpressionStatement) { |
| 738 // Take the preceding expression from the accumulator and use | 749 // Take the preceding expression from the accumulator and use |
| 739 // it as the initializer expression. | 750 // it as the initializer expression. |
| 740 js.ExpressionStatement initStmt = accumulator.removeLast(); | 751 js.ExpressionStatement initStmt = accumulator.removeLast(); |
| 741 init = initStmt.expression; | 752 init = initStmt.expression; |
| 742 } | 753 } |
| 743 js.Expression update = makeSequence(node.updates); | 754 js.Expression update = makeSequence(node.updates); |
| 744 loopNode = new js.For(init, condition, update, body); | 755 loopNode = new js.For(init, condition, update, body); |
| 745 } | 756 } |
| 746 accumulator.add(insertLabel(node.label, loopNode)); | 757 accumulator.add(insertLabel(node.label, loopNode)); |
| 747 visitStatement(node.next); | 758 return node.next; |
| 748 } | 759 } |
| 749 | 760 |
| 750 @override | 761 @override |
| 751 void visitWhileTrue(tree_ir.WhileTrue node) { | 762 void visitWhileTrue(tree_ir.WhileTrue node) { |
| 752 // A short break in the while will jump to the current fallthrough target. | 763 // A short break in the while will jump to the current fallthrough target. |
| 753 shortBreak.push(fallthrough.target); | 764 shortBreak.push(fallthrough.target); |
| 754 shortContinue.push(node); | 765 shortContinue.push(node); |
| 755 fallthrough.push(node); | 766 fallthrough.push(node); |
| 756 emitUnreachableAsReturn.add(true); | 767 emitUnreachableAsReturn.add(true); |
| 757 js.Statement jsBody = buildBodyStatement(node.body); | 768 js.Statement jsBody = buildBodyStatement(node.body); |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1008 js.Expression visitForeignExpression(tree_ir.ForeignExpression node) { | 1019 js.Expression visitForeignExpression(tree_ir.ForeignExpression node) { |
| 1009 return handleForeignCode(node); | 1020 return handleForeignCode(node); |
| 1010 } | 1021 } |
| 1011 | 1022 |
| 1012 @override | 1023 @override |
| 1013 void visitForeignStatement(tree_ir.ForeignStatement node) { | 1024 void visitForeignStatement(tree_ir.ForeignStatement node) { |
| 1014 accumulator.add(handleForeignCode(node)); | 1025 accumulator.add(handleForeignCode(node)); |
| 1015 } | 1026 } |
| 1016 | 1027 |
| 1017 @override | 1028 @override |
| 1018 void visitYield(tree_ir.Yield node) { | 1029 visitYield(tree_ir.Yield node) { |
| 1019 js.Expression value = visitExpression(node.input); | 1030 js.Expression value = visitExpression(node.input); |
| 1020 accumulator.add(new js.DartYield(value, node.hasStar)); | 1031 accumulator.add(new js.DartYield(value, node.hasStar)); |
| 1021 visitStatement(node.next); | 1032 return node.next; |
| 1022 } | 1033 } |
| 1023 | 1034 |
| 1024 @override | 1035 @override |
| 1025 void visitNullCheck(tree_ir.NullCheck node) { | 1036 visitNullCheck(tree_ir.NullCheck node) { |
| 1026 js.Expression value = visitExpression(node.value); | 1037 js.Expression value = visitExpression(node.value); |
| 1027 // TODO(sra): Try to use the selector even when [useSelector] is false. The | 1038 // TODO(sra): Try to use the selector even when [useSelector] is false. The |
| 1028 // reason we use 'toString' is that it is always defined so avoids a slow | 1039 // reason we use 'toString' is that it is always defined so avoids a slow |
| 1029 // lookup (in V8) of an absent property. We could use the property for the | 1040 // lookup (in V8) of an absent property. We could use the property for the |
| 1030 // selector if we knew it was present. The property is present if the | 1041 // selector if we knew it was present. The property is present if the |
| 1031 // associated method was not inlined away, or if there is a noSuchMethod | 1042 // associated method was not inlined away, or if there is a noSuchMethod |
| 1032 // hook for that selector. We don't know these things here, but the decision | 1043 // hook for that selector. We don't know these things here, but the decision |
| 1033 // could be deferred by creating a deferred property that was resolved after | 1044 // could be deferred by creating a deferred property that was resolved after |
| 1034 // codegen. | 1045 // codegen. |
| 1035 js.Expression access = node.selector != null && node.useSelector | 1046 js.Expression access = node.selector != null && node.useSelector |
| 1036 ? js.js('#.#', [value, glue.invocationName(node.selector)]) | 1047 ? js.js('#.#', [value, glue.invocationName(node.selector)]) |
| 1037 : js.js('#.toString', [value]); | 1048 : js.js('#.toString', [value]); |
| 1038 if (node.condition != null) { | 1049 if (node.condition != null) { |
| 1039 js.Expression condition = visitExpression(node.condition); | 1050 js.Expression condition = visitExpression(node.condition); |
| 1040 js.Statement body = isNullReturn(node.next) | 1051 js.Statement body = isNullReturn(node.next) |
| 1041 ? new js.ExpressionStatement(access) | 1052 ? new js.ExpressionStatement(access) |
| 1042 : new js.Return(access); | 1053 : new js.Return(access); |
| 1043 accumulator.add(new js.If.noElse(condition, body)); | 1054 accumulator.add(new js.If.noElse(condition, body)); |
| 1044 } else { | 1055 } else { |
| 1045 accumulator.add(new js.ExpressionStatement(access)); | 1056 accumulator.add(new js.ExpressionStatement(access)); |
| 1046 } | 1057 } |
| 1047 visitStatement(node.next); | 1058 return node.next; |
| 1048 } | 1059 } |
| 1049 | 1060 |
| 1050 @override | 1061 @override |
| 1051 js.Expression visitApplyBuiltinOperator(tree_ir.ApplyBuiltinOperator node) { | 1062 js.Expression visitApplyBuiltinOperator(tree_ir.ApplyBuiltinOperator node) { |
| 1052 List<js.Expression> args = visitExpressionList(node.arguments); | 1063 List<js.Expression> args = visitExpressionList(node.arguments); |
| 1053 switch (node.operator) { | 1064 switch (node.operator) { |
| 1054 case BuiltinOperator.NumAdd: | 1065 case BuiltinOperator.NumAdd: |
| 1055 return new js.Binary('+', args[0], args[1]); | 1066 return new js.Binary('+', args[0], args[1]); |
| 1056 case BuiltinOperator.NumSubtract: | 1067 case BuiltinOperator.NumSubtract: |
| 1057 return new js.Binary('-', args[0], args[1]); | 1068 return new js.Binary('-', args[0], args[1]); |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1225 void registerDefaultParameterValues(ExecutableElement element) { | 1236 void registerDefaultParameterValues(ExecutableElement element) { |
| 1226 if (element is! FunctionElement) return; | 1237 if (element is! FunctionElement) return; |
| 1227 FunctionElement function = element; | 1238 FunctionElement function = element; |
| 1228 if (function.isStatic) return; // Defaults are inlined at call sites. | 1239 if (function.isStatic) return; // Defaults are inlined at call sites. |
| 1229 function.functionSignature.forEachOptionalParameter((param) { | 1240 function.functionSignature.forEachOptionalParameter((param) { |
| 1230 ConstantValue constant = glue.getDefaultParameterValue(param); | 1241 ConstantValue constant = glue.getDefaultParameterValue(param); |
| 1231 registry.registerCompileTimeConstant(constant); | 1242 registry.registerCompileTimeConstant(constant); |
| 1232 }); | 1243 }); |
| 1233 } | 1244 } |
| 1234 } | 1245 } |
| OLD | NEW |