Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 rewrite_async; | 5 library rewrite_async; |
| 6 | 6 |
| 7 // TODO(sigurdm): Move the try/catch expression to a js_helper function. | |
| 8 // That would also simplify the sync* case, where the error can just be thrown. | |
| 9 | |
| 10 import "dart:math" show max; | 7 import "dart:math" show max; |
| 11 import 'dart:collection'; | 8 import 'dart:collection'; |
| 12 | 9 |
| 13 import 'package:_internal/compiler/js_lib/shared/async_await_error_codes.dart' | 10 import 'package:_internal/compiler/js_lib/shared/async_await_error_codes.dart' |
| 14 as error_codes; | 11 as error_codes; |
| 15 | 12 |
| 16 import "js.dart" as js; | 13 import "js.dart" as js; |
| 17 | 14 |
| 18 import '../util/util.dart'; | 15 import '../util/util.dart'; |
| 19 import '../dart2jslib.dart' show DiagnosticListener; | 16 import '../dart2jslib.dart' show DiagnosticListener; |
| (...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 694 /// | 691 /// |
| 695 /// function (x, y, z) async { | 692 /// function (x, y, z) async { |
| 696 /// var p = await foo(); | 693 /// var p = await foo(); |
| 697 /// return bar(p); | 694 /// return bar(p); |
| 698 /// } | 695 /// } |
| 699 /// | 696 /// |
| 700 /// Becomes (without error handling): | 697 /// Becomes (without error handling): |
| 701 /// | 698 /// |
| 702 /// function(x, y, z) { | 699 /// function(x, y, z) { |
| 703 /// var goto = 0, returnValue, completer = new Completer(), p; | 700 /// var goto = 0, returnValue, completer = new Completer(), p; |
| 704 /// function helper(result) { | 701 /// function body(result) { |
| 705 /// while (true) { | 702 /// while (true) { |
| 706 /// switch (goto) { | 703 /// switch (goto) { |
| 707 /// case 0: | 704 /// case 0: |
| 708 /// goto = 1 // Remember where to continue when the future succeeds. | 705 /// goto = 1 // Remember where to continue when the future succeeds. |
| 709 /// return thenHelper(foo(), helper, completer); | 706 /// return thenHelper(foo(), helper, completer); |
| 710 /// case 1: | 707 /// case 1: |
| 711 /// p = result; | 708 /// p = result; |
| 712 /// returnValue = bar(p); | 709 /// returnValue = bar(p); |
| 713 /// goto = 2; | 710 /// goto = 2; |
| 714 /// break; | 711 /// break; |
| 715 /// case 2: | 712 /// case 2: |
| 716 /// return thenHelper(returnValue, null, completer) | 713 /// return thenHelper(returnValue, null, completer) |
| 717 /// } | 714 /// } |
| 718 /// } | 715 /// } |
| 719 /// return thenHelper(null, helper, completer); | 716 /// return thenHelper(null, helper, completer); |
| 720 /// } | 717 /// } |
| 721 /// } | 718 /// } |
| 722 /// | 719 /// |
| 723 /// Try/catch is implemented by maintaining [handlerName] to contain the label | 720 /// Try/catch is implemented by maintaining [handlerName] to contain the label |
| 724 /// of the current handler. The switch is nested inside a try/catch that will | 721 /// of the current handler. If [bodyName] throws, the caller should catch the |
| 725 /// redirect the flow to the current handler. | 722 /// error and recall [bodyName] with first argument [error_codes.ERROR] and |
| 723 /// second argument the error. | |
| 726 /// | 724 /// |
| 727 /// A `finally` clause is compiled similar to normal code, with the additional | 725 /// A `finally` clause is compiled similar to normal code, with the additional |
| 728 /// complexity that `finally` clauses need to know where to jump to after the | 726 /// complexity that `finally` clauses need to know where to jump to after the |
| 729 /// clause is done. In the translation, each flow-path that enters a `finally` | 727 /// clause is done. In the translation, each flow-path that enters a `finally` |
| 730 /// sets up the variable [nextName] with a stack of finally-blocks and a final | 728 /// sets up the variable [nextName] with a stack of finally-blocks and a final |
| 731 /// jump-target (exit, catch, ...). | 729 /// jump-target (exit, catch, ...). |
| 732 /// | 730 /// |
| 733 /// function (x, y, z) async { | 731 /// function(x, y, z) async { |
| 734 /// try { | 732 /// try { |
| 735 /// try { | 733 /// try { |
| 736 /// throw "error"; | 734 /// throw "error"; |
| 737 /// } finally { | 735 /// } finally { |
| 738 /// finalize1(); | 736 /// finalize1(); |
| 739 /// } | 737 /// } |
| 740 /// } catch (e) { | 738 /// } catch (e) { |
| 741 /// handle(e); | 739 /// handle(e); |
| 742 /// } finally { | 740 /// } finally { |
| 743 /// finalize2(); | 741 /// finalize2(); |
| 744 /// } | 742 /// } |
| 745 /// } | 743 /// } |
| 746 /// | 744 /// |
| 747 /// Translates into (besides the fact that structures not containing | 745 /// Translates into (besides the fact that structures not containing |
| 748 /// await/yield/yield* are left intact): | 746 /// await/yield/yield* are left intact): |
| 749 /// | 747 /// |
| 750 /// function(x, y, z) { | 748 /// function(x, y, z) { |
| 751 /// var goto = 0; | 749 /// var goto = 0; |
| 752 /// var returnValue; | 750 /// var returnValue; |
| 753 /// var completer = new Completer(); | 751 /// var completer = new Completer(); |
| 754 /// var handler = 8; // Outside try-blocks go to the rethrow label. | 752 /// var handler = 8; // Outside try-blocks go to the rethrow label. |
| 755 /// var p; | 753 /// var p; |
| 756 /// var storedError; | 754 /// var currentError; |
| 757 /// // The result can be either the result of an awaited future, or an | 755 /// // The result can be either the result of an awaited future, or an |
| 758 /// // error if the future completed with an error. | 756 /// // error if the future completed with an error. |
| 759 /// function helper(errorCode, result) { | 757 /// function body(errorCode, result) { |
| 760 /// if (errorCode == 1) { | 758 /// if (errorCode == 1) { |
| 761 /// storedError = result; | 759 /// currentError = result; |
| 762 /// goto = handler; | 760 /// goto = handler; |
| 763 /// } | 761 /// } |
| 764 /// while (true) { | 762 /// while (true) { |
| 765 /// try { | 763 /// switch (goto) { |
| 766 /// switch (goto) { | 764 /// case 0: |
| 767 /// case 0: | 765 /// handler = 4; // The outer catch-handler |
| 768 /// handler = 4; // The outer catch-handler | 766 /// handler = 1; // The inner (implicit) catch-handler |
| 769 /// handler = 1; // The inner (implicit) catch-handler | 767 /// throw "error"; |
| 770 /// throw "error"; | 768 /// next = [3]; |
| 771 /// next = [3]; | 769 /// // After the finally (2) continue normally after the try. |
| 772 /// // After the finally (2) continue normally after the try. | 770 /// goto = 2; |
| 773 /// goto = 2; | 771 /// break; |
| 774 /// break; | 772 /// case 1: // (implicit) catch handler for inner try. |
| 775 /// case 1: // (implicit) catch handler for inner try. | 773 /// next = [3]; // destination after the finally. |
| 776 /// next = [3]; // destination after the finally. | 774 /// // fall-though to the finally handler. |
| 777 /// // fall-though to the finally handler. | 775 /// case 2: // finally for inner try |
| 778 /// case 2: // finally for inner try | 776 /// handler = 4; // catch-handler for outer try. |
| 779 /// handler = 4; // catch-handler for outer try. | 777 /// finalize1(); |
| 780 /// finalize1(); | 778 /// goto = next.pop(); |
| 781 /// goto = next.pop(); | 779 /// break; |
| 782 /// break; | 780 /// case 3: // exiting inner try. |
| 783 /// case 3: // exiting inner try. | 781 /// next = [6]; |
| 784 /// next = [6]; | 782 /// goto = 5; // finally handler for outer try. |
| 785 /// goto = 5; // finally handler for outer try. | 783 /// break; |
| 786 /// break; | 784 /// case 4: // catch handler for outer try. |
| 787 /// case 4: // catch handler for outer try. | 785 /// handler = 5; // If the handler throws, do the finally .. |
| 788 /// handler = 5; // If the handler throws, do the finally .. | 786 /// next = [8] // ... and rethrow. |
| 789 /// next = [8] // ... and rethrow. | 787 /// e = storedError; |
| 790 /// e = storedError; | 788 /// handle(e); |
| 791 /// handle(e); | 789 /// // Fall through to finally. |
| 792 /// // Fall through to finally. | 790 /// case 5: // finally handler for outer try. |
| 793 /// case 5: // finally handler for outer try. | 791 /// handler = null; |
| 794 /// handler = null; | 792 /// finalize2(); |
| 795 /// finalize2(); | 793 /// goto = next.pop(); |
| 796 /// goto = next.pop(); | 794 /// break; |
| 797 /// break; | 795 /// case 6: // Exiting outer try. |
| 798 /// case 6: // Exiting outer try. | 796 /// case 7: // return |
| 799 /// case 7: // return | 797 /// return thenHelper(returnValue, 0, completer); |
| 800 /// return thenHelper(returnValue, 0, completer); | 798 /// case 8: // Rethrow |
| 801 /// case 8: // Rethrow | 799 /// return thenHelper(currentError, 1, completer); |
| 802 /// return thenHelper(storedError, 1, completer); | |
| 803 /// } | |
| 804 /// } catch (error) { | |
| 805 /// storedError = error; | |
| 806 /// goto = handler; | |
| 807 /// } | 800 /// } |
| 808 /// } | 801 /// } |
| 809 /// return thenHelper(null, helper, completer); | 802 /// return thenHelper(null, helper, completer); |
| 810 /// } | 803 /// } |
| 811 /// } | 804 /// } |
| 812 /// | 805 /// |
| 813 @override | 806 @override |
| 814 js.Expression visitFun(js.Fun node) { | 807 js.Expression visitFun(js.Fun node) { |
| 815 if (isSync) return node; | 808 if (isSync) return node; |
| 816 | 809 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 828 addExit(); | 821 addExit(); |
| 829 | 822 |
| 830 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { | 823 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { |
| 831 return new js.Case(js.number(label), new js.Block(labelledParts[label])); | 824 return new js.Case(js.number(label), new js.Block(labelledParts[label])); |
| 832 }).toList(); | 825 }).toList(); |
| 833 js.Statement helperBody = | 826 js.Statement helperBody = |
| 834 new js.Switch(new js.VariableUse(gotoName), clauses); | 827 new js.Switch(new js.VariableUse(gotoName), clauses); |
| 835 if (hasJumpThoughOuterLabel) { | 828 if (hasJumpThoughOuterLabel) { |
| 836 helperBody = new js.LabeledStatement(outerLabelName, helperBody); | 829 helperBody = new js.LabeledStatement(outerLabelName, helperBody); |
| 837 } | 830 } |
| 838 helperBody = js.js.statement(""" | 831 |
| 839 try { | |
| 840 #body | |
| 841 } catch (#error){ | |
| 842 #currentError = #error; | |
| 843 #goto = #handler; | |
| 844 }""", { | |
| 845 "body": helperBody, | |
| 846 "goto": gotoName, | |
| 847 "error": errorName, | |
| 848 "currentError": currentErrorName, | |
| 849 "handler": handlerName, | |
| 850 }); | |
| 851 List<js.VariableInitialization> inits = <js.VariableInitialization>[]; | 832 List<js.VariableInitialization> inits = <js.VariableInitialization>[]; |
| 852 | 833 |
| 853 js.VariableInitialization makeInit(String name, js.Expression initValue) { | 834 js.VariableInitialization makeInit(String name, js.Expression initValue) { |
| 854 return new js.VariableInitialization( | 835 return new js.VariableInitialization( |
| 855 new js.VariableDeclaration(name), initValue); | 836 new js.VariableDeclaration(name), initValue); |
| 856 } | 837 } |
| 857 | 838 |
| 858 inits.add(makeInit(gotoName, js.number(0))); | 839 inits.add(makeInit(gotoName, js.number(0))); |
| 859 if (isAsync) { | 840 if (isAsync) { |
| 860 inits.add(makeInit(completerName, new js.New(newCompleter, []))); | 841 inits.add(makeInit(completerName, new js.New(newCompleter, []))); |
| 861 } else if (isAsyncStar) { | 842 } else if (isAsyncStar) { |
| 862 inits.add(makeInit(controllerName, | 843 inits.add(makeInit(controllerName, |
| 863 new js.Call(newController, [new js.VariableUse(bodyName)]))); | 844 new js.Call(newController, [new js.VariableUse(bodyName)]))); |
|
sra1
2015/02/19 20:55:02
new js.Call... -> js('#(#)', [newController, bo
sigurdm
2015/02/23 14:58:37
Done.
| |
| 864 } | 845 } |
| 865 inits.add(makeInit(handlerName, js.number(rethrowLabel))); | 846 inits.add(makeInit(handlerName, js.number(rethrowLabel))); |
| 866 inits.add(makeInit(currentErrorName, null)); | 847 inits.add(makeInit(currentErrorName, null)); |
| 867 if (hasJumpThroughFinally || analysis.hasYield) { | 848 if (hasJumpThroughFinally || analysis.hasYield) { |
| 868 inits.add(makeInit(nextName, null)); | 849 inits.add(makeInit(nextName, null)); |
| 869 } | 850 } |
| 870 if (analysis.hasExplicitReturns && isAsync) { | 851 if (analysis.hasExplicitReturns && isAsync) { |
| 871 inits.add(makeInit(returnValueName, null)); | 852 inits.add(makeInit(returnValueName, null)); |
| 872 } | 853 } |
| 873 if (isSyncStar) { | |
| 874 inits.add(makeInit(resultName, null)); | |
| 875 } | |
| 876 if (analysis.hasThis && !isSyncStar) { | 854 if (analysis.hasThis && !isSyncStar) { |
| 877 // Sync* functions must remember `this` on the level of the outer | 855 // Sync* functions must remember `this` on the level of the outer |
| 878 // function. | 856 // function. |
| 879 inits.add(makeInit(selfName, new js.This())); | 857 inits.add(makeInit(selfName, new js.This())); |
|
sra1
2015/02/19 20:55:02
new js.This() --> js('this')
sigurdm
2015/02/23 14:58:37
Done.
| |
| 880 } | 858 } |
| 881 inits.addAll(localVariables.map((js.VariableDeclaration decl) { | 859 inits.addAll(localVariables.map((js.VariableDeclaration decl) { |
| 882 return new js.VariableInitialization(decl, null); | 860 return new js.VariableInitialization(decl, null); |
| 883 })); | 861 })); |
| 884 inits.addAll(new Iterable.generate(tempVarHighWaterMark, | 862 inits.addAll(new Iterable.generate(tempVarHighWaterMark, |
| 885 (int i) => makeInit(useTempVar(i + 1).name, null))); | 863 (int i) => makeInit(useTempVar(i + 1).name, null))); |
| 886 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits); | 864 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits); |
| 887 // TODO(sigurdm): Explain the difference between these cases. | 865 // TODO(sigurdm): Explain the difference between these cases. |
| 888 if (isSyncStar) { | 866 if (isSyncStar) { |
| 889 return js.js(""" | 867 return js.js(""" |
| 890 function (#params) { | 868 function (#params) { |
| 891 if (#needsThis) | 869 if (#needsThis) |
| 892 var #self = this; | 870 var #self = this; |
| 893 return new #newIterable(function () { | 871 return new #newIterable(function () { |
| 894 #varDecl; | 872 #varDecl; |
| 895 return function #body() { | 873 return function #body(#errorCode, #result) { |
| 874 if (#errorCode == #ERROR) { | |
|
sra1
2015/02/19 20:55:02
== --> ===
sigurdm
2015/02/23 14:58:37
Done.
| |
| 875 #currentError = #result; | |
| 876 #goto = #handler; | |
| 877 } | |
| 896 while (true) | 878 while (true) |
| 897 #helperBody; | 879 #helperBody; |
| 898 }; | 880 }; |
| 899 }); | 881 }); |
| 900 } | 882 } |
| 901 """, { | 883 """, { |
| 902 "params": node.params, | 884 "params": node.params, |
| 903 "needsThis": analysis.hasThis, | 885 "needsThis": analysis.hasThis, |
| 904 "helperBody": helperBody, | 886 "helperBody": helperBody, |
| 905 "varDecl": varDecl, | 887 "varDecl": varDecl, |
| 888 "errorCode": errorCodeName, | |
| 906 "newIterable": newIterable, | 889 "newIterable": newIterable, |
| 907 "body": bodyName, | 890 "body": bodyName, |
| 908 "self": selfName, | 891 "self": selfName, |
| 892 "result": resultName, | |
| 893 "goto": gotoName, | |
| 894 "handler": handlerName, | |
| 895 "currentError": currentErrorName, | |
| 896 "ERROR": js.number(error_codes.ERROR), | |
| 909 }); | 897 }); |
| 910 } | 898 } |
| 911 return js.js(""" | 899 return js.js(""" |
| 912 function (#params) { | 900 function (#params) { |
| 913 #varDecl; | 901 #varDecl; |
| 914 function #bodyName(#errorCode, #result) { | 902 function #bodyName(#errorCode, #result) { |
| 915 if (#hasYield) | 903 if (#hasYield) |
| 916 switch (#errorCode) { | 904 switch (#errorCode) { |
| 917 case #STREAM_WAS_CANCELED: | 905 case #STREAM_WAS_CANCELED: |
| 918 #next = #nextWhenCanceled; | 906 #next = #nextWhenCanceled; |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1111 return withExpression(node.condition, (js.Expression condition) { | 1099 return withExpression(node.condition, (js.Expression condition) { |
| 1112 return new js.Conditional(condition, node.then, node.otherwise); | 1100 return new js.Conditional(condition, node.then, node.otherwise); |
| 1113 }); | 1101 }); |
| 1114 } | 1102 } |
| 1115 int thenLabel = newLabel("then"); | 1103 int thenLabel = newLabel("then"); |
| 1116 int joinLabel = newLabel("join"); | 1104 int joinLabel = newLabel("join"); |
| 1117 int elseLabel = newLabel("else"); | 1105 int elseLabel = newLabel("else"); |
| 1118 withExpression(node.condition, (js.Expression condition) { | 1106 withExpression(node.condition, (js.Expression condition) { |
| 1119 addExpressionStatement(new js.Assignment(new js.VariableUse(gotoName), | 1107 addExpressionStatement(new js.Assignment(new js.VariableUse(gotoName), |
| 1120 new js.Conditional( | 1108 new js.Conditional( |
| 1121 condition, js.number(thenLabel), js.number(elseLabel)))); | 1109 condition, js.number(thenLabel), js.number(elseLabel)))); |
|
sra1
2015/02/19 20:55:02
js.statement('# = # ? # : #',
gotoName, conditio
sigurdm
2015/02/23 14:58:37
Done.
| |
| 1122 }, store: false); | 1110 }, store: false); |
| 1123 addBreak(); | 1111 addBreak(); |
| 1124 beginLabel(thenLabel); | 1112 beginLabel(thenLabel); |
| 1125 withExpression(node.then, (js.Expression value) { | 1113 withExpression(node.then, (js.Expression value) { |
| 1126 if (!isResult(value)) { | 1114 if (!isResult(value)) { |
| 1127 addExpressionStatement( | 1115 addExpressionStatement( |
| 1128 new js.Assignment(new js.VariableUse(resultName), value)); | 1116 new js.Assignment(new js.VariableUse(resultName), value)); |
| 1129 } | 1117 } |
| 1130 }, store: false); | 1118 }, store: false); |
| 1131 addGoto(joinLabel); | 1119 addGoto(joinLabel); |
| (...skipping 1175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2307 return condition || body; | 2295 return condition || body; |
| 2308 } | 2296 } |
| 2309 | 2297 |
| 2310 @override | 2298 @override |
| 2311 bool visitDartYield(js.DartYield node) { | 2299 bool visitDartYield(js.DartYield node) { |
| 2312 hasYield = true; | 2300 hasYield = true; |
| 2313 visit(node.expression); | 2301 visit(node.expression); |
| 2314 return true; | 2302 return true; |
| 2315 } | 2303 } |
| 2316 } | 2304 } |
| OLD | NEW |