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 import 'dart:collection'; | 7 import 'dart:collection'; |
8 import "dart:math" show max; | 8 import 'dart:math' show max; |
9 | 9 |
10 import 'package:js_runtime/shared/async_await_error_codes.dart' as error_codes; | 10 import 'package:js_runtime/shared/async_await_error_codes.dart' as error_codes; |
11 | 11 |
12 import '../common.dart'; | 12 import '../common.dart'; |
| 13 import '../io/source_information.dart' show SourceInformation; |
13 import '../util/util.dart' show Pair; | 14 import '../util/util.dart' show Pair; |
14 import "js.dart" as js; | 15 import 'js.dart' as js; |
15 | 16 |
16 /// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield | 17 /// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield |
17 /// (with dart-like semantics) to an equivalent function without these. | 18 /// (with dart-like semantics) to an equivalent function without these. |
18 /// await-for is not handled and must be rewritten before. (Currently handled | 19 /// await-for is not handled and must be rewritten before. (Currently handled |
19 /// in ssa/builder.dart). | 20 /// in ssa/builder.dart). |
20 /// | 21 /// |
21 /// When generating the input to this, special care must be taken that | 22 /// When generating the input to this, special care must be taken that |
22 /// parameters to sync* functions that are mutated in the body must be boxed. | 23 /// parameters to sync* functions that are mutated in the body must be boxed. |
23 /// (Currently handled in closure.dart). | 24 /// (Currently handled in closure.dart). |
24 /// | 25 /// |
(...skipping 487 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
512 | 513 |
513 void addFunctionExits() { | 514 void addFunctionExits() { |
514 addSuccesExit(); | 515 addSuccesExit(); |
515 addErrorExit(); | 516 addErrorExit(); |
516 } | 517 } |
517 | 518 |
518 /// Returns the rewritten function. | 519 /// Returns the rewritten function. |
519 js.Fun finishFunction( | 520 js.Fun finishFunction( |
520 List<js.Parameter> parameters, | 521 List<js.Parameter> parameters, |
521 js.Statement rewrittenBody, | 522 js.Statement rewrittenBody, |
522 js.VariableDeclarationList variableDeclarations); | 523 js.VariableDeclarationList variableDeclarations, |
| 524 SourceInformation sourceInformation); |
523 | 525 |
524 Iterable<js.VariableInitialization> variableInitializations(); | 526 Iterable<js.VariableInitialization> variableInitializations(); |
525 | 527 |
526 /// Rewrites an async/sync*/async* function to a normal Javascript function. | 528 /// Rewrites an async/sync*/async* function to a normal Javascript function. |
527 /// | 529 /// |
528 /// The control flow is flattened by simulating 'goto' using a switch in a | 530 /// The control flow is flattened by simulating 'goto' using a switch in a |
529 /// loop and a state variable [goto] inside a nested function [body] | 531 /// loop and a state variable [goto] inside a nested function [body] |
530 /// that can be called back by [asyncStarHelper]/[asyncStarHelper]/the | 532 /// that can be called back by [asyncStarHelper]/[asyncStarHelper]/the |
531 /// [Iterator]. | 533 /// [Iterator]. |
532 /// | 534 /// |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
698 variables.add(_makeVariableInitializer(self, js.js('this'))); | 700 variables.add(_makeVariableInitializer(self, js.js('this'))); |
699 } | 701 } |
700 variables.addAll(localVariables.map((js.VariableDeclaration declaration) { | 702 variables.addAll(localVariables.map((js.VariableDeclaration declaration) { |
701 return new js.VariableInitialization(declaration, null); | 703 return new js.VariableInitialization(declaration, null); |
702 })); | 704 })); |
703 variables.addAll(new Iterable.generate(tempVarHighWaterMark, | 705 variables.addAll(new Iterable.generate(tempVarHighWaterMark, |
704 (int i) => _makeVariableInitializer(useTempVar(i + 1).name, null))); | 706 (int i) => _makeVariableInitializer(useTempVar(i + 1).name, null))); |
705 js.VariableDeclarationList variableDeclarations = | 707 js.VariableDeclarationList variableDeclarations = |
706 new js.VariableDeclarationList(variables); | 708 new js.VariableDeclarationList(variables); |
707 | 709 |
708 return finishFunction(node.params, rewrittenBody, variableDeclarations); | 710 return finishFunction(node.params, rewrittenBody, variableDeclarations, |
| 711 node.sourceInformation); |
709 } | 712 } |
710 | 713 |
711 @override | 714 @override |
712 js.Expression visitFun(js.Fun node) { | 715 js.Expression visitFun(js.Fun node) { |
713 if (node.asyncModifier.isAsync || node.asyncModifier.isYielding) { | 716 if (node.asyncModifier.isAsync || node.asyncModifier.isYielding) { |
714 // The translation does not handle nested functions that are generators | 717 // The translation does not handle nested functions that are generators |
715 // or asynchronous. These functions should only be ones that are | 718 // or asynchronous. These functions should only be ones that are |
716 // introduced by JS foreign code from our own libraries. | 719 // introduced by JS foreign code from our own libraries. |
717 reporter.internalError( | 720 reporter.internalError( |
718 spannable, 'Nested function is a generator or asynchronous.'); | 721 spannable, 'Nested function is a generator or asynchronous.'); |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
844 return; | 847 return; |
845 } | 848 } |
846 translateJump(target, breakLabels[target]); | 849 translateJump(target, breakLabels[target]); |
847 } | 850 } |
848 | 851 |
849 @override | 852 @override |
850 js.Expression visitCall(js.Call node) { | 853 js.Expression visitCall(js.Call node) { |
851 bool storeTarget = node.arguments.any(shouldTransform); | 854 bool storeTarget = node.arguments.any(shouldTransform); |
852 return withCallTargetExpression(node.target, (target) { | 855 return withCallTargetExpression(node.target, (target) { |
853 return withExpressions(node.arguments, (List<js.Expression> arguments) { | 856 return withExpressions(node.arguments, (List<js.Expression> arguments) { |
854 return new js.Call(target, arguments); | 857 return new js.Call(target, arguments) |
| 858 .withSourceInformation(node.sourceInformation); |
855 }); | 859 }); |
856 }, store: storeTarget); | 860 }, store: storeTarget); |
857 } | 861 } |
858 | 862 |
859 @override | 863 @override |
860 void visitCase(js.Case node) { | 864 void visitCase(js.Case node) { |
861 return unreachable(node); | 865 return unreachable(node); |
862 } | 866 } |
863 | 867 |
864 @override | 868 @override |
(...skipping 533 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1398 } | 1402 } |
1399 | 1403 |
1400 @override | 1404 @override |
1401 js.Expression visitThis(js.This node) { | 1405 js.Expression visitThis(js.This node) { |
1402 return self; | 1406 return self; |
1403 } | 1407 } |
1404 | 1408 |
1405 @override | 1409 @override |
1406 void visitThrow(js.Throw node) { | 1410 void visitThrow(js.Throw node) { |
1407 withExpression(node.expression, (js.Expression expression) { | 1411 withExpression(node.expression, (js.Expression expression) { |
1408 addStatement(new js.Throw(expression)); | 1412 addStatement(new js.Throw(expression) |
| 1413 .withSourceInformation(node.sourceInformation)); |
1409 }, store: false); | 1414 }, store: false); |
1410 } | 1415 } |
1411 | 1416 |
1412 setErrorHandler([int errorHandler]) { | 1417 setErrorHandler([int errorHandler]) { |
1413 js.Expression label = | 1418 js.Expression label = |
1414 (errorHandler == null) ? currentErrorHandler : js.number(errorHandler); | 1419 (errorHandler == null) ? currentErrorHandler : js.number(errorHandler); |
1415 addStatement(js.js.statement('# = #;', [handler, label])); | 1420 addStatement(js.js.statement('# = #;', [handler, label])); |
1416 } | 1421 } |
1417 | 1422 |
1418 List<int> _finalliesUpToAndEnclosingHandler() { | 1423 List<int> _finalliesUpToAndEnclosingHandler() { |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1705 /// Returning from an async method calls [asyncStarHelper] with the result. | 1710 /// Returning from an async method calls [asyncStarHelper] with the result. |
1706 /// (the result might have been stored in [returnValue] by some finally | 1711 /// (the result might have been stored in [returnValue] by some finally |
1707 /// block). | 1712 /// block). |
1708 void addSuccesExit() { | 1713 void addSuccesExit() { |
1709 if (analysis.hasExplicitReturns) { | 1714 if (analysis.hasExplicitReturns) { |
1710 beginLabel(exitLabel); | 1715 beginLabel(exitLabel); |
1711 } else { | 1716 } else { |
1712 addStatement(new js.Comment("implicit return")); | 1717 addStatement(new js.Comment("implicit return")); |
1713 } | 1718 } |
1714 addStatement(js.js.statement( | 1719 addStatement(js.js.statement( |
1715 "return #runtimeHelper(#returnValue, #successCode, #completer);", | 1720 "return #runtimeHelper(#returnValue, #successCode, #completer);", { |
1716 { | 1721 "runtimeHelper": asyncHelper, |
1717 "runtimeHelper": asyncHelper, | 1722 "successCode": js.number(error_codes.SUCCESS), |
1718 "successCode": js.number(error_codes.SUCCESS), | 1723 "returnValue": |
1719 "returnValue": | 1724 analysis.hasExplicitReturns ? returnValue : new js.LiteralNull(), |
1720 analysis.hasExplicitReturns ? returnValue : new js.LiteralNull(), | 1725 "completer": completer |
1721 "completer": completer | 1726 })); |
1722 })); | |
1723 } | 1727 } |
1724 | 1728 |
1725 @override | 1729 @override |
1726 Iterable<js.VariableInitialization> variableInitializations() { | 1730 Iterable<js.VariableInitialization> variableInitializations() { |
1727 List<js.VariableInitialization> variables = | 1731 List<js.VariableInitialization> variables = |
1728 new List<js.VariableInitialization>(); | 1732 new List<js.VariableInitialization>(); |
1729 variables | 1733 variables |
1730 .add(_makeVariableInitializer(completer, new js.New(newCompleter, []))); | 1734 .add(_makeVariableInitializer(completer, new js.New(newCompleter, []))); |
1731 if (analysis.hasExplicitReturns) { | 1735 if (analysis.hasExplicitReturns) { |
1732 variables.add(_makeVariableInitializer(returnValue, null)); | 1736 variables.add(_makeVariableInitializer(returnValue, null)); |
(...skipping 19 matching lines...) Expand all Loading... |
1752 "value": value, | 1756 "value": value, |
1753 "bodyName": bodyName, | 1757 "bodyName": bodyName, |
1754 "completer": completer | 1758 "completer": completer |
1755 }); | 1759 }); |
1756 } | 1760 } |
1757 | 1761 |
1758 @override | 1762 @override |
1759 js.Fun finishFunction( | 1763 js.Fun finishFunction( |
1760 List<js.Parameter> parameters, | 1764 List<js.Parameter> parameters, |
1761 js.Statement rewrittenBody, | 1765 js.Statement rewrittenBody, |
1762 js.VariableDeclarationList variableDeclarations) { | 1766 js.VariableDeclarationList variableDeclarations, |
| 1767 SourceInformation sourceInformation) { |
1763 return js.js( | 1768 return js.js( |
1764 """ | 1769 """ |
1765 function (#parameters) { | 1770 function (#parameters) { |
1766 #variableDeclarations; | 1771 #variableDeclarations; |
1767 var #bodyName = #wrapBody(function (#errorCode, #result) { | 1772 var #bodyName = #wrapBody(function (#errorCode, #result) { |
1768 if (#errorCode === #ERROR) { | 1773 if (#errorCode === #ERROR) { |
1769 #currentError = #result; | 1774 #currentError = #result; |
1770 #goto = #handler; | 1775 #goto = #handler; |
1771 } | 1776 } |
1772 #rewrittenBody; | 1777 #rewrittenBody; |
1773 }); | 1778 }); |
1774 return #asyncHelper(null, #bodyName, #completer); | 1779 return #asyncHelper(null, #bodyName, #completer); |
1775 }""", | 1780 }""", |
1776 { | 1781 { |
1777 "parameters": parameters, | 1782 "parameters": parameters, |
1778 "variableDeclarations": variableDeclarations, | 1783 "variableDeclarations": variableDeclarations, |
1779 "ERROR": js.number(error_codes.ERROR), | 1784 "ERROR": js.number(error_codes.ERROR), |
1780 "rewrittenBody": rewrittenBody, | 1785 "rewrittenBody": rewrittenBody, |
1781 "bodyName": bodyName, | 1786 "bodyName": bodyName, |
1782 "currentError": currentError, | 1787 "currentError": currentError, |
1783 "goto": goto, | 1788 "goto": goto, |
1784 "handler": handler, | 1789 "handler": handler, |
1785 "errorCode": errorCodeName, | 1790 "errorCode": errorCodeName, |
1786 "result": resultName, | 1791 "result": resultName, |
1787 "asyncHelper": asyncHelper, | 1792 "asyncHelper": asyncHelper, |
1788 "completer": completer, | 1793 "completer": completer, |
1789 "wrapBody": wrapBody, | 1794 "wrapBody": wrapBody, |
1790 }); | 1795 }).withSourceInformation(sourceInformation); |
1791 } | 1796 } |
1792 } | 1797 } |
1793 | 1798 |
1794 class SyncStarRewriter extends AsyncRewriterBase { | 1799 class SyncStarRewriter extends AsyncRewriterBase { |
1795 bool get isSyncStar => true; | 1800 bool get isSyncStar => true; |
1796 | 1801 |
1797 /// Contructor creating the Iterable for a sync* method. Called with | 1802 /// Contructor creating the Iterable for a sync* method. Called with |
1798 /// [bodyName]. | 1803 /// [bodyName]. |
1799 final js.Expression newIterable; | 1804 final js.Expression newIterable; |
1800 | 1805 |
(...skipping 30 matching lines...) Expand all Loading... |
1831 new js.Return(new js.Call(yieldStarExpression, [expression]))); | 1836 new js.Return(new js.Call(yieldStarExpression, [expression]))); |
1832 } else { | 1837 } else { |
1833 addStatement(new js.Return(expression)); | 1838 addStatement(new js.Return(expression)); |
1834 } | 1839 } |
1835 } | 1840 } |
1836 | 1841 |
1837 @override | 1842 @override |
1838 js.Fun finishFunction( | 1843 js.Fun finishFunction( |
1839 List<js.Parameter> parameters, | 1844 List<js.Parameter> parameters, |
1840 js.Statement rewrittenBody, | 1845 js.Statement rewrittenBody, |
1841 js.VariableDeclarationList variableDeclarations) { | 1846 js.VariableDeclarationList variableDeclarations, |
| 1847 SourceInformation sourceInformation) { |
1842 // Each iterator invocation on the iterable should work on its own copy of | 1848 // Each iterator invocation on the iterable should work on its own copy of |
1843 // the parameters. | 1849 // the parameters. |
1844 // TODO(sigurdm): We only need to do this copying for parameters that are | 1850 // TODO(sigurdm): We only need to do this copying for parameters that are |
1845 // mutated. | 1851 // mutated. |
1846 List<js.VariableInitialization> declarations = | 1852 List<js.VariableInitialization> declarations = |
1847 new List<js.VariableInitialization>(); | 1853 new List<js.VariableInitialization>(); |
1848 List<js.Parameter> renamedParameters = new List<js.Parameter>(); | 1854 List<js.Parameter> renamedParameters = new List<js.Parameter>(); |
1849 for (js.Parameter parameter in parameters) { | 1855 for (js.Parameter parameter in parameters) { |
1850 String name = parameter.name; | 1856 String name = parameter.name; |
1851 String renamedName = freshName(name); | 1857 String renamedName = freshName(name); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1884 "varDecl": variableDeclarations, | 1890 "varDecl": variableDeclarations, |
1885 "errorCode": errorCodeName, | 1891 "errorCode": errorCodeName, |
1886 "newIterable": newIterable, | 1892 "newIterable": newIterable, |
1887 "body": bodyName, | 1893 "body": bodyName, |
1888 "self": selfName, | 1894 "self": selfName, |
1889 "result": resultName, | 1895 "result": resultName, |
1890 "goto": goto, | 1896 "goto": goto, |
1891 "handler": handler, | 1897 "handler": handler, |
1892 "currentError": currentErrorName, | 1898 "currentError": currentErrorName, |
1893 "ERROR": js.number(error_codes.ERROR), | 1899 "ERROR": js.number(error_codes.ERROR), |
1894 }); | 1900 }).withSourceInformation(sourceInformation); |
1895 } | 1901 } |
1896 | 1902 |
1897 void addErrorExit() { | 1903 void addErrorExit() { |
1898 beginLabel(rethrowLabel); | 1904 beginLabel(rethrowLabel); |
1899 addStatement(js.js | 1905 addStatement(js.js |
1900 .statement('return #(#);', [uncaughtErrorExpression, currentError])); | 1906 .statement('return #(#);', [uncaughtErrorExpression, currentError])); |
1901 } | 1907 } |
1902 | 1908 |
1903 /// Returning from a sync* function returns an [endOfIteration] marker. | 1909 /// Returning from a sync* function returns an [endOfIteration] marker. |
1904 void addSuccesExit() { | 1910 void addSuccesExit() { |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2023 "expression": expression, | 2029 "expression": expression, |
2024 "bodyName": bodyName, | 2030 "bodyName": bodyName, |
2025 "controller": controllerName, | 2031 "controller": controllerName, |
2026 })); | 2032 })); |
2027 } | 2033 } |
2028 | 2034 |
2029 @override | 2035 @override |
2030 js.Fun finishFunction( | 2036 js.Fun finishFunction( |
2031 List<js.Parameter> parameters, | 2037 List<js.Parameter> parameters, |
2032 js.Statement rewrittenBody, | 2038 js.Statement rewrittenBody, |
2033 js.VariableDeclarationList variableDeclarations) { | 2039 js.VariableDeclarationList variableDeclarations, |
| 2040 SourceInformation sourceInformation) { |
2034 return js.js( | 2041 return js.js( |
2035 """ | 2042 """ |
2036 function (#parameters) { | 2043 function (#parameters) { |
2037 var #bodyName = #wrapBody(function (#errorCode, #result) { | 2044 var #bodyName = #wrapBody(function (#errorCode, #result) { |
2038 if (#hasYield) { | 2045 if (#hasYield) { |
2039 switch (#errorCode) { | 2046 switch (#errorCode) { |
2040 case #STREAM_WAS_CANCELED: | 2047 case #STREAM_WAS_CANCELED: |
2041 #next = #nextWhenCanceled; | 2048 #next = #nextWhenCanceled; |
2042 #goto = #next.pop(); | 2049 #goto = #next.pop(); |
2043 break; | 2050 break; |
(...skipping 23 matching lines...) Expand all Loading... |
2067 "currentError": currentError, | 2074 "currentError": currentError, |
2068 "goto": goto, | 2075 "goto": goto, |
2069 "handler": handler, | 2076 "handler": handler, |
2070 "next": next, | 2077 "next": next, |
2071 "nextWhenCanceled": nextWhenCanceled, | 2078 "nextWhenCanceled": nextWhenCanceled, |
2072 "errorCode": errorCodeName, | 2079 "errorCode": errorCodeName, |
2073 "result": resultName, | 2080 "result": resultName, |
2074 "streamOfController": streamOfController, | 2081 "streamOfController": streamOfController, |
2075 "controller": controllerName, | 2082 "controller": controllerName, |
2076 "wrapBody": wrapBody, | 2083 "wrapBody": wrapBody, |
2077 }); | 2084 }).withSourceInformation(sourceInformation); |
2078 } | 2085 } |
2079 | 2086 |
2080 @override | 2087 @override |
2081 void addErrorExit() { | 2088 void addErrorExit() { |
2082 beginLabel(rethrowLabel); | 2089 beginLabel(rethrowLabel); |
2083 addStatement(js.js.statement( | 2090 addStatement(js.js.statement( |
2084 "return #asyncHelper(#currentError, #errorCode, #controller);", { | 2091 "return #asyncHelper(#currentError, #errorCode, #controller);", { |
2085 "asyncHelper": asyncStarHelper, | 2092 "asyncHelper": asyncStarHelper, |
2086 "errorCode": js.number(error_codes.ERROR), | 2093 "errorCode": js.number(error_codes.ERROR), |
2087 "currentError": currentError, | 2094 "currentError": currentError, |
(...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2585 return condition || body; | 2592 return condition || body; |
2586 } | 2593 } |
2587 | 2594 |
2588 @override | 2595 @override |
2589 bool visitDartYield(js.DartYield node) { | 2596 bool visitDartYield(js.DartYield node) { |
2590 hasYield = true; | 2597 hasYield = true; |
2591 visit(node.expression); | 2598 visit(node.expression); |
2592 return true; | 2599 return true; |
2593 } | 2600 } |
2594 } | 2601 } |
OLD | NEW |