| 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 |