| 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:math" show max; | 7 import "dart:math" show max; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:js_runtime/shared/async_await_error_codes.dart' | 10 import 'package:js_runtime/shared/async_await_error_codes.dart' |
| 11 as error_codes; | 11 as error_codes; |
| 12 | 12 |
| 13 import "js.dart" as js; | 13 import "js.dart" as js; |
| 14 | 14 |
| 15 import '../common.dart'; | 15 import '../common.dart'; |
| 16 import '../util/util.dart' show | 16 import '../util/util.dart' show |
| 17 Pair; | 17 Pair; |
| 18 | 18 |
| 19 /// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield | 19 /// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield |
| 20 /// (with dart-like semantics) to an equivalent function without these. | 20 /// (with dart-like semantics) to an equivalent function without these. |
| 21 /// await-for is not handled and must be rewritten before. (Currently handled | 21 /// await-for is not handled and must be rewritten before. (Currently handled |
| 22 /// in ssa/builder.dart). | 22 /// in ssa/builder.dart). |
| 23 /// | 23 /// |
| 24 /// When generating the input to this, special care must be taken that | 24 /// When generating the input to this, special care must be taken that |
| 25 /// parameters to sync* functions that are mutated in the body must be boxed. | 25 /// parameters to sync* functions that are mutated in the body must be boxed. |
| 26 /// (Currently handled in closure.dart). | 26 /// (Currently handled in closure.dart). |
| 27 /// | 27 /// |
| 28 /// Look at [visitFun], [visitDartYield] and [visitAwait] for more explanation. | 28 /// Look at [rewriteFunction], [visitDartYield] and [visitAwait] for more |
| 29 /// explanation. |
| 29 abstract class AsyncRewriterBase extends js.NodeVisitor { | 30 abstract class AsyncRewriterBase extends js.NodeVisitor { |
| 30 | 31 |
| 31 // Local variables are hoisted to the top of the function, so they are | 32 // Local variables are hoisted to the top of the function, so they are |
| 32 // collected here. | 33 // collected here. |
| 33 List<js.VariableDeclaration> localVariables = | 34 List<js.VariableDeclaration> localVariables = |
| 34 new List<js.VariableDeclaration>(); | 35 new List<js.VariableDeclaration>(); |
| 35 | 36 |
| 36 Map<js.Node, int> continueLabels = new Map<js.Node, int>(); | 37 Map<js.Node, int> continueLabels = new Map<js.Node, int>(); |
| 37 Map<js.Node, int> breakLabels = new Map<js.Node, int>(); | 38 Map<js.Node, int> breakLabels = new Map<js.Node, int>(); |
| 38 | 39 |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 200 gotoName = freshName("goto"); | 201 gotoName = freshName("goto"); |
| 201 handlerName = freshName("handler"); | 202 handlerName = freshName("handler"); |
| 202 nextName = freshName("next"); | 203 nextName = freshName("next"); |
| 203 returnValueName = freshName("returnValue"); | 204 returnValueName = freshName("returnValue"); |
| 204 currentErrorName = freshName("currentError"); | 205 currentErrorName = freshName("currentError"); |
| 205 outerLabelName = freshName("outer"); | 206 outerLabelName = freshName("outer"); |
| 206 selfName = freshName("self"); | 207 selfName = freshName("self"); |
| 207 // Initialize names specific to the subclass. | 208 // Initialize names specific to the subclass. |
| 208 initializeNames(); | 209 initializeNames(); |
| 209 | 210 |
| 210 return node.accept(this); | 211 return rewriteFunction(node); |
| 211 } | 212 } |
| 212 | 213 |
| 213 js.Expression get currentErrorHandler { | 214 js.Expression get currentErrorHandler { |
| 214 return js.number(handlerLabels[jumpTargets.lastWhere( | 215 return js.number(handlerLabels[jumpTargets.lastWhere( |
| 215 (node) => handlerLabels[node] != null)]); | 216 (node) => handlerLabels[node] != null)]); |
| 216 } | 217 } |
| 217 | 218 |
| 218 int allocateTempVar() { | 219 int allocateTempVar() { |
| 219 assert(tempVarHighWaterMark >= currentTempVarIndex); | 220 assert(tempVarHighWaterMark >= currentTempVarIndex); |
| 220 currentTempVarIndex++; | 221 currentTempVarIndex++; |
| (...skipping 437 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 658 /// case 7: // return | 659 /// case 7: // return |
| 659 /// return thenHelper(returnValue, 0, completer); | 660 /// return thenHelper(returnValue, 0, completer); |
| 660 /// case 8: // Rethrow | 661 /// case 8: // Rethrow |
| 661 /// return thenHelper(currentError, 1, completer); | 662 /// return thenHelper(currentError, 1, completer); |
| 662 /// } | 663 /// } |
| 663 /// } | 664 /// } |
| 664 /// return thenHelper(null, helper, completer); | 665 /// return thenHelper(null, helper, completer); |
| 665 /// } | 666 /// } |
| 666 /// } | 667 /// } |
| 667 /// | 668 /// |
| 668 @override | 669 js.Expression rewriteFunction(js.Fun node) { |
| 669 js.Expression visitFun(js.Fun node) { | |
| 670 beginLabel(newLabel("Function start")); | 670 beginLabel(newLabel("Function start")); |
| 671 // AsyncStar needs a returnlabel for its handling of cancelation. See | 671 // AsyncStar needs a returnlabel for its handling of cancelation. See |
| 672 // [visitDartYield]. | 672 // [visitDartYield]. |
| 673 exitLabel = (analysis.hasExplicitReturns || isAsyncStar) | 673 exitLabel = (analysis.hasExplicitReturns || isAsyncStar) |
| 674 ? newLabel("return") | 674 ? newLabel("return") |
| 675 : null; | 675 : null; |
| 676 rethrowLabel = newLabel("rethrow"); | 676 rethrowLabel = newLabel("rethrow"); |
| 677 handlerLabels[node] = rethrowLabel; | 677 handlerLabels[node] = rethrowLabel; |
| 678 js.Statement body = node.body; | 678 js.Statement body = node.body; |
| 679 jumpTargets.add(node); | 679 jumpTargets.add(node); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 713 })); | 713 })); |
| 714 variables.addAll(new Iterable.generate(tempVarHighWaterMark, | 714 variables.addAll(new Iterable.generate(tempVarHighWaterMark, |
| 715 (int i) => _makeVariableInitializer(useTempVar(i + 1).name, null))); | 715 (int i) => _makeVariableInitializer(useTempVar(i + 1).name, null))); |
| 716 js.VariableDeclarationList variableDeclarations = | 716 js.VariableDeclarationList variableDeclarations = |
| 717 new js.VariableDeclarationList(variables); | 717 new js.VariableDeclarationList(variables); |
| 718 | 718 |
| 719 return finishFunction(node.params, rewrittenBody, variableDeclarations); | 719 return finishFunction(node.params, rewrittenBody, variableDeclarations); |
| 720 } | 720 } |
| 721 | 721 |
| 722 @override | 722 @override |
| 723 js.Expression visitFun(js.Fun node) { |
| 724 if (node.asyncModifier.isAsync || node.asyncModifier.isYielding) { |
| 725 // The translation does not handle nested functions that are generators |
| 726 // or asynchronous. These functions should only be ones that are |
| 727 // introduced by JS foreign code from our own libraries. |
| 728 reporter.internalError(spannable, |
| 729 'Nested function is a generator or asynchronous.'); |
| 730 } |
| 731 return node; |
| 732 } |
| 733 |
| 734 @override |
| 723 js.Expression visitAccess(js.PropertyAccess node) { | 735 js.Expression visitAccess(js.PropertyAccess node) { |
| 724 return withExpression2(node.receiver, node.selector, | 736 return withExpression2(node.receiver, node.selector, |
| 725 (receiver, selector) => js.js('#[#]', [receiver, selector])); | 737 (receiver, selector) => js.js('#[#]', [receiver, selector])); |
| 726 } | 738 } |
| 727 | 739 |
| 728 @override | 740 @override |
| 729 js.Expression visitArrayHole(js.ArrayHole node) { | 741 js.Expression visitArrayHole(js.ArrayHole node) { |
| 730 return node; | 742 return node; |
| 731 } | 743 } |
| 732 | 744 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 765 }); | 777 }); |
| 766 } else { | 778 } else { |
| 767 throw "Unexpected assignment left hand side $leftHandSide"; | 779 throw "Unexpected assignment left hand side $leftHandSide"; |
| 768 } | 780 } |
| 769 } | 781 } |
| 770 | 782 |
| 771 js.Statement awaitStatement(js.Expression value); | 783 js.Statement awaitStatement(js.Expression value); |
| 772 | 784 |
| 773 /// An await is translated to an [awaitStatement]. | 785 /// An await is translated to an [awaitStatement]. |
| 774 /// | 786 /// |
| 775 /// See the comments of [visitFun] for an example. | 787 /// See the comments of [rewriteFunction] for an example. |
| 776 @override | 788 @override |
| 777 js.Expression visitAwait(js.Await node) { | 789 js.Expression visitAwait(js.Await node) { |
| 778 assert(isAsync || isAsyncStar); | 790 assert(isAsync || isAsyncStar); |
| 779 int afterAwait = newLabel("returning from await."); | 791 int afterAwait = newLabel("returning from await."); |
| 780 withExpression(node.expression, (js.Expression value) { | 792 withExpression(node.expression, (js.Expression value) { |
| 781 addStatement(setGotoVariable(afterAwait)); | 793 addStatement(setGotoVariable(afterAwait)); |
| 782 addStatement(awaitStatement(value)); | 794 addStatement(awaitStatement(value)); |
| 783 }, store: false); | 795 }, store: false); |
| 784 beginLabel(afterAwait); | 796 beginLabel(afterAwait); |
| 785 return result; | 797 return result; |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 922 } else { | 934 } else { |
| 923 addStatement(new js.Break(null)); | 935 addStatement(new js.Break(null)); |
| 924 } | 936 } |
| 925 } | 937 } |
| 926 | 938 |
| 927 /// Common code for handling break, continue, return. | 939 /// Common code for handling break, continue, return. |
| 928 /// | 940 /// |
| 929 /// It is necessary to run all nesting finally-handlers between the jump and | 941 /// It is necessary to run all nesting finally-handlers between the jump and |
| 930 /// the target. For that [next] is used as a stack of places to go. | 942 /// the target. For that [next] is used as a stack of places to go. |
| 931 /// | 943 /// |
| 932 /// See also [visitFun]. | 944 /// See also [rewriteFunction]. |
| 933 void translateJump(js.Node target, int targetLabel) { | 945 void translateJump(js.Node target, int targetLabel) { |
| 934 // Compute a stack of all the 'finally' nodes that must be visited before | 946 // Compute a stack of all the 'finally' nodes that must be visited before |
| 935 // the jump. | 947 // the jump. |
| 936 // The bottom of the stack is the label where the jump goes to. | 948 // The bottom of the stack is the label where the jump goes to. |
| 937 List<int> jumpStack = new List<int>(); | 949 List<int> jumpStack = new List<int>(); |
| 938 for (js.Node node in jumpTargets.reversed) { | 950 for (js.Node node in jumpTargets.reversed) { |
| 939 if (finallyLabels[node] != null) { | 951 if (finallyLabels[node] != null) { |
| 940 jumpStack.add(finallyLabels[node]); | 952 jumpStack.add(finallyLabels[node]); |
| 941 } else if (node == target) { | 953 } else if (node == target) { |
| 942 jumpStack.add(targetLabel); | 954 jumpStack.add(targetLabel); |
| (...skipping 482 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1425 break; | 1437 break; |
| 1426 } | 1438 } |
| 1427 int finallyLabel = finallyLabels[node]; | 1439 int finallyLabel = finallyLabels[node]; |
| 1428 if (finallyLabel != null) { | 1440 if (finallyLabel != null) { |
| 1429 result.add(finallyLabel); | 1441 result.add(finallyLabel); |
| 1430 } | 1442 } |
| 1431 } | 1443 } |
| 1432 return result.reversed.toList(); | 1444 return result.reversed.toList(); |
| 1433 } | 1445 } |
| 1434 | 1446 |
| 1435 /// See the comments of [visitFun] for more explanation. | 1447 /// See the comments of [rewriteFunction] for more explanation. |
| 1436 void visitTry(js.Try node) { | 1448 void visitTry(js.Try node) { |
| 1437 if (!shouldTransform(node)) { | 1449 if (!shouldTransform(node)) { |
| 1438 js.Block body = translateInBlock(node.body); | 1450 js.Block body = translateInBlock(node.body); |
| 1439 js.Catch catchPart = (node.catchPart == null) | 1451 js.Catch catchPart = (node.catchPart == null) |
| 1440 ? null | 1452 ? null |
| 1441 : new js.Catch(node.catchPart.declaration, | 1453 : new js.Catch(node.catchPart.declaration, |
| 1442 translateInBlock(node.catchPart.body)); | 1454 translateInBlock(node.catchPart.body)); |
| 1443 js.Block finallyPart = (node.finallyPart == null) | 1455 js.Block finallyPart = (node.finallyPart == null) |
| 1444 ? null | 1456 ? null |
| 1445 : translateInBlock(node.finallyPart); | 1457 : translateInBlock(node.finallyPart); |
| (...skipping 1135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2581 return condition || body; | 2593 return condition || body; |
| 2582 } | 2594 } |
| 2583 | 2595 |
| 2584 @override | 2596 @override |
| 2585 bool visitDartYield(js.DartYield node) { | 2597 bool visitDartYield(js.DartYield node) { |
| 2586 hasYield = true; | 2598 hasYield = true; |
| 2587 visit(node.expression); | 2599 visit(node.expression); |
| 2588 return true; | 2600 return true; |
| 2589 } | 2601 } |
| 2590 } | 2602 } |
| OLD | NEW |