| 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): Avoid using variables in templates. It could blow up memory | |
| 8 // use. | |
| 9 // TODO(sigurdm): Move the try/catch expression to a js_helper function. | 7 // TODO(sigurdm): Move the try/catch expression to a js_helper function. |
| 10 // That would also simplify the sync* case, where the error can just be thrown. | 8 // That would also simplify the sync* case, where the error can just be thrown. |
| 11 | 9 |
| 12 import "dart:math" show max; | 10 import "dart:math" show max; |
| 13 import 'dart:collection'; | 11 import 'dart:collection'; |
| 14 | 12 |
| 15 import 'package:_internal/compiler/js_lib/shared/async_await_error_codes.dart' | 13 import 'package:_internal/compiler/js_lib/shared/async_await_error_codes.dart' |
| 16 as error_codes; | 14 as error_codes; |
| 17 | 15 |
| 18 import "js.dart" as js; | 16 import "js.dart" as js; |
| (...skipping 575 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 594 /// Returning from an async* function calls the [streamHelper] with an | 592 /// Returning from an async* function calls the [streamHelper] with an |
| 595 /// [endOfIteration] marker. | 593 /// [endOfIteration] marker. |
| 596 void addExit() { | 594 void addExit() { |
| 597 if (analysis.hasExplicitReturns || isAsyncStar) { | 595 if (analysis.hasExplicitReturns || isAsyncStar) { |
| 598 beginLabel(exitLabel); | 596 beginLabel(exitLabel); |
| 599 } else { | 597 } else { |
| 600 addStatement(new js.Comment("implicit return")); | 598 addStatement(new js.Comment("implicit return")); |
| 601 } | 599 } |
| 602 switch (async) { | 600 switch (async) { |
| 603 case const js.AsyncModifier.async(): | 601 case const js.AsyncModifier.async(): |
| 604 String returnValue = | |
| 605 analysis.hasExplicitReturns ? returnValueName : "null"; | |
| 606 addStatement(js.js.statement( | 602 addStatement(js.js.statement( |
| 607 "return #thenHelper($returnValue, #successCode, " | 603 "return #runtimeHelper(#returnValue, #successCode, " |
| 608 "$completerName, null)", { | 604 "#completer, null)", { |
| 609 "thenHelper": asyncHelper, | 605 "runtimeHelper": asyncHelper, |
| 610 "successCode": js.number(error_codes.SUCCESS)})); | 606 "successCode": js.number(error_codes.SUCCESS), |
| 607 "returnValue": analysis.hasExplicitReturns |
| 608 ? returnValueName |
| 609 : new js.LiteralNull(), |
| 610 "completer": completerName})); |
| 611 break; | 611 break; |
| 612 case const js.AsyncModifier.syncStar(): | 612 case const js.AsyncModifier.syncStar(): |
| 613 addStatement(new js.Return(new js.Call(endOfIteration, []))); | 613 addStatement(new js.Return(new js.Call(endOfIteration, []))); |
| 614 break; | 614 break; |
| 615 case const js.AsyncModifier.asyncStar(): | 615 case const js.AsyncModifier.asyncStar(): |
| 616 addStatement(js.js.statement( | 616 addStatement(js.js.statement( |
| 617 "return #streamHelper(null, #successCode, $controllerName)", { | 617 "return #streamHelper(null, #successCode, #controller)", { |
| 618 "streamHelper": streamHelper, | 618 "streamHelper": streamHelper, |
| 619 "successCode": js.number(error_codes.SUCCESS)})); | 619 "successCode": js.number(error_codes.SUCCESS), |
| 620 "controller": controllerName})); |
| 620 break; | 621 break; |
| 621 default: | 622 default: |
| 622 diagnosticListener.internalError( | 623 diagnosticListener.internalError( |
| 623 spannable, "Internal error, unexpected asyncmodifier $async"); | 624 spannable, "Internal error, unexpected asyncmodifier $async"); |
| 624 } | 625 } |
| 625 if (isAsync || isAsyncStar) { | 626 if (isAsync || isAsyncStar) { |
| 626 beginLabel(rethrowLabel); | 627 beginLabel(rethrowLabel); |
| 627 addStatement(js.js.statement( | 628 addStatement(js.js.statement( |
| 628 "return #thenHelper($currentErrorName, #errorCode, " | 629 "return #thenHelper(#currentError, #errorCode, #controller)", { |
| 629 "${isAsync ? completerName : controllerName})", { | |
| 630 "thenHelper": isAsync ? asyncHelper : streamHelper, | 630 "thenHelper": isAsync ? asyncHelper : streamHelper, |
| 631 "errorCode": js.number(error_codes.ERROR)})); | 631 "errorCode": js.number(error_codes.ERROR), |
| 632 "currentError": currentErrorName, |
| 633 "controller": isAsync ? completerName : controllerName})); |
| 632 } else { | 634 } else { |
| 633 assert(isSyncStar); | 635 assert(isSyncStar); |
| 634 beginLabel(rethrowLabel); | 636 beginLabel(rethrowLabel); |
| 635 addStatement(new js.Return(new js.Call(uncaughtErrorExpression, | 637 addStatement(new js.Return(new js.Call(uncaughtErrorExpression, |
| 636 [new js.VariableUse(currentErrorName)]))); | 638 [new js.VariableUse(currentErrorName)]))); |
| 637 } | 639 } |
| 638 } | 640 } |
| 639 | 641 |
| 640 /// The initial call to [asyncHelper]/[streamHelper]. | 642 /// The initial call to [asyncHelper]/[streamHelper]. |
| 641 /// | 643 /// |
| 642 /// There is no value to await/yield, so the first argument is `null` and | 644 /// There is no value to await/yield, so the first argument is `null` and |
| 643 /// also the errorCallback is `null`. | 645 /// also the errorCallback is `null`. |
| 644 /// | 646 /// |
| 645 /// Returns the [Future]/[Stream] coming from [completerName]/ | 647 /// Returns the [Future]/[Stream] coming from [completerName]/ |
| 646 /// [controllerName]. | 648 /// [controllerName]. |
| 647 js.Statement generateInitializer() { | 649 js.Statement generateInitializer() { |
| 648 if (isAsync) { | 650 if (isAsync) { |
| 649 return js.js.statement( | 651 return js.js.statement( |
| 650 "return #asyncHelper(null, $bodyName, $completerName, null);", { | 652 "return #asyncHelper(null, #body, #completer, null);", { |
| 651 "asyncHelper": asyncHelper | 653 "asyncHelper": asyncHelper, |
| 654 "body": bodyName, |
| 655 "completer": completerName, |
| 652 }); | 656 }); |
| 653 } else if (isAsyncStar) { | 657 } else if (isAsyncStar) { |
| 654 return js.js.statement( | 658 return js.js.statement( |
| 655 "return #streamOfController($controllerName);", { | 659 "return #streamOfController(#controller);", { |
| 656 "streamOfController": streamOfController | 660 "streamOfController": streamOfController, |
| 661 "controller": controllerName, |
| 657 }); | 662 }); |
| 658 } else { | 663 } else { |
| 659 throw diagnosticListener.internalError( | 664 throw diagnosticListener.internalError( |
| 660 spannable, "Unexpected asyncModifier: $async"); | 665 spannable, "Unexpected asyncModifier: $async"); |
| 661 } | 666 } |
| 662 } | 667 } |
| 663 | 668 |
| 664 /// Rewrites an async/sync*/async* function to a normal Javascript function. | 669 /// Rewrites an async/sync*/async* function to a normal Javascript function. |
| 665 /// | 670 /// |
| 666 /// The control flow is flattened by simulating 'goto' using a switch in a | 671 /// The control flow is flattened by simulating 'goto' using a switch in a |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 821 visitStatement(body); | 826 visitStatement(body); |
| 822 jumpTargets.removeLast(); | 827 jumpTargets.removeLast(); |
| 823 addExit(); | 828 addExit(); |
| 824 | 829 |
| 825 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { | 830 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { |
| 826 return new js.Case(js.number(label), new js.Block(labelledParts[label])); | 831 return new js.Case(js.number(label), new js.Block(labelledParts[label])); |
| 827 }).toList(); | 832 }).toList(); |
| 828 js.Statement helperBody = | 833 js.Statement helperBody = |
| 829 new js.Switch(new js.VariableUse(gotoName), clauses); | 834 new js.Switch(new js.VariableUse(gotoName), clauses); |
| 830 if (hasJumpThoughOuterLabel) { | 835 if (hasJumpThoughOuterLabel) { |
| 831 helperBody = js.js.statement("$outerLabelName: #", [helperBody]); | 836 helperBody = new js.LabeledStatement(outerLabelName, helperBody); |
| 832 } | 837 } |
| 833 helperBody = js.js.statement(""" | 838 helperBody = js.js.statement(""" |
| 834 try { | 839 try { |
| 835 #body | 840 #body |
| 836 } catch ($errorName){ | 841 } catch (#error){ |
| 837 $currentErrorName = $errorName; | 842 #currentError = #error; |
| 838 $gotoName = $handlerName; | 843 #goto = #handler; |
| 839 }""", {"body": helperBody}); | 844 }""", { |
| 845 "body": helperBody, |
| 846 "goto": gotoName, |
| 847 "error": errorName, |
| 848 "currentError": currentErrorName, |
| 849 "handler": handlerName, |
| 850 }); |
| 840 List<js.VariableInitialization> inits = <js.VariableInitialization>[]; | 851 List<js.VariableInitialization> inits = <js.VariableInitialization>[]; |
| 841 | 852 |
| 842 js.VariableInitialization makeInit(String name, js.Expression initValue) { | 853 js.VariableInitialization makeInit(String name, js.Expression initValue) { |
| 843 return new js.VariableInitialization( | 854 return new js.VariableInitialization( |
| 844 new js.VariableDeclaration(name), initValue); | 855 new js.VariableDeclaration(name), initValue); |
| 845 } | 856 } |
| 846 | 857 |
| 847 inits.add(makeInit(gotoName, js.number(0))); | 858 inits.add(makeInit(gotoName, js.number(0))); |
| 848 if (isAsync) { | 859 if (isAsync) { |
| 849 inits.add(makeInit(completerName, new js.New(newCompleter, []))); | 860 inits.add(makeInit(completerName, new js.New(newCompleter, []))); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 871 return new js.VariableInitialization(decl, null); | 882 return new js.VariableInitialization(decl, null); |
| 872 })); | 883 })); |
| 873 inits.addAll(new Iterable.generate(tempVarHighWaterMark, | 884 inits.addAll(new Iterable.generate(tempVarHighWaterMark, |
| 874 (int i) => makeInit(useTempVar(i + 1).name, null))); | 885 (int i) => makeInit(useTempVar(i + 1).name, null))); |
| 875 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits); | 886 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits); |
| 876 // TODO(sigurdm): Explain the difference between these cases. | 887 // TODO(sigurdm): Explain the difference between these cases. |
| 877 if (isSyncStar) { | 888 if (isSyncStar) { |
| 878 return js.js(""" | 889 return js.js(""" |
| 879 function (#params) { | 890 function (#params) { |
| 880 if (#needsThis) | 891 if (#needsThis) |
| 881 var $selfName = this; | 892 var #self = this; |
| 882 return new #newIterable(function () { | 893 return new #newIterable(function () { |
| 883 #varDecl; | 894 #varDecl; |
| 884 return function $bodyName() { | 895 return function #body() { |
| 885 while (true) | 896 while (true) |
| 886 #helperBody; | 897 #helperBody; |
| 887 }; | 898 }; |
| 888 }); | 899 }); |
| 889 } | 900 } |
| 890 """, { | 901 """, { |
| 891 "params": node.params, | 902 "params": node.params, |
| 892 "needsThis": analysis.hasThis, | 903 "needsThis": analysis.hasThis, |
| 893 "helperBody": helperBody, | 904 "helperBody": helperBody, |
| 894 "varDecl": varDecl, | 905 "varDecl": varDecl, |
| 895 "newIterable": newIterable | 906 "newIterable": newIterable, |
| 907 "body": bodyName, |
| 908 "self": selfName, |
| 896 }); | 909 }); |
| 897 } | 910 } |
| 898 return js.js(""" | 911 return js.js(""" |
| 899 function (#params) { | 912 function (#params) { |
| 900 #varDecl; | 913 #varDecl; |
| 901 function $bodyName($errorCodeName, $resultName) { | 914 function #bodyName(#errorCode, #result) { |
| 902 if (#hasYield) | 915 if (#hasYield) |
| 903 switch ($errorCodeName) { | 916 switch (#errorCode) { |
| 904 case #streamWasCanceled: | 917 case #STREAM_WAS_CANCELED: |
| 905 $nextName = $nextWhenCanceledName; | 918 #next = #nextWhenCanceled; |
| 906 $gotoName = $nextName.pop(); | 919 #goto = #next.pop(); |
| 907 break; | 920 break; |
| 908 case #errorCode: | 921 case #ERROR: |
| 909 $currentErrorName = $resultName; | 922 #currentError = #result; |
| 910 $gotoName = $handlerName; | 923 #goto = #handler; |
| 911 } | 924 } |
| 912 else | 925 else |
| 913 if ($errorCodeName == #errorCode) { | 926 if (#errorCode == #ERROR) { |
| 914 $currentErrorName = $resultName; | 927 #currentError = #result; |
| 915 $gotoName = $handlerName; | 928 #goto = #handler; |
| 916 } | 929 } |
| 917 while (true) | 930 while (true) |
| 918 #helperBody; | 931 #helperBody; |
| 919 } | 932 } |
| 920 #init; | 933 #init; |
| 921 }""", { | 934 }""", { |
| 922 "params": node.params, | 935 "params": node.params, |
| 923 "varDecl": varDecl, | 936 "varDecl": varDecl, |
| 924 "streamWasCanceled": js.number(error_codes.STREAM_WAS_CANCELED), | 937 "STREAM_WAS_CANCELED": js.number(error_codes.STREAM_WAS_CANCELED), |
| 925 "errorCode": js.number(error_codes.ERROR), | 938 "ERROR": js.number(error_codes.ERROR), |
| 926 "hasYield": analysis.hasYield, | 939 "hasYield": analysis.hasYield, |
| 927 "helperBody": helperBody, | 940 "helperBody": helperBody, |
| 928 "init": generateInitializer() | 941 "init": generateInitializer(), |
| 942 "bodyName": bodyName, |
| 943 "currentError": currentErrorName, |
| 944 "goto": gotoName, |
| 945 "handler": handlerName, |
| 946 "next": nextName, |
| 947 "nextWhenCanceled": nextWhenCanceledName, |
| 948 "errorCode": errorCodeName, |
| 949 "result": resultName, |
| 929 }); | 950 }); |
| 930 } | 951 } |
| 931 | 952 |
| 932 @override | 953 @override |
| 933 js.Expression visitAccess(js.PropertyAccess node) { | 954 js.Expression visitAccess(js.PropertyAccess node) { |
| 934 return withExpression2(node.receiver, node.selector, | 955 return withExpression2(node.receiver, node.selector, |
| 935 (receiver, selector) => new js.PropertyAccess(receiver, selector)); | 956 (receiver, selector) => new js.PropertyAccess(receiver, selector)); |
| 936 } | 957 } |
| 937 | 958 |
| 938 @override | 959 @override |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 977 /// | 998 /// |
| 978 /// See the comments of [visitFun] for an example. | 999 /// See the comments of [visitFun] for an example. |
| 979 @override | 1000 @override |
| 980 js.Expression visitAwait(js.Await node) { | 1001 js.Expression visitAwait(js.Await node) { |
| 981 assert(isAsync || isAsyncStar); | 1002 assert(isAsync || isAsyncStar); |
| 982 int afterAwait = newLabel("returning from await."); | 1003 int afterAwait = newLabel("returning from await."); |
| 983 withExpression(node.expression, (js.Expression value) { | 1004 withExpression(node.expression, (js.Expression value) { |
| 984 addStatement(setGotoVariable(afterAwait)); | 1005 addStatement(setGotoVariable(afterAwait)); |
| 985 addStatement(js.js.statement(""" | 1006 addStatement(js.js.statement(""" |
| 986 return #asyncHelper(#value, | 1007 return #asyncHelper(#value, |
| 987 $bodyName, | 1008 #body, |
| 988 ${isAsync ? completerName : controllerName}); | 1009 #controller); |
| 989 """, { | 1010 """, { |
| 990 "asyncHelper": isAsync ? asyncHelper : streamHelper, | 1011 "asyncHelper": isAsync ? asyncHelper : streamHelper, |
| 991 "value": value, | 1012 "value": value, |
| 1013 "body": bodyName, |
| 1014 "controller": isAsync ? completerName : controllerName, |
| 992 })); | 1015 })); |
| 993 }, store: false); | 1016 }, store: false); |
| 994 beginLabel(afterAwait); | 1017 beginLabel(afterAwait); |
| 995 return new js.VariableUse(resultName); | 1018 return new js.VariableUse(resultName); |
| 996 } | 1019 } |
| 997 | 1020 |
| 998 /// Checks if [node] is the variable named [resultName]. | 1021 /// Checks if [node] is the variable named [resultName]. |
| 999 /// | 1022 /// |
| 1000 /// [resultName] is used to hold the result of a transformed computation | 1023 /// [resultName] is used to hold the result of a transformed computation |
| 1001 /// for example the result of awaiting, or the result of a conditional or | 1024 /// for example the result of awaiting, or the result of a conditional or |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1158 // Ignore other nodes. | 1181 // Ignore other nodes. |
| 1159 } | 1182 } |
| 1160 jumpStack = jumpStack.reversed.toList(); | 1183 jumpStack = jumpStack.reversed.toList(); |
| 1161 // As the program jumps directly to the top of the stack, it is taken off | 1184 // As the program jumps directly to the top of the stack, it is taken off |
| 1162 // now. | 1185 // now. |
| 1163 int firstTarget = jumpStack.removeLast(); | 1186 int firstTarget = jumpStack.removeLast(); |
| 1164 if (jumpStack.isNotEmpty) { | 1187 if (jumpStack.isNotEmpty) { |
| 1165 hasJumpThroughFinally = true; | 1188 hasJumpThroughFinally = true; |
| 1166 js.Expression jsJumpStack = new js.ArrayInitializer( | 1189 js.Expression jsJumpStack = new js.ArrayInitializer( |
| 1167 jumpStack.map((int label) => js.number(label)).toList()); | 1190 jumpStack.map((int label) => js.number(label)).toList()); |
| 1168 addStatement(js.js.statement("$nextName = #", [jsJumpStack])); | 1191 addStatement(js.js.statement("# = #", [nextName, jsJumpStack])); |
| 1169 } | 1192 } |
| 1170 addGoto(firstTarget); | 1193 addGoto(firstTarget); |
| 1171 } | 1194 } |
| 1172 | 1195 |
| 1173 @override | 1196 @override |
| 1174 void visitDefault(js.Default node) => unreachable(node); | 1197 void visitDefault(js.Default node) => unreachable(node); |
| 1175 | 1198 |
| 1176 @override | 1199 @override |
| 1177 void visitDo(js.Do node) { | 1200 void visitDo(js.Do node) { |
| 1178 if (!shouldTransform(node)) { | 1201 if (!shouldTransform(node)) { |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1331 } | 1354 } |
| 1332 beginLabel(joinLabel); | 1355 beginLabel(joinLabel); |
| 1333 } | 1356 } |
| 1334 | 1357 |
| 1335 @override | 1358 @override |
| 1336 visitInterpolatedExpression(js.InterpolatedExpression node) { | 1359 visitInterpolatedExpression(js.InterpolatedExpression node) { |
| 1337 return unsupported(node); | 1360 return unsupported(node); |
| 1338 } | 1361 } |
| 1339 | 1362 |
| 1340 @override | 1363 @override |
| 1364 visitInterpolatedDeclaration(js.InterpolatedDeclaration node) { |
| 1365 return unsupported(node); |
| 1366 } |
| 1367 |
| 1368 @override |
| 1341 visitInterpolatedLiteral(js.InterpolatedLiteral node) => unsupported(node); | 1369 visitInterpolatedLiteral(js.InterpolatedLiteral node) => unsupported(node); |
| 1342 | 1370 |
| 1343 @override | 1371 @override |
| 1344 visitInterpolatedParameter(js.InterpolatedParameter node) { | 1372 visitInterpolatedParameter(js.InterpolatedParameter node) { |
| 1345 return unsupported(node); | 1373 return unsupported(node); |
| 1346 } | 1374 } |
| 1347 | 1375 |
| 1348 @override | 1376 @override |
| 1349 visitInterpolatedSelector(js.InterpolatedSelector node) { | 1377 visitInterpolatedSelector(js.InterpolatedSelector node) { |
| 1350 return unsupported(node); | 1378 return unsupported(node); |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1478 | 1506 |
| 1479 @override | 1507 @override |
| 1480 js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node; | 1508 js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node; |
| 1481 | 1509 |
| 1482 @override | 1510 @override |
| 1483 void visitReturn(js.Return node) { | 1511 void visitReturn(js.Return node) { |
| 1484 assert(node.value == null || !isSyncStar && !isAsyncStar); | 1512 assert(node.value == null || !isSyncStar && !isAsyncStar); |
| 1485 js.Node target = analysis.targets[node]; | 1513 js.Node target = analysis.targets[node]; |
| 1486 if (node.value != null) { | 1514 if (node.value != null) { |
| 1487 withExpression(node.value, (js.Expression value) { | 1515 withExpression(node.value, (js.Expression value) { |
| 1488 addStatement(js.js.statement("$returnValueName = #", [value])); | 1516 addStatement(js.js.statement("# = #", [returnValueName, value])); |
| 1489 }, store: false); | 1517 }, store: false); |
| 1490 } | 1518 } |
| 1491 translateJump(target, exitLabel); | 1519 translateJump(target, exitLabel); |
| 1492 } | 1520 } |
| 1493 | 1521 |
| 1494 @override | 1522 @override |
| 1495 void visitSwitch(js.Switch node) { | 1523 void visitSwitch(js.Switch node) { |
| 1496 if (!node.cases.any(shouldTransform)) { | 1524 if (!node.cases.any(shouldTransform)) { |
| 1497 // If only the key has an await, translation can be simplified. | 1525 // If only the key has an await, translation can be simplified. |
| 1498 bool oldInsideUntranslated = insideUntranslatedBreakable; | 1526 bool oldInsideUntranslated = insideUntranslatedBreakable; |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1659 | 1687 |
| 1660 js.Node last = jumpTargets.removeLast(); | 1688 js.Node last = jumpTargets.removeLast(); |
| 1661 assert(last == node); | 1689 assert(last == node); |
| 1662 | 1690 |
| 1663 if (node.finallyPart == null) { | 1691 if (node.finallyPart == null) { |
| 1664 setErrorHandler(); | 1692 setErrorHandler(); |
| 1665 addGoto(afterFinallyLabel); | 1693 addGoto(afterFinallyLabel); |
| 1666 } else { | 1694 } else { |
| 1667 // The handler is reset as the first thing in the finally block. | 1695 // The handler is reset as the first thing in the finally block. |
| 1668 addStatement( | 1696 addStatement( |
| 1669 js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)])); | 1697 js.js.statement("# = [#];", |
| 1698 [nextName, js.number(afterFinallyLabel)])); |
| 1670 addGoto(finallyLabel); | 1699 addGoto(finallyLabel); |
| 1671 } | 1700 } |
| 1672 | 1701 |
| 1673 if (node.catchPart != null) { | 1702 if (node.catchPart != null) { |
| 1674 beginLabel(handlerLabel); | 1703 beginLabel(handlerLabel); |
| 1675 // [uncaughtLabel] is the handler for the code in the catch-part. | 1704 // [uncaughtLabel] is the handler for the code in the catch-part. |
| 1676 // It ensures that [nextName] is set up to run the right finally blocks. | 1705 // It ensures that [nextName] is set up to run the right finally blocks. |
| 1677 handlerLabels[node.catchPart] = uncaughtLabel; | 1706 handlerLabels[node.catchPart] = uncaughtLabel; |
| 1678 jumpTargets.add(node.catchPart); | 1707 jumpTargets.add(node.catchPart); |
| 1679 setErrorHandler(); | 1708 setErrorHandler(); |
| 1680 // The catch declaration name can shadow outer variables, so a fresh name | 1709 // The catch declaration name can shadow outer variables, so a fresh name |
| 1681 // is needed to avoid collisions. See Ecma 262, 3rd edition, | 1710 // is needed to avoid collisions. See Ecma 262, 3rd edition, |
| 1682 // section 12.14. | 1711 // section 12.14. |
| 1683 String errorRename = freshName(node.catchPart.declaration.name); | 1712 String errorRename = freshName(node.catchPart.declaration.name); |
| 1684 localVariables.add(new js.VariableDeclaration(errorRename)); | 1713 localVariables.add(new js.VariableDeclaration(errorRename)); |
| 1685 variableRenamings | 1714 variableRenamings |
| 1686 .add(new Pair(node.catchPart.declaration.name, errorRename)); | 1715 .add(new Pair(node.catchPart.declaration.name, errorRename)); |
| 1687 addExpressionStatement(new js.Assignment( | 1716 addExpressionStatement(new js.Assignment( |
| 1688 new js.VariableUse(errorRename), | 1717 new js.VariableUse(errorRename), |
| 1689 new js.VariableUse(currentErrorName))); | 1718 new js.VariableUse(currentErrorName))); |
| 1690 visitStatement(node.catchPart.body); | 1719 visitStatement(node.catchPart.body); |
| 1691 variableRenamings.removeLast(); | 1720 variableRenamings.removeLast(); |
| 1692 if (node.finallyPart != null) { | 1721 if (node.finallyPart != null) { |
| 1693 // The error has been caught, so after the finally, continue after the | 1722 // The error has been caught, so after the finally, continue after the |
| 1694 // try. | 1723 // try. |
| 1695 addStatement(js.js.statement("$nextName = [#];", | 1724 addStatement(js.js.statement("# = [#];", |
| 1696 [js.number(afterFinallyLabel)])); | 1725 [nextName, js.number(afterFinallyLabel)])); |
| 1697 addGoto(finallyLabel); | 1726 addGoto(finallyLabel); |
| 1698 } else { | 1727 } else { |
| 1699 addGoto(afterFinallyLabel); | 1728 addGoto(afterFinallyLabel); |
| 1700 } | 1729 } |
| 1701 js.Node last = jumpTargets.removeLast(); | 1730 js.Node last = jumpTargets.removeLast(); |
| 1702 assert(last == node.catchPart); | 1731 assert(last == node.catchPart); |
| 1703 } | 1732 } |
| 1704 | 1733 |
| 1705 // The "uncaught"-handler tells the finally-block to continue with | 1734 // The "uncaught"-handler tells the finally-block to continue with |
| 1706 // the enclosing finally-blocks until the current catch-handler. | 1735 // the enclosing finally-blocks until the current catch-handler. |
| 1707 beginLabel(uncaughtLabel); | 1736 beginLabel(uncaughtLabel); |
| 1708 | 1737 |
| 1709 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler(); | 1738 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler(); |
| 1710 | 1739 |
| 1711 int nextLabel = enclosingFinallies.removeLast(); | 1740 int nextLabel = enclosingFinallies.removeLast(); |
| 1712 if (enclosingFinallies.isNotEmpty) { | 1741 if (enclosingFinallies.isNotEmpty) { |
| 1713 // [enclosingFinallies] can be empty if there is no surrounding finally | 1742 // [enclosingFinallies] can be empty if there is no surrounding finally |
| 1714 // blocks. Then [nextLabel] will be [rethrowLabel]. | 1743 // blocks. Then [nextLabel] will be [rethrowLabel]. |
| 1715 addStatement( | 1744 addStatement( |
| 1716 js.js.statement("$nextName = #;", new js.ArrayInitializer( | 1745 js.js.statement("# = #;", [nextName, new js.ArrayInitializer( |
| 1717 enclosingFinallies.map(js.number).toList()))); | 1746 enclosingFinallies.map(js.number).toList())])); |
| 1718 } | 1747 } |
| 1719 if (node.finallyPart == null) { | 1748 if (node.finallyPart == null) { |
| 1720 // The finally-block belonging to [node] will be visited because of | 1749 // The finally-block belonging to [node] will be visited because of |
| 1721 // fallthrough. If it does not exist, add an explicit goto. | 1750 // fallthrough. If it does not exist, add an explicit goto. |
| 1722 addGoto(nextLabel); | 1751 addGoto(nextLabel); |
| 1723 } | 1752 } |
| 1724 if (node.finallyPart != null) { | 1753 if (node.finallyPart != null) { |
| 1725 js.Node last = jumpTargets.removeLast(); | 1754 js.Node last = jumpTargets.removeLast(); |
| 1726 assert(last == node.finallyPart); | 1755 assert(last == node.finallyPart); |
| 1727 | 1756 |
| 1728 beginLabel(finallyLabel); | 1757 beginLabel(finallyLabel); |
| 1729 setErrorHandler(); | 1758 setErrorHandler(); |
| 1730 visitStatement(node.finallyPart); | 1759 visitStatement(node.finallyPart); |
| 1731 addStatement(new js.Comment("// goto the next finally handler")); | 1760 addStatement(new js.Comment("// goto the next finally handler")); |
| 1732 addStatement(js.js.statement("$gotoName = $nextName.pop();")); | 1761 addStatement(js.js.statement("# = #.pop();", [gotoName, nextName])); |
| 1733 addBreak(); | 1762 addBreak(); |
| 1734 } | 1763 } |
| 1735 beginLabel(afterFinallyLabel); | 1764 beginLabel(afterFinallyLabel); |
| 1736 } | 1765 } |
| 1737 | 1766 |
| 1738 @override | 1767 @override |
| 1739 visitVariableDeclaration(js.VariableDeclaration node) { | 1768 visitVariableDeclaration(js.VariableDeclaration node) { |
| 1740 unreachable(node); | 1769 unreachable(node); |
| 1741 } | 1770 } |
| 1742 | 1771 |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1825 /// must be run in case the stream was canceled. | 1854 /// must be run in case the stream was canceled. |
| 1826 void addAsyncYield(js.DartYield node, js.Expression expression) { | 1855 void addAsyncYield(js.DartYield node, js.Expression expression) { |
| 1827 assert(isAsyncStar); | 1856 assert(isAsyncStar); |
| 1828 // Find all the finally blocks that should be performed if the stream is | 1857 // Find all the finally blocks that should be performed if the stream is |
| 1829 // canceled during the yield. | 1858 // canceled during the yield. |
| 1830 // At the bottom of the stack is the return label. | 1859 // At the bottom of the stack is the return label. |
| 1831 List<int> enclosingFinallyLabels = <int>[exitLabel]; | 1860 List<int> enclosingFinallyLabels = <int>[exitLabel]; |
| 1832 enclosingFinallyLabels.addAll(jumpTargets | 1861 enclosingFinallyLabels.addAll(jumpTargets |
| 1833 .where((js.Node node) => finallyLabels[node] != null) | 1862 .where((js.Node node) => finallyLabels[node] != null) |
| 1834 .map((js.Block node) => finallyLabels[node])); | 1863 .map((js.Block node) => finallyLabels[node])); |
| 1835 addStatement(js.js.statement("$nextWhenCanceledName = #", | 1864 addStatement(js.js.statement("# = #;", |
| 1836 [new js.ArrayInitializer(enclosingFinallyLabels.map(js.number) | 1865 [nextWhenCanceledName, new js.ArrayInitializer( |
| 1837 .toList())])); | 1866 enclosingFinallyLabels.map(js.number).toList())])); |
| 1838 addStatement(js.js.statement(""" | 1867 addStatement(js.js.statement(""" |
| 1839 return #streamHelper(#yieldExpression(#expression), | 1868 return #streamHelper(#yieldExpression(#expression), #body, |
| 1840 $bodyName, $controllerName);""", { | 1869 #controller);""", { |
| 1841 "streamHelper": streamHelper, | 1870 "streamHelper": streamHelper, |
| 1842 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression, | 1871 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression, |
| 1843 "expression": expression, | 1872 "expression": expression, |
| 1873 "body": bodyName, |
| 1874 "controller": controllerName, |
| 1844 })); | 1875 })); |
| 1845 } | 1876 } |
| 1846 | 1877 |
| 1847 @override | 1878 @override |
| 1848 void visitDartYield(js.DartYield node) { | 1879 void visitDartYield(js.DartYield node) { |
| 1849 assert(isSyncStar || isAsyncStar); | 1880 assert(isSyncStar || isAsyncStar); |
| 1850 int label = newLabel("after yield"); | 1881 int label = newLabel("after yield"); |
| 1851 // Don't do a break here for the goto, but instead a return in either | 1882 // Don't do a break here for the goto, but instead a return in either |
| 1852 // addSynYield or addAsyncYield. | 1883 // addSynYield or addAsyncYield. |
| 1853 withExpression(node.expression, (js.Expression expression) { | 1884 withExpression(node.expression, (js.Expression expression) { |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2082 bool otherwise = visit(node.otherwise); | 2113 bool otherwise = visit(node.otherwise); |
| 2083 return condition || then || otherwise; | 2114 return condition || then || otherwise; |
| 2084 } | 2115 } |
| 2085 | 2116 |
| 2086 @override | 2117 @override |
| 2087 bool visitInterpolatedExpression(js.InterpolatedExpression node) { | 2118 bool visitInterpolatedExpression(js.InterpolatedExpression node) { |
| 2088 return unsupported(node); | 2119 return unsupported(node); |
| 2089 } | 2120 } |
| 2090 | 2121 |
| 2091 @override | 2122 @override |
| 2123 bool visitInterpolatedDeclaration(js.InterpolatedDeclaration node) { |
| 2124 return unsupported(node); |
| 2125 } |
| 2126 |
| 2127 @override |
| 2092 bool visitInterpolatedLiteral(js.InterpolatedLiteral node) { | 2128 bool visitInterpolatedLiteral(js.InterpolatedLiteral node) { |
| 2093 return unsupported(node); | 2129 return unsupported(node); |
| 2094 } | 2130 } |
| 2095 | 2131 |
| 2096 @override | 2132 @override |
| 2097 bool visitInterpolatedParameter(js.InterpolatedParameter node) { | 2133 bool visitInterpolatedParameter(js.InterpolatedParameter node) { |
| 2098 return unsupported(node); | 2134 return unsupported(node); |
| 2099 } | 2135 } |
| 2100 | 2136 |
| 2101 @override | 2137 @override |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2271 return condition || body; | 2307 return condition || body; |
| 2272 } | 2308 } |
| 2273 | 2309 |
| 2274 @override | 2310 @override |
| 2275 bool visitDartYield(js.DartYield node) { | 2311 bool visitDartYield(js.DartYield node) { |
| 2276 hasYield = true; | 2312 hasYield = true; |
| 2277 visit(node.expression); | 2313 visit(node.expression); |
| 2278 return true; | 2314 return true; |
| 2279 } | 2315 } |
| 2280 } | 2316 } |
| OLD | NEW |