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 |