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' as error_codes; |
11 as error_codes; | |
12 | 11 |
13 import "js.dart" as js; | 12 import "js.dart" as js; |
14 | 13 |
15 import '../common.dart'; | 14 import '../common.dart'; |
16 import '../util/util.dart' show | 15 import '../util/util.dart' show Pair; |
17 Pair; | |
18 | 16 |
19 /// 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 |
20 /// (with dart-like semantics) to an equivalent function without these. | 18 /// (with dart-like semantics) to an equivalent function without these. |
21 /// 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 |
22 /// in ssa/builder.dart). | 20 /// in ssa/builder.dart). |
23 /// | 21 /// |
24 /// 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 |
25 /// 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. |
26 /// (Currently handled in closure.dart). | 24 /// (Currently handled in closure.dart). |
27 /// | 25 /// |
28 /// Look at [rewriteFunction], [visitDartYield] and [visitAwait] for more | 26 /// Look at [rewriteFunction], [visitDartYield] and [visitAwait] for more |
29 /// explanation. | 27 /// explanation. |
30 abstract class AsyncRewriterBase extends js.NodeVisitor { | 28 abstract class AsyncRewriterBase extends js.NodeVisitor { |
31 | |
32 // Local variables are hoisted to the top of the function, so they are | 29 // Local variables are hoisted to the top of the function, so they are |
33 // collected here. | 30 // collected here. |
34 List<js.VariableDeclaration> localVariables = | 31 List<js.VariableDeclaration> localVariables = |
35 new List<js.VariableDeclaration>(); | 32 new List<js.VariableDeclaration>(); |
36 | 33 |
37 Map<js.Node, int> continueLabels = new Map<js.Node, int>(); | 34 Map<js.Node, int> continueLabels = new Map<js.Node, int>(); |
38 Map<js.Node, int> breakLabels = new Map<js.Node, int>(); | 35 Map<js.Node, int> breakLabels = new Map<js.Node, int>(); |
39 | 36 |
40 /// The label of a finally part. | 37 /// The label of a finally part. |
41 Map<js.Block, int> finallyLabels = new Map<js.Block, int>(); | 38 Map<js.Block, int> finallyLabels = new Map<js.Block, int>(); |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 // The highest temporary variable index currently in use. | 166 // The highest temporary variable index currently in use. |
170 int currentTempVarIndex = 0; | 167 int currentTempVarIndex = 0; |
171 // The highest temporary variable index ever in use in this function. | 168 // The highest temporary variable index ever in use in this function. |
172 int tempVarHighWaterMark = 0; | 169 int tempVarHighWaterMark = 0; |
173 Map<int, js.Expression> tempVarNames = new Map<int, js.Expression>(); | 170 Map<int, js.Expression> tempVarNames = new Map<int, js.Expression>(); |
174 | 171 |
175 bool get isAsync => false; | 172 bool get isAsync => false; |
176 bool get isSyncStar => false; | 173 bool get isSyncStar => false; |
177 bool get isAsyncStar => false; | 174 bool get isAsyncStar => false; |
178 | 175 |
179 AsyncRewriterBase(this.reporter, | 176 AsyncRewriterBase( |
180 this._spannable, | 177 this.reporter, this._spannable, this.safeVariableName, this.bodyName); |
181 this.safeVariableName, | |
182 this.bodyName); | |
183 | 178 |
184 /// Initialize names used by the subClass. | 179 /// Initialize names used by the subClass. |
185 void initializeNames(); | 180 void initializeNames(); |
186 | 181 |
187 /// Main entry point. | 182 /// Main entry point. |
188 /// Rewrites a sync*/async/async* function to an equivalent normal function. | 183 /// Rewrites a sync*/async/async* function to an equivalent normal function. |
189 /// | 184 /// |
190 /// [spannable] can be passed to have a location for error messages. | 185 /// [spannable] can be passed to have a location for error messages. |
191 js.Fun rewrite(js.Fun node, [Spannable spannable]) { | 186 js.Fun rewrite(js.Fun node, [Spannable spannable]) { |
192 _spannable = spannable; | 187 _spannable = spannable; |
(...skipping 12 matching lines...) Expand all Loading... |
205 currentErrorName = freshName("currentError"); | 200 currentErrorName = freshName("currentError"); |
206 outerLabelName = freshName("outer"); | 201 outerLabelName = freshName("outer"); |
207 selfName = freshName("self"); | 202 selfName = freshName("self"); |
208 // Initialize names specific to the subclass. | 203 // Initialize names specific to the subclass. |
209 initializeNames(); | 204 initializeNames(); |
210 | 205 |
211 return rewriteFunction(node); | 206 return rewriteFunction(node); |
212 } | 207 } |
213 | 208 |
214 js.Expression get currentErrorHandler { | 209 js.Expression get currentErrorHandler { |
215 return js.number(handlerLabels[jumpTargets.lastWhere( | 210 return js.number(handlerLabels[ |
216 (node) => handlerLabels[node] != null)]); | 211 jumpTargets.lastWhere((node) => handlerLabels[node] != null)]); |
217 } | 212 } |
218 | 213 |
219 int allocateTempVar() { | 214 int allocateTempVar() { |
220 assert(tempVarHighWaterMark >= currentTempVarIndex); | 215 assert(tempVarHighWaterMark >= currentTempVarIndex); |
221 currentTempVarIndex++; | 216 currentTempVarIndex++; |
222 tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark); | 217 tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark); |
223 return currentTempVarIndex; | 218 return currentTempVarIndex; |
224 } | 219 } |
225 | 220 |
226 js.VariableUse useTempVar(int i) { | 221 js.VariableUse useTempVar(int i) { |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 bool shouldTransform(js.Node node) { | 337 bool shouldTransform(js.Node node) { |
343 return analysis.hasAwaitOrYield.contains(node); | 338 return analysis.hasAwaitOrYield.contains(node); |
344 } | 339 } |
345 | 340 |
346 void unsupported(js.Node node) { | 341 void unsupported(js.Node node) { |
347 throw new UnsupportedError( | 342 throw new UnsupportedError( |
348 "Node $node cannot be transformed by the await-sync transformer"); | 343 "Node $node cannot be transformed by the await-sync transformer"); |
349 } | 344 } |
350 | 345 |
351 void unreachable(js.Node node) { | 346 void unreachable(js.Node node) { |
352 reporter.internalError( | 347 reporter.internalError(spannable, "Internal error, trying to visit $node"); |
353 spannable, "Internal error, trying to visit $node"); | |
354 } | 348 } |
355 | 349 |
356 visitStatement(js.Statement node) { | 350 visitStatement(js.Statement node) { |
357 node.accept(this); | 351 node.accept(this); |
358 } | 352 } |
359 | 353 |
360 /// Visits [node] to ensure its sideeffects are performed, but throwing away | 354 /// Visits [node] to ensure its sideeffects are performed, but throwing away |
361 /// the result. | 355 /// the result. |
362 /// | 356 /// |
363 /// If the return value of visiting [node] is an expression guaranteed to have | 357 /// If the return value of visiting [node] is an expression guaranteed to have |
364 /// no side effect, it is dropped. | 358 /// no side effect, it is dropped. |
365 void visitExpressionIgnoreResult(js.Expression node) { | 359 void visitExpressionIgnoreResult(js.Expression node) { |
366 js.Expression result = node.accept(this); | 360 js.Expression result = node.accept(this); |
367 if (!(result is js.Literal || result is js.VariableUse)) { | 361 if (!(result is js.Literal || result is js.VariableUse)) { |
368 addExpressionStatement(result); | 362 addExpressionStatement(result); |
369 } | 363 } |
370 } | 364 } |
371 | 365 |
372 js.Expression visitExpression(js.Expression node) { | 366 js.Expression visitExpression(js.Expression node) { |
373 return node.accept(this); | 367 return node.accept(this); |
374 } | 368 } |
375 | 369 |
376 | |
377 /// Calls [fn] with the value of evaluating [node1] and [node2]. | 370 /// Calls [fn] with the value of evaluating [node1] and [node2]. |
378 /// | 371 /// |
379 /// Both nodes are evaluated in order. | 372 /// Both nodes are evaluated in order. |
380 /// | 373 /// |
381 /// If node2 must be transformed (see [shouldTransform]), then the evaluation | 374 /// If node2 must be transformed (see [shouldTransform]), then the evaluation |
382 /// of node1 is added to the current statement-list and the result is stored | 375 /// of node1 is added to the current statement-list and the result is stored |
383 /// in a temporary variable. The evaluation of node2 is then free to emit | 376 /// in a temporary variable. The evaluation of node2 is then free to emit |
384 /// statements without affecting the result of node1. | 377 /// statements without affecting the result of node1. |
385 /// | 378 /// |
386 /// This is necessary, because await or yield expressions have to emit | 379 /// This is necessary, because await or yield expressions have to emit |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
422 /// If [store] is true the result of evaluating [node] is stored in a | 415 /// If [store] is true the result of evaluating [node] is stored in a |
423 /// temporary. | 416 /// temporary. |
424 /// | 417 /// |
425 /// We cannot rewrite `<receiver>.m()` to: | 418 /// We cannot rewrite `<receiver>.m()` to: |
426 /// temp = <receiver>.m; | 419 /// temp = <receiver>.m; |
427 /// temp(); | 420 /// temp(); |
428 /// Because this leaves `this` unbound in the call. But because of dart | 421 /// Because this leaves `this` unbound in the call. But because of dart |
429 /// evaluation order we can write: | 422 /// evaluation order we can write: |
430 /// temp = <receiver>; | 423 /// temp = <receiver>; |
431 /// temp.m(); | 424 /// temp.m(); |
432 withCallTargetExpression(js.Expression node, | 425 withCallTargetExpression(js.Expression node, fn(js.Expression result), |
433 fn(js.Expression result), {bool store}) { | 426 {bool store}) { |
434 int oldTempVarIndex = currentTempVarIndex; | 427 int oldTempVarIndex = currentTempVarIndex; |
435 js.Expression visited = visitExpression(node); | 428 js.Expression visited = visitExpression(node); |
436 js.Expression selector; | 429 js.Expression selector; |
437 js.Expression storedIfNeeded; | 430 js.Expression storedIfNeeded; |
438 if (store) { | 431 if (store) { |
439 if (visited is js.PropertyAccess) { | 432 if (visited is js.PropertyAccess) { |
440 js.PropertyAccess propertyAccess = visited; | 433 js.PropertyAccess propertyAccess = visited; |
441 selector = propertyAccess.selector; | 434 selector = propertyAccess.selector; |
442 visited = propertyAccess.receiver; | 435 visited = propertyAccess.receiver; |
443 } | 436 } |
444 storedIfNeeded = _storeIfNecessary(visited); | 437 storedIfNeeded = _storeIfNecessary(visited); |
445 } else { | 438 } else { |
446 storedIfNeeded = visited; | 439 storedIfNeeded = visited; |
447 } | 440 } |
448 js.Expression result; | 441 js.Expression result; |
449 if (selector == null) { | 442 if (selector == null) { |
450 result = fn(storedIfNeeded); | 443 result = fn(storedIfNeeded); |
451 } else { | 444 } else { |
452 result = fn(new js.PropertyAccess(storedIfNeeded, selector)); | 445 result = fn(new js.PropertyAccess(storedIfNeeded, selector)); |
453 } | 446 } |
454 currentTempVarIndex = oldTempVarIndex; | 447 currentTempVarIndex = oldTempVarIndex; |
455 return result; | 448 return result; |
456 } | 449 } |
457 | 450 |
458 | |
459 /// Calls [fn] with the value of evaluating [node1] and [node2]. | 451 /// Calls [fn] with the value of evaluating [node1] and [node2]. |
460 /// | 452 /// |
461 /// If `shouldTransform(node2)` the first expression is stored in a temporary | 453 /// If `shouldTransform(node2)` the first expression is stored in a temporary |
462 /// variable. | 454 /// variable. |
463 /// | 455 /// |
464 /// This is because node1 must be evaluated before visiting node2, | 456 /// This is because node1 must be evaluated before visiting node2, |
465 /// because the evaluation of an await or yield cannot be expressed as | 457 /// because the evaluation of an await or yield cannot be expressed as |
466 /// an expression, visiting node2 it will output statements that | 458 /// an expression, visiting node2 it will output statements that |
467 /// might have an influence on the value of node1. | 459 /// might have an influence on the value of node1. |
468 withExpression2(js.Expression node1, js.Expression node2, | 460 withExpression2(js.Expression node1, js.Expression node2, |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
518 /// Emits the block that control flows to if an error has been thrown | 510 /// Emits the block that control flows to if an error has been thrown |
519 /// but not caught. (after going through all the enclosing finally blocks). | 511 /// but not caught. (after going through all the enclosing finally blocks). |
520 void addErrorExit(); | 512 void addErrorExit(); |
521 | 513 |
522 void addFunctionExits() { | 514 void addFunctionExits() { |
523 addSuccesExit(); | 515 addSuccesExit(); |
524 addErrorExit(); | 516 addErrorExit(); |
525 } | 517 } |
526 | 518 |
527 /// Returns the rewritten function. | 519 /// Returns the rewritten function. |
528 js.Fun finishFunction(List<js.Parameter> parameters, | 520 js.Fun finishFunction( |
529 js.Statement rewrittenBody, | 521 List<js.Parameter> parameters, |
530 js.VariableDeclarationList variableDeclarations); | 522 js.Statement rewrittenBody, |
| 523 js.VariableDeclarationList variableDeclarations); |
531 | 524 |
532 Iterable<js.VariableInitialization> variableInitializations(); | 525 Iterable<js.VariableInitialization> variableInitializations(); |
533 | 526 |
534 /// Rewrites an async/sync*/async* function to a normal Javascript function. | 527 /// Rewrites an async/sync*/async* function to a normal Javascript function. |
535 /// | 528 /// |
536 /// The control flow is flattened by simulating 'goto' using a switch in a | 529 /// The control flow is flattened by simulating 'goto' using a switch in a |
537 /// loop and a state variable [goto] inside a nested function [body] | 530 /// loop and a state variable [goto] inside a nested function [body] |
538 /// that can be called back by [asyncStarHelper]/[asyncStarHelper]/the | 531 /// that can be called back by [asyncStarHelper]/[asyncStarHelper]/the |
539 /// [Iterator]. | 532 /// [Iterator]. |
540 /// | 533 /// |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
677 handlerLabels[node] = rethrowLabel; | 670 handlerLabels[node] = rethrowLabel; |
678 js.Statement body = node.body; | 671 js.Statement body = node.body; |
679 jumpTargets.add(node); | 672 jumpTargets.add(node); |
680 visitStatement(body); | 673 visitStatement(body); |
681 jumpTargets.removeLast(); | 674 jumpTargets.removeLast(); |
682 addFunctionExits(); | 675 addFunctionExits(); |
683 | 676 |
684 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { | 677 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { |
685 return new js.Case(js.number(label), new js.Block(labelledParts[label])); | 678 return new js.Case(js.number(label), new js.Block(labelledParts[label])); |
686 }).toList(); | 679 }).toList(); |
687 js.Statement rewrittenBody = | 680 js.Statement rewrittenBody = new js.Switch(goto, clauses); |
688 new js.Switch(goto, clauses); | |
689 if (hasJumpThoughOuterLabel) { | 681 if (hasJumpThoughOuterLabel) { |
690 rewrittenBody = new js.LabeledStatement(outerLabelName, rewrittenBody); | 682 rewrittenBody = new js.LabeledStatement(outerLabelName, rewrittenBody); |
691 } | 683 } |
692 rewrittenBody = js.js.statement('while (true) {#}', rewrittenBody); | 684 rewrittenBody = js.js.statement('while (true) {#}', rewrittenBody); |
693 List<js.VariableInitialization> variables = | 685 List<js.VariableInitialization> variables = |
694 new List<js.VariableInitialization>(); | 686 new List<js.VariableInitialization>(); |
695 | 687 |
696 variables.add(_makeVariableInitializer(goto, js.number(0))); | 688 variables.add(_makeVariableInitializer(goto, js.number(0))); |
697 variables.addAll(variableInitializations()); | 689 variables.addAll(variableInitializations()); |
698 variables.add( | 690 variables.add(_makeVariableInitializer(handler, js.number(rethrowLabel))); |
699 _makeVariableInitializer(handler, js.number(rethrowLabel))); | |
700 variables.add(_makeVariableInitializer(currentError, null)); | 691 variables.add(_makeVariableInitializer(currentError, null)); |
701 if (analysis.hasFinally || (isAsyncStar && analysis.hasYield)) { | 692 if (analysis.hasFinally || (isAsyncStar && analysis.hasYield)) { |
702 variables.add(_makeVariableInitializer(next, | 693 variables.add(_makeVariableInitializer( |
703 new js.ArrayInitializer(<js.Expression>[]))); | 694 next, new js.ArrayInitializer(<js.Expression>[]))); |
704 } | 695 } |
705 if (analysis.hasThis && !isSyncStar) { | 696 if (analysis.hasThis && !isSyncStar) { |
706 // Sync* functions must remember `this` on the level of the outer | 697 // Sync* functions must remember `this` on the level of the outer |
707 // function. | 698 // function. |
708 variables.add(_makeVariableInitializer(self, js.js('this'))); | 699 variables.add(_makeVariableInitializer(self, js.js('this'))); |
709 } | 700 } |
710 variables.addAll(localVariables.map( | 701 variables.addAll(localVariables.map((js.VariableDeclaration declaration) { |
711 (js.VariableDeclaration declaration) { | |
712 return new js.VariableInitialization(declaration, null); | 702 return new js.VariableInitialization(declaration, null); |
713 })); | 703 })); |
714 variables.addAll(new Iterable.generate(tempVarHighWaterMark, | 704 variables.addAll(new Iterable.generate(tempVarHighWaterMark, |
715 (int i) => _makeVariableInitializer(useTempVar(i + 1).name, null))); | 705 (int i) => _makeVariableInitializer(useTempVar(i + 1).name, null))); |
716 js.VariableDeclarationList variableDeclarations = | 706 js.VariableDeclarationList variableDeclarations = |
717 new js.VariableDeclarationList(variables); | 707 new js.VariableDeclarationList(variables); |
718 | 708 |
719 return finishFunction(node.params, rewrittenBody, variableDeclarations); | 709 return finishFunction(node.params, rewrittenBody, variableDeclarations); |
720 } | 710 } |
721 | 711 |
722 @override | 712 @override |
723 js.Expression visitFun(js.Fun node) { | 713 js.Expression visitFun(js.Fun node) { |
724 if (node.asyncModifier.isAsync || node.asyncModifier.isYielding) { | 714 if (node.asyncModifier.isAsync || node.asyncModifier.isYielding) { |
725 // The translation does not handle nested functions that are generators | 715 // The translation does not handle nested functions that are generators |
726 // or asynchronous. These functions should only be ones that are | 716 // or asynchronous. These functions should only be ones that are |
727 // introduced by JS foreign code from our own libraries. | 717 // introduced by JS foreign code from our own libraries. |
728 reporter.internalError(spannable, | 718 reporter.internalError( |
729 'Nested function is a generator or asynchronous.'); | 719 spannable, 'Nested function is a generator or asynchronous.'); |
730 } | 720 } |
731 return node; | 721 return node; |
732 } | 722 } |
733 | 723 |
734 @override | 724 @override |
735 js.Expression visitAccess(js.PropertyAccess node) { | 725 js.Expression visitAccess(js.PropertyAccess node) { |
736 return withExpression2(node.receiver, node.selector, | 726 return withExpression2(node.receiver, node.selector, |
737 (receiver, selector) => js.js('#[#]', [receiver, selector])); | 727 (receiver, selector) => js.js('#[#]', [receiver, selector])); |
738 } | 728 } |
739 | 729 |
(...skipping 14 matching lines...) Expand all Loading... |
754 if (!shouldTransform(node)) { | 744 if (!shouldTransform(node)) { |
755 return new js.Assignment.compound(visitExpression(node.leftHandSide), | 745 return new js.Assignment.compound(visitExpression(node.leftHandSide), |
756 node.op, visitExpression(node.value)); | 746 node.op, visitExpression(node.value)); |
757 } | 747 } |
758 js.Expression leftHandSide = node.leftHandSide; | 748 js.Expression leftHandSide = node.leftHandSide; |
759 if (leftHandSide is js.VariableUse) { | 749 if (leftHandSide is js.VariableUse) { |
760 return withExpression(node.value, (js.Expression value) { | 750 return withExpression(node.value, (js.Expression value) { |
761 // A non-compound [js.Assignment] has `op==null`. So it works out to | 751 // A non-compound [js.Assignment] has `op==null`. So it works out to |
762 // use [js.Assignment.compound] for all cases. | 752 // use [js.Assignment.compound] for all cases. |
763 // Visit the [js.VariableUse] to ensure renaming is done correctly. | 753 // Visit the [js.VariableUse] to ensure renaming is done correctly. |
764 return new js.Assignment.compound(visitExpression(leftHandSide), | 754 return new js.Assignment.compound( |
765 node.op, | 755 visitExpression(leftHandSide), node.op, value); |
766 value); | |
767 }, store: false); | 756 }, store: false); |
768 } else if (leftHandSide is js.PropertyAccess) { | 757 } else if (leftHandSide is js.PropertyAccess) { |
769 return withExpressions([ | 758 return withExpressions( |
770 leftHandSide.receiver, | 759 [leftHandSide.receiver, leftHandSide.selector, node.value], |
771 leftHandSide.selector, | 760 (evaluated) { |
772 node.value | |
773 ], (evaluated) { | |
774 return new js.Assignment.compound( | 761 return new js.Assignment.compound( |
775 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op, | 762 new js.PropertyAccess(evaluated[0], evaluated[1]), |
| 763 node.op, |
776 evaluated[2]); | 764 evaluated[2]); |
777 }); | 765 }); |
778 } else { | 766 } else { |
779 throw "Unexpected assignment left hand side $leftHandSide"; | 767 throw "Unexpected assignment left hand side $leftHandSide"; |
780 } | 768 } |
781 } | 769 } |
782 | 770 |
783 js.Statement awaitStatement(js.Expression value); | 771 js.Statement awaitStatement(js.Expression value); |
784 | 772 |
785 /// An await is translated to an [awaitStatement]. | 773 /// An await is translated to an [awaitStatement]. |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
880 } | 868 } |
881 | 869 |
882 @override | 870 @override |
883 void visitComment(js.Comment node) { | 871 void visitComment(js.Comment node) { |
884 addStatement(node); | 872 addStatement(node); |
885 } | 873 } |
886 | 874 |
887 @override | 875 @override |
888 js.Expression visitConditional(js.Conditional node) { | 876 js.Expression visitConditional(js.Conditional node) { |
889 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) { | 877 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) { |
890 return js.js('# ? # : #', [visitExpression(node.condition), | 878 return js.js('# ? # : #', [ |
891 visitExpression(node.then), | 879 visitExpression(node.condition), |
892 visitExpression(node.otherwise)]); | 880 visitExpression(node.then), |
| 881 visitExpression(node.otherwise) |
| 882 ]); |
893 } | 883 } |
894 int thenLabel = newLabel("then"); | 884 int thenLabel = newLabel("then"); |
895 int joinLabel = newLabel("join"); | 885 int joinLabel = newLabel("join"); |
896 int elseLabel = newLabel("else"); | 886 int elseLabel = newLabel("else"); |
897 withExpression(node.condition, (js.Expression condition) { | 887 withExpression(node.condition, (js.Expression condition) { |
898 addStatement(js.js.statement('# = # ? # : #;', | 888 addStatement(js.js.statement('# = # ? # : #;', |
899 [goto, condition, js.number(thenLabel), js.number(elseLabel)])); | 889 [goto, condition, js.number(thenLabel), js.number(elseLabel)])); |
900 }, store: false); | 890 }, store: false); |
901 addBreak(); | 891 addBreak(); |
902 beginLabel(thenLabel); | 892 beginLabel(thenLabel); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
970 | 960 |
971 @override | 961 @override |
972 void visitDefault(js.Default node) => unreachable(node); | 962 void visitDefault(js.Default node) => unreachable(node); |
973 | 963 |
974 @override | 964 @override |
975 void visitDo(js.Do node) { | 965 void visitDo(js.Do node) { |
976 if (!shouldTransform(node)) { | 966 if (!shouldTransform(node)) { |
977 bool oldInsideUntranslatedBreakable = insideUntranslatedBreakable; | 967 bool oldInsideUntranslatedBreakable = insideUntranslatedBreakable; |
978 insideUntranslatedBreakable = true; | 968 insideUntranslatedBreakable = true; |
979 addStatement(js.js.statement('do {#} while (#)', | 969 addStatement(js.js.statement('do {#} while (#)', |
980 [translateInBlock(node.body), | 970 [translateInBlock(node.body), visitExpression(node.condition)])); |
981 visitExpression(node.condition)])); | |
982 insideUntranslatedBreakable = oldInsideUntranslatedBreakable; | 971 insideUntranslatedBreakable = oldInsideUntranslatedBreakable; |
983 return; | 972 return; |
984 } | 973 } |
985 int startLabel = newLabel("do body"); | 974 int startLabel = newLabel("do body"); |
986 | 975 |
987 int continueLabel = newLabel("do condition"); | 976 int continueLabel = newLabel("do condition"); |
988 continueLabels[node] = continueLabel; | 977 continueLabels[node] = continueLabel; |
989 | 978 |
990 int afterLabel = newLabel("after do"); | 979 int afterLabel = newLabel("after do"); |
991 breakLabels[node] = afterLabel; | 980 breakLabels[node] = afterLabel; |
992 | 981 |
993 beginLabel(startLabel); | 982 beginLabel(startLabel); |
994 | 983 |
995 jumpTargets.add(node); | 984 jumpTargets.add(node); |
996 visitStatement(node.body); | 985 visitStatement(node.body); |
997 jumpTargets.removeLast(); | 986 jumpTargets.removeLast(); |
998 | 987 |
999 beginLabel(continueLabel); | 988 beginLabel(continueLabel); |
1000 withExpression(node.condition, (js.Expression condition) { | 989 withExpression(node.condition, (js.Expression condition) { |
1001 addStatement(js.js.statement('if (#) #', | 990 addStatement( |
1002 [condition, gotoAndBreak(startLabel)])); | 991 js.js.statement('if (#) #', [condition, gotoAndBreak(startLabel)])); |
1003 }, store: false); | 992 }, store: false); |
1004 beginLabel(afterLabel); | 993 beginLabel(afterLabel); |
1005 } | 994 } |
1006 | 995 |
1007 @override | 996 @override |
1008 void visitEmptyStatement(js.EmptyStatement node) { | 997 void visitEmptyStatement(js.EmptyStatement node) { |
1009 addStatement(node); | 998 addStatement(node); |
1010 } | 999 } |
1011 | 1000 |
1012 | |
1013 @override | 1001 @override |
1014 void visitExpressionStatement(js.ExpressionStatement node) { | 1002 void visitExpressionStatement(js.ExpressionStatement node) { |
1015 visitExpressionIgnoreResult(node.expression); | 1003 visitExpressionIgnoreResult(node.expression); |
1016 } | 1004 } |
1017 | 1005 |
1018 @override | 1006 @override |
1019 void visitFor(js.For node) { | 1007 void visitFor(js.For node) { |
1020 if (!shouldTransform(node)) { | 1008 if (!shouldTransform(node)) { |
1021 bool oldInsideUntranslated = insideUntranslatedBreakable; | 1009 bool oldInsideUntranslated = insideUntranslatedBreakable; |
1022 insideUntranslatedBreakable = true; | 1010 insideUntranslatedBreakable = true; |
1023 // Note that node.init, node.condition, node.update all can be null, but | 1011 // Note that node.init, node.condition, node.update all can be null, but |
1024 // withExpressions handles that. | 1012 // withExpressions handles that. |
1025 withExpressions([ | 1013 withExpressions([node.init, node.condition, node.update], |
1026 node.init, | 1014 (List<js.Expression> transformed) { |
1027 node.condition, | |
1028 node.update | |
1029 ], (List<js.Expression> transformed) { | |
1030 addStatement(new js.For(transformed[0], transformed[1], transformed[2], | 1015 addStatement(new js.For(transformed[0], transformed[1], transformed[2], |
1031 translateInBlock(node.body))); | 1016 translateInBlock(node.body))); |
1032 }); | 1017 }); |
1033 insideUntranslatedBreakable = oldInsideUntranslated; | 1018 insideUntranslatedBreakable = oldInsideUntranslated; |
1034 return; | 1019 return; |
1035 } | 1020 } |
1036 | 1021 |
1037 if (node.init != null) { | 1022 if (node.init != null) { |
1038 visitExpressionIgnoreResult(node.init); | 1023 visitExpressionIgnoreResult(node.init); |
1039 } | 1024 } |
1040 int startLabel = newLabel("for condition"); | 1025 int startLabel = newLabel("for condition"); |
1041 // If there is no update, continuing the loop is the same as going to the | 1026 // If there is no update, continuing the loop is the same as going to the |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1093 void visitIf(js.If node) { | 1078 void visitIf(js.If node) { |
1094 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) { | 1079 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) { |
1095 withExpression(node.condition, (js.Expression condition) { | 1080 withExpression(node.condition, (js.Expression condition) { |
1096 addStatement(new js.If(condition, translateInBlock(node.then), | 1081 addStatement(new js.If(condition, translateInBlock(node.then), |
1097 translateInBlock(node.otherwise))); | 1082 translateInBlock(node.otherwise))); |
1098 }, store: false); | 1083 }, store: false); |
1099 return; | 1084 return; |
1100 } | 1085 } |
1101 int thenLabel = newLabel("then"); | 1086 int thenLabel = newLabel("then"); |
1102 int joinLabel = newLabel("join"); | 1087 int joinLabel = newLabel("join"); |
1103 int elseLabel = (node.otherwise is js.EmptyStatement) | 1088 int elseLabel = |
1104 ? joinLabel | 1089 (node.otherwise is js.EmptyStatement) ? joinLabel : newLabel("else"); |
1105 : newLabel("else"); | |
1106 | 1090 |
1107 withExpression(node.condition, (js.Expression condition) { | 1091 withExpression(node.condition, (js.Expression condition) { |
1108 addExpressionStatement( | 1092 addExpressionStatement(new js.Assignment( |
1109 new js.Assignment( | 1093 goto, |
1110 goto, | 1094 new js.Conditional( |
1111 new js.Conditional( | 1095 condition, js.number(thenLabel), js.number(elseLabel)))); |
1112 condition, | |
1113 js.number(thenLabel), | |
1114 js.number(elseLabel)))); | |
1115 }, store: false); | 1096 }, store: false); |
1116 addBreak(); | 1097 addBreak(); |
1117 beginLabel(thenLabel); | 1098 beginLabel(thenLabel); |
1118 visitStatement(node.then); | 1099 visitStatement(node.then); |
1119 if (node.otherwise is! js.EmptyStatement) { | 1100 if (node.otherwise is! js.EmptyStatement) { |
1120 addGoto(joinLabel); | 1101 addGoto(joinLabel); |
1121 beginLabel(elseLabel); | 1102 beginLabel(elseLabel); |
1122 visitStatement(node.otherwise); | 1103 visitStatement(node.otherwise); |
1123 } | 1104 } |
1124 beginLabel(joinLabel); | 1105 beginLabel(joinLabel); |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1288 store: false); | 1269 store: false); |
1289 } | 1270 } |
1290 | 1271 |
1291 @override | 1272 @override |
1292 js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node; | 1273 js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node; |
1293 | 1274 |
1294 @override | 1275 @override |
1295 void visitReturn(js.Return node) { | 1276 void visitReturn(js.Return node) { |
1296 js.Node target = analysis.targets[node]; | 1277 js.Node target = analysis.targets[node]; |
1297 if (node.value != null) { | 1278 if (node.value != null) { |
1298 if(isSyncStar || isAsyncStar) { | 1279 if (isSyncStar || isAsyncStar) { |
1299 // Even though `return expr;` is not allowed in the dart sync* and | 1280 // Even though `return expr;` is not allowed in the dart sync* and |
1300 // async* code, the backend sometimes generates code like this, but | 1281 // async* code, the backend sometimes generates code like this, but |
1301 // only when it is known that the 'expr' throws, and the return is just | 1282 // only when it is known that the 'expr' throws, and the return is just |
1302 // to tell the JavaScript VM that the code won't continue here. | 1283 // to tell the JavaScript VM that the code won't continue here. |
1303 // It is therefore interpreted as `expr; return;` | 1284 // It is therefore interpreted as `expr; return;` |
1304 visitExpressionIgnoreResult(node.value); | 1285 visitExpressionIgnoreResult(node.value); |
1305 } else { | 1286 } else { |
1306 withExpression(node.value, (js.Expression value) { | 1287 withExpression(node.value, (js.Expression value) { |
1307 addStatement(js.js.statement("# = #;", [returnValue, value])); | 1288 addStatement(js.js.statement("# = #;", [returnValue, value])); |
1308 }, store: false); | 1289 }, store: false); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1370 } else { | 1351 } else { |
1371 addGoto(labels[defaultIndex]); | 1352 addGoto(labels[defaultIndex]); |
1372 } | 1353 } |
1373 } else { | 1354 } else { |
1374 bool hasDefault = false; | 1355 bool hasDefault = false; |
1375 int i = 0; | 1356 int i = 0; |
1376 List<js.SwitchClause> clauses = new List<js.SwitchClause>(); | 1357 List<js.SwitchClause> clauses = new List<js.SwitchClause>(); |
1377 for (js.SwitchClause clause in node.cases) { | 1358 for (js.SwitchClause clause in node.cases) { |
1378 if (clause is js.Case) { | 1359 if (clause is js.Case) { |
1379 labels[i] = newLabel("case"); | 1360 labels[i] = newLabel("case"); |
1380 clauses.add(new js.Case(visitExpression(clause.expression), | 1361 clauses.add(new js.Case( |
1381 gotoAndBreak(labels[i]))); | 1362 visitExpression(clause.expression), gotoAndBreak(labels[i]))); |
1382 } else if (clause is js.Default) { | 1363 } else if (clause is js.Default) { |
1383 labels[i] = newLabel("default"); | 1364 labels[i] = newLabel("default"); |
1384 clauses.add(new js.Default(gotoAndBreak(labels[i]))); | 1365 clauses.add(new js.Default(gotoAndBreak(labels[i]))); |
1385 hasDefault = true; | 1366 hasDefault = true; |
1386 } else { | 1367 } else { |
1387 reporter.internalError( | 1368 reporter.internalError(spannable, "Unknown clause type $clause"); |
1388 spannable, "Unknown clause type $clause"); | |
1389 } | 1369 } |
1390 i++; | 1370 i++; |
1391 } | 1371 } |
1392 if (!hasDefault) { | 1372 if (!hasDefault) { |
1393 clauses.add(new js.Default(gotoAndBreak(after))); | 1373 clauses.add(new js.Default(gotoAndBreak(after))); |
1394 } | 1374 } |
1395 withExpression(node.key, (js.Expression key) { | 1375 withExpression(node.key, (js.Expression key) { |
1396 addStatement(new js.Switch(key, clauses)); | 1376 addStatement(new js.Switch(key, clauses)); |
1397 }, store: false); | 1377 }, store: false); |
1398 | 1378 |
(...skipping 15 matching lines...) Expand all Loading... |
1414 } | 1394 } |
1415 | 1395 |
1416 @override | 1396 @override |
1417 void visitThrow(js.Throw node) { | 1397 void visitThrow(js.Throw node) { |
1418 withExpression(node.expression, (js.Expression expression) { | 1398 withExpression(node.expression, (js.Expression expression) { |
1419 addStatement(new js.Throw(expression)); | 1399 addStatement(new js.Throw(expression)); |
1420 }, store: false); | 1400 }, store: false); |
1421 } | 1401 } |
1422 | 1402 |
1423 setErrorHandler([int errorHandler]) { | 1403 setErrorHandler([int errorHandler]) { |
1424 js.Expression label = (errorHandler == null) | 1404 js.Expression label = |
1425 ? currentErrorHandler | 1405 (errorHandler == null) ? currentErrorHandler : js.number(errorHandler); |
1426 : js.number(errorHandler); | 1406 addStatement(js.js.statement('# = #;', [handler, label])); |
1427 addStatement(js.js.statement('# = #;',[handler, label])); | |
1428 } | 1407 } |
1429 | 1408 |
1430 List<int> _finalliesUpToAndEnclosingHandler() { | 1409 List<int> _finalliesUpToAndEnclosingHandler() { |
1431 List<int> result = new List<int>(); | 1410 List<int> result = new List<int>(); |
1432 for (int i = jumpTargets.length - 1; i >= 0; i--) { | 1411 for (int i = jumpTargets.length - 1; i >= 0; i--) { |
1433 js.Node node = jumpTargets[i]; | 1412 js.Node node = jumpTargets[i]; |
1434 int handlerLabel = handlerLabels[node]; | 1413 int handlerLabel = handlerLabels[node]; |
1435 if (handlerLabel != null) { | 1414 if (handlerLabel != null) { |
1436 result.add(handlerLabel); | 1415 result.add(handlerLabel); |
1437 break; | 1416 break; |
(...skipping 16 matching lines...) Expand all Loading... |
1454 translateInBlock(node.catchPart.body)); | 1433 translateInBlock(node.catchPart.body)); |
1455 js.Block finallyPart = (node.finallyPart == null) | 1434 js.Block finallyPart = (node.finallyPart == null) |
1456 ? null | 1435 ? null |
1457 : translateInBlock(node.finallyPart); | 1436 : translateInBlock(node.finallyPart); |
1458 addStatement(new js.Try(body, catchPart, finallyPart)); | 1437 addStatement(new js.Try(body, catchPart, finallyPart)); |
1459 return; | 1438 return; |
1460 } | 1439 } |
1461 | 1440 |
1462 hasTryBlocks = true; | 1441 hasTryBlocks = true; |
1463 int uncaughtLabel = newLabel("uncaught"); | 1442 int uncaughtLabel = newLabel("uncaught"); |
1464 int handlerLabel = (node.catchPart == null) | 1443 int handlerLabel = |
1465 ? uncaughtLabel | 1444 (node.catchPart == null) ? uncaughtLabel : newLabel("catch"); |
1466 : newLabel("catch"); | |
1467 | 1445 |
1468 int finallyLabel = newLabel("finally"); | 1446 int finallyLabel = newLabel("finally"); |
1469 int afterFinallyLabel = newLabel("after finally"); | 1447 int afterFinallyLabel = newLabel("after finally"); |
1470 if (node.finallyPart != null) { | 1448 if (node.finallyPart != null) { |
1471 finallyLabels[node.finallyPart] = finallyLabel; | 1449 finallyLabels[node.finallyPart] = finallyLabel; |
1472 jumpTargets.add(node.finallyPart); | 1450 jumpTargets.add(node.finallyPart); |
1473 } | 1451 } |
1474 | 1452 |
1475 handlerLabels[node] = handlerLabel; | 1453 handlerLabels[node] = handlerLabel; |
1476 jumpTargets.add(node); | 1454 jumpTargets.add(node); |
(...skipping 30 matching lines...) Expand all Loading... |
1507 String errorRename = freshName(node.catchPart.declaration.name); | 1485 String errorRename = freshName(node.catchPart.declaration.name); |
1508 localVariables.add(new js.VariableDeclaration(errorRename)); | 1486 localVariables.add(new js.VariableDeclaration(errorRename)); |
1509 variableRenamings | 1487 variableRenamings |
1510 .add(new Pair(node.catchPart.declaration.name, errorRename)); | 1488 .add(new Pair(node.catchPart.declaration.name, errorRename)); |
1511 addStatement(js.js.statement("# = #;", [errorRename, currentError])); | 1489 addStatement(js.js.statement("# = #;", [errorRename, currentError])); |
1512 visitStatement(node.catchPart.body); | 1490 visitStatement(node.catchPart.body); |
1513 variableRenamings.removeLast(); | 1491 variableRenamings.removeLast(); |
1514 if (node.finallyPart != null) { | 1492 if (node.finallyPart != null) { |
1515 // The error has been caught, so after the finally, continue after the | 1493 // The error has been caught, so after the finally, continue after the |
1516 // try. | 1494 // try. |
1517 addStatement( | 1495 addStatement(js.js |
1518 js.js.statement("#.push(#);", | 1496 .statement("#.push(#);", [next, js.number(afterFinallyLabel)])); |
1519 [next, js.number(afterFinallyLabel)])); | |
1520 addGoto(finallyLabel); | 1497 addGoto(finallyLabel); |
1521 } else { | 1498 } else { |
1522 addGoto(afterFinallyLabel); | 1499 addGoto(afterFinallyLabel); |
1523 } | 1500 } |
1524 js.Node last = jumpTargets.removeLast(); | 1501 js.Node last = jumpTargets.removeLast(); |
1525 assert(last == node.catchPart); | 1502 assert(last == node.catchPart); |
1526 } | 1503 } |
1527 | 1504 |
1528 // The "uncaught"-handler tells the finally-block to continue with | 1505 // The "uncaught"-handler tells the finally-block to continue with |
1529 // the enclosing finally-blocks until the current catch-handler. | 1506 // the enclosing finally-blocks until the current catch-handler. |
1530 beginLabel(uncaughtLabel); | 1507 beginLabel(uncaughtLabel); |
1531 | 1508 |
1532 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler(); | 1509 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler(); |
1533 | 1510 |
1534 int nextLabel = enclosingFinallies.removeLast(); | 1511 int nextLabel = enclosingFinallies.removeLast(); |
1535 if (enclosingFinallies.isNotEmpty) { | 1512 if (enclosingFinallies.isNotEmpty) { |
1536 // [enclosingFinallies] can be empty if there is no surrounding finally | 1513 // [enclosingFinallies] can be empty if there is no surrounding finally |
1537 // blocks. Then [nextLabel] will be [rethrowLabel]. | 1514 // blocks. Then [nextLabel] will be [rethrowLabel]. |
1538 addStatement( | 1515 addStatement(js.js.statement("# = #;", [ |
1539 js.js.statement("# = #;", [next, new js.ArrayInitializer( | 1516 next, |
1540 enclosingFinallies.map(js.number).toList())])); | 1517 new js.ArrayInitializer(enclosingFinallies.map(js.number).toList()) |
| 1518 ])); |
1541 } | 1519 } |
1542 if (node.finallyPart == null) { | 1520 if (node.finallyPart == null) { |
1543 // The finally-block belonging to [node] will be visited because of | 1521 // The finally-block belonging to [node] will be visited because of |
1544 // fallthrough. If it does not exist, add an explicit goto. | 1522 // fallthrough. If it does not exist, add an explicit goto. |
1545 addGoto(nextLabel); | 1523 addGoto(nextLabel); |
1546 } | 1524 } |
1547 if (node.finallyPart != null) { | 1525 if (node.finallyPart != null) { |
1548 js.Node last = jumpTargets.removeLast(); | 1526 js.Node last = jumpTargets.removeLast(); |
1549 assert(last == node.finallyPart); | 1527 assert(last == node.finallyPart); |
1550 | 1528 |
(...skipping 28 matching lines...) Expand all Loading... |
1579 } | 1557 } |
1580 | 1558 |
1581 @override | 1559 @override |
1582 void visitVariableInitialization(js.VariableInitialization node) { | 1560 void visitVariableInitialization(js.VariableInitialization node) { |
1583 unreachable(node); | 1561 unreachable(node); |
1584 } | 1562 } |
1585 | 1563 |
1586 @override | 1564 @override |
1587 js.Expression visitVariableUse(js.VariableUse node) { | 1565 js.Expression visitVariableUse(js.VariableUse node) { |
1588 Pair<String, String> renaming = variableRenamings.lastWhere( | 1566 Pair<String, String> renaming = variableRenamings.lastWhere( |
1589 (Pair renaming) => renaming.a == node.name, orElse: () => null); | 1567 (Pair renaming) => renaming.a == node.name, |
| 1568 orElse: () => null); |
1590 if (renaming == null) return node; | 1569 if (renaming == null) return node; |
1591 return new js.VariableUse(renaming.b); | 1570 return new js.VariableUse(renaming.b); |
1592 } | 1571 } |
1593 | 1572 |
1594 @override | 1573 @override |
1595 void visitWhile(js.While node) { | 1574 void visitWhile(js.While node) { |
1596 if (!shouldTransform(node)) { | 1575 if (!shouldTransform(node)) { |
1597 bool oldInsideUntranslated = insideUntranslatedBreakable; | 1576 bool oldInsideUntranslated = insideUntranslatedBreakable; |
1598 insideUntranslatedBreakable = true; | 1577 insideUntranslatedBreakable = true; |
1599 withExpression(node.condition, (js.Expression condition) { | 1578 withExpression(node.condition, (js.Expression condition) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1632 // Don't do a break here for the goto, but instead a return in either | 1611 // Don't do a break here for the goto, but instead a return in either |
1633 // addSynYield or addAsyncYield. | 1612 // addSynYield or addAsyncYield. |
1634 withExpression(node.expression, (js.Expression expression) { | 1613 withExpression(node.expression, (js.Expression expression) { |
1635 addStatement(setGotoVariable(label)); | 1614 addStatement(setGotoVariable(label)); |
1636 addYield(node, expression); | 1615 addYield(node, expression); |
1637 }, store: false); | 1616 }, store: false); |
1638 beginLabel(label); | 1617 beginLabel(label); |
1639 } | 1618 } |
1640 } | 1619 } |
1641 | 1620 |
1642 js.VariableInitialization | 1621 js.VariableInitialization _makeVariableInitializer( |
1643 _makeVariableInitializer(dynamic variable, js.Expression initValue) { | 1622 dynamic variable, js.Expression initValue) { |
1644 js.VariableDeclaration declaration; | 1623 js.VariableDeclaration declaration; |
1645 if (variable is js.VariableUse) { | 1624 if (variable is js.VariableUse) { |
1646 declaration = new js.VariableDeclaration(variable.name); | 1625 declaration = new js.VariableDeclaration(variable.name); |
1647 } else if (variable is String) { | 1626 } else if (variable is String) { |
1648 declaration = new js.VariableDeclaration(variable); | 1627 declaration = new js.VariableDeclaration(variable); |
1649 } else { | 1628 } else { |
1650 assert(variable is js.VariableDeclaration); | 1629 assert(variable is js.VariableDeclaration); |
1651 declaration = variable; | 1630 declaration = variable; |
1652 } | 1631 } |
1653 return new js.VariableInitialization(declaration, initValue); | 1632 return new js.VariableInitialization(declaration, initValue); |
1654 } | 1633 } |
1655 | 1634 |
1656 class AsyncRewriter extends AsyncRewriterBase { | 1635 class AsyncRewriter extends AsyncRewriterBase { |
1657 | |
1658 bool get isAsync => true; | 1636 bool get isAsync => true; |
1659 | 1637 |
1660 /// The Completer that will finish an async function. | 1638 /// The Completer that will finish an async function. |
1661 /// | 1639 /// |
1662 /// Not used for sync* or async* functions. | 1640 /// Not used for sync* or async* functions. |
1663 String completerName; | 1641 String completerName; |
1664 js.VariableUse get completer => new js.VariableUse(completerName); | 1642 js.VariableUse get completer => new js.VariableUse(completerName); |
1665 | 1643 |
1666 /// The function called by an async function to simulate an await or return. | 1644 /// The function called by an async function to simulate an await or return. |
1667 /// | 1645 /// |
(...skipping 16 matching lines...) Expand all Loading... |
1684 /// - The completer object [completer] | 1662 /// - The completer object [completer] |
1685 final js.Expression asyncHelper; | 1663 final js.Expression asyncHelper; |
1686 | 1664 |
1687 /// Contructor used to initialize the [completer] variable. | 1665 /// Contructor used to initialize the [completer] variable. |
1688 /// | 1666 /// |
1689 /// Specific to async methods. | 1667 /// Specific to async methods. |
1690 final js.Expression newCompleter; | 1668 final js.Expression newCompleter; |
1691 | 1669 |
1692 final js.Expression wrapBody; | 1670 final js.Expression wrapBody; |
1693 | 1671 |
1694 AsyncRewriter(DiagnosticReporter reporter, | 1672 AsyncRewriter(DiagnosticReporter reporter, Spannable spannable, |
1695 Spannable spannable, | 1673 {this.asyncHelper, |
1696 {this.asyncHelper, | 1674 this.newCompleter, |
1697 this.newCompleter, | 1675 this.wrapBody, |
1698 this.wrapBody, | 1676 String safeVariableName(String proposedName), |
1699 String safeVariableName(String proposedName), | 1677 js.Name bodyName}) |
1700 js.Name bodyName}) | 1678 : super(reporter, spannable, safeVariableName, bodyName); |
1701 : super(reporter, | |
1702 spannable, | |
1703 safeVariableName, | |
1704 bodyName); | |
1705 | 1679 |
1706 @override | 1680 @override |
1707 void addYield(js.DartYield node, js.Expression expression) { | 1681 void addYield(js.DartYield node, js.Expression expression) { |
1708 reporter.internalError(spannable, | 1682 reporter.internalError(spannable, "Yield in non-generating async function"); |
1709 "Yield in non-generating async function"); | |
1710 } | 1683 } |
1711 | 1684 |
1712 void addErrorExit() { | 1685 void addErrorExit() { |
1713 beginLabel(rethrowLabel); | 1686 beginLabel(rethrowLabel); |
1714 addStatement(js.js.statement( | 1687 addStatement(js.js.statement( |
1715 "return #thenHelper(#currentError, #errorCode, #completer);", { | 1688 "return #thenHelper(#currentError, #errorCode, #completer);", { |
1716 "thenHelper": asyncHelper, | 1689 "thenHelper": asyncHelper, |
1717 "errorCode": js.number(error_codes.ERROR), | 1690 "errorCode": js.number(error_codes.ERROR), |
1718 "currentError": currentError, | 1691 "currentError": currentError, |
1719 "completer": completer})); | 1692 "completer": completer |
| 1693 })); |
1720 } | 1694 } |
1721 | 1695 |
1722 /// Returning from an async method calls [asyncStarHelper] with the result. | 1696 /// Returning from an async method calls [asyncStarHelper] with the result. |
1723 /// (the result might have been stored in [returnValue] by some finally | 1697 /// (the result might have been stored in [returnValue] by some finally |
1724 /// block). | 1698 /// block). |
1725 void addSuccesExit() { | 1699 void addSuccesExit() { |
1726 if (analysis.hasExplicitReturns) { | 1700 if (analysis.hasExplicitReturns) { |
1727 beginLabel(exitLabel); | 1701 beginLabel(exitLabel); |
1728 } else { | 1702 } else { |
1729 addStatement(new js.Comment("implicit return")); | 1703 addStatement(new js.Comment("implicit return")); |
1730 } | 1704 } |
1731 addStatement(js.js.statement( | 1705 addStatement(js.js.statement( |
1732 "return #runtimeHelper(#returnValue, #successCode, " | 1706 "return #runtimeHelper(#returnValue, #successCode, " |
1733 "#completer, null);", { | 1707 "#completer, null);", |
1734 "runtimeHelper": asyncHelper, | 1708 { |
1735 "successCode": js.number(error_codes.SUCCESS), | 1709 "runtimeHelper": asyncHelper, |
1736 "returnValue": analysis.hasExplicitReturns | 1710 "successCode": js.number(error_codes.SUCCESS), |
1737 ? returnValue | 1711 "returnValue": |
1738 : new js.LiteralNull(), | 1712 analysis.hasExplicitReturns ? returnValue : new js.LiteralNull(), |
1739 "completer": completer})); | 1713 "completer": completer |
| 1714 })); |
1740 } | 1715 } |
1741 | 1716 |
1742 @override | 1717 @override |
1743 Iterable<js.VariableInitialization> variableInitializations() { | 1718 Iterable<js.VariableInitialization> variableInitializations() { |
1744 List<js.VariableInitialization> variables = | 1719 List<js.VariableInitialization> variables = |
1745 new List<js.VariableInitialization>(); | 1720 new List<js.VariableInitialization>(); |
1746 variables.add(_makeVariableInitializer(completer, | 1721 variables |
1747 new js.New(newCompleter, []))); | 1722 .add(_makeVariableInitializer(completer, new js.New(newCompleter, []))); |
1748 if (analysis.hasExplicitReturns) { | 1723 if (analysis.hasExplicitReturns) { |
1749 variables.add(_makeVariableInitializer(returnValue, null)); | 1724 variables.add(_makeVariableInitializer(returnValue, null)); |
1750 } | 1725 } |
1751 return variables; | 1726 return variables; |
1752 } | 1727 } |
1753 | 1728 |
1754 @override | 1729 @override |
1755 void initializeNames() { | 1730 void initializeNames() { |
1756 completerName = freshName("completer"); | 1731 completerName = freshName("completer"); |
1757 } | 1732 } |
1758 | 1733 |
1759 @override | 1734 @override |
1760 js.Statement awaitStatement(js.Expression value) { | 1735 js.Statement awaitStatement(js.Expression value) { |
1761 return js.js.statement(""" | 1736 return js.js.statement( |
| 1737 """ |
1762 return #asyncHelper(#value, | 1738 return #asyncHelper(#value, |
1763 #bodyName, | 1739 #bodyName, |
1764 #completer); | 1740 #completer); |
1765 """, { | 1741 """, |
1766 "asyncHelper": asyncHelper, | 1742 { |
1767 "value": value, | 1743 "asyncHelper": asyncHelper, |
1768 "bodyName": bodyName, | 1744 "value": value, |
1769 "completer": completer}); | 1745 "bodyName": bodyName, |
| 1746 "completer": completer |
| 1747 }); |
1770 } | 1748 } |
1771 | 1749 |
1772 @override | 1750 @override |
1773 js.Fun finishFunction(List<js.Parameter> parameters, | 1751 js.Fun finishFunction( |
1774 js.Statement rewrittenBody, | 1752 List<js.Parameter> parameters, |
1775 js.VariableDeclarationList variableDeclarations) { | 1753 js.Statement rewrittenBody, |
1776 return js.js(""" | 1754 js.VariableDeclarationList variableDeclarations) { |
| 1755 return js.js( |
| 1756 """ |
1777 function (#parameters) { | 1757 function (#parameters) { |
1778 #variableDeclarations; | 1758 #variableDeclarations; |
1779 var #bodyName = #wrapBody(function (#errorCode, #result) { | 1759 var #bodyName = #wrapBody(function (#errorCode, #result) { |
1780 if (#errorCode === #ERROR) { | 1760 if (#errorCode === #ERROR) { |
1781 #currentError = #result; | 1761 #currentError = #result; |
1782 #goto = #handler; | 1762 #goto = #handler; |
1783 } | 1763 } |
1784 #rewrittenBody; | 1764 #rewrittenBody; |
1785 }); | 1765 }); |
1786 return #asyncHelper(null, #bodyName, #completer, null); | 1766 return #asyncHelper(null, #bodyName, #completer, null); |
1787 }""", { | 1767 }""", |
| 1768 { |
1788 "parameters": parameters, | 1769 "parameters": parameters, |
1789 "variableDeclarations": variableDeclarations, | 1770 "variableDeclarations": variableDeclarations, |
1790 "ERROR": js.number(error_codes.ERROR), | 1771 "ERROR": js.number(error_codes.ERROR), |
1791 "rewrittenBody": rewrittenBody, | 1772 "rewrittenBody": rewrittenBody, |
1792 "bodyName": bodyName, | 1773 "bodyName": bodyName, |
1793 "currentError": currentError, | 1774 "currentError": currentError, |
1794 "goto": goto, | 1775 "goto": goto, |
1795 "handler": handler, | 1776 "handler": handler, |
1796 "errorCode": errorCodeName, | 1777 "errorCode": errorCodeName, |
1797 "result": resultName, | 1778 "result": resultName, |
1798 "asyncHelper": asyncHelper, | 1779 "asyncHelper": asyncHelper, |
1799 "completer": completer, | 1780 "completer": completer, |
1800 "wrapBody": wrapBody, | 1781 "wrapBody": wrapBody, |
1801 }); | 1782 }); |
1802 } | 1783 } |
1803 } | 1784 } |
1804 | 1785 |
1805 class SyncStarRewriter extends AsyncRewriterBase { | 1786 class SyncStarRewriter extends AsyncRewriterBase { |
1806 | |
1807 bool get isSyncStar => true; | 1787 bool get isSyncStar => true; |
1808 | 1788 |
1809 /// Contructor creating the Iterable for a sync* method. Called with | 1789 /// Contructor creating the Iterable for a sync* method. Called with |
1810 /// [bodyName]. | 1790 /// [bodyName]. |
1811 final js.Expression newIterable; | 1791 final js.Expression newIterable; |
1812 | 1792 |
1813 /// A JS Expression that creates a marker showing that iteration is over. | 1793 /// A JS Expression that creates a marker showing that iteration is over. |
1814 /// | 1794 /// |
1815 /// Called without arguments. | 1795 /// Called without arguments. |
1816 final js.Expression endOfIteration; | 1796 final js.Expression endOfIteration; |
1817 | 1797 |
1818 /// A JS Expression that creates a marker indication a 'yield*' statement. | 1798 /// A JS Expression that creates a marker indication a 'yield*' statement. |
1819 /// | 1799 /// |
1820 /// Called with the stream to yield from. | 1800 /// Called with the stream to yield from. |
1821 final js.Expression yieldStarExpression; | 1801 final js.Expression yieldStarExpression; |
1822 | 1802 |
1823 /// Used by sync* functions to throw exeptions. | 1803 /// Used by sync* functions to throw exeptions. |
1824 final js.Expression uncaughtErrorExpression; | 1804 final js.Expression uncaughtErrorExpression; |
1825 | 1805 |
1826 SyncStarRewriter(DiagnosticReporter diagnosticListener, | 1806 SyncStarRewriter(DiagnosticReporter diagnosticListener, spannable, |
1827 spannable, | 1807 {this.endOfIteration, |
1828 {this.endOfIteration, | 1808 this.newIterable, |
1829 this.newIterable, | 1809 this.yieldStarExpression, |
1830 this.yieldStarExpression, | 1810 this.uncaughtErrorExpression, |
1831 this.uncaughtErrorExpression, | 1811 String safeVariableName(String proposedName), |
1832 String safeVariableName(String proposedName), | 1812 js.Name bodyName}) |
1833 js.Name bodyName}) | 1813 : super(diagnosticListener, spannable, safeVariableName, bodyName); |
1834 : super(diagnosticListener, | |
1835 spannable, | |
1836 safeVariableName, | |
1837 bodyName); | |
1838 | 1814 |
1839 /// Translates a yield/yield* in an sync*. | 1815 /// Translates a yield/yield* in an sync*. |
1840 /// | 1816 /// |
1841 /// `yield` in a sync* function just returns [value]. | 1817 /// `yield` in a sync* function just returns [value]. |
1842 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it. | 1818 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it. |
1843 @override | 1819 @override |
1844 void addYield(js.DartYield node, js.Expression expression) { | 1820 void addYield(js.DartYield node, js.Expression expression) { |
1845 if (node.hasStar) { | 1821 if (node.hasStar) { |
1846 addStatement( | 1822 addStatement( |
1847 new js.Return(new js.Call(yieldStarExpression, [expression]))); | 1823 new js.Return(new js.Call(yieldStarExpression, [expression]))); |
1848 } else { | 1824 } else { |
1849 addStatement(new js.Return(expression)); | 1825 addStatement(new js.Return(expression)); |
1850 } | 1826 } |
1851 } | 1827 } |
1852 | 1828 |
1853 @override | 1829 @override |
1854 js.Fun finishFunction(List<js.Parameter> parameters, | 1830 js.Fun finishFunction( |
1855 js.Statement rewrittenBody, | 1831 List<js.Parameter> parameters, |
1856 js.VariableDeclarationList variableDeclarations) { | 1832 js.Statement rewrittenBody, |
| 1833 js.VariableDeclarationList variableDeclarations) { |
1857 // Each iterator invocation on the iterable should work on its own copy of | 1834 // Each iterator invocation on the iterable should work on its own copy of |
1858 // the parameters. | 1835 // the parameters. |
1859 // TODO(sigurdm): We only need to do this copying for parameters that are | 1836 // TODO(sigurdm): We only need to do this copying for parameters that are |
1860 // mutated. | 1837 // mutated. |
1861 List<js.VariableInitialization> declarations = | 1838 List<js.VariableInitialization> declarations = |
1862 new List<js.VariableInitialization>(); | 1839 new List<js.VariableInitialization>(); |
1863 List<js.Parameter> renamedParameters = new List<js.Parameter>(); | 1840 List<js.Parameter> renamedParameters = new List<js.Parameter>(); |
1864 for (js.Parameter parameter in parameters) { | 1841 for (js.Parameter parameter in parameters) { |
1865 String name = parameter.name; | 1842 String name = parameter.name; |
1866 String renamedName = freshName(name); | 1843 String renamedName = freshName(name); |
1867 renamedParameters.add(new js.Parameter(renamedName)); | 1844 renamedParameters.add(new js.Parameter(renamedName)); |
1868 declarations.add( | 1845 declarations.add(new js.VariableInitialization( |
1869 new js.VariableInitialization(new js.VariableDeclaration(name), | 1846 new js.VariableDeclaration(name), new js.VariableUse(renamedName))); |
1870 new js.VariableUse(renamedName))); | |
1871 } | 1847 } |
1872 js.VariableDeclarationList copyParameters = | 1848 js.VariableDeclarationList copyParameters = |
1873 new js.VariableDeclarationList(declarations); | 1849 new js.VariableDeclarationList(declarations); |
1874 return js.js(""" | 1850 return js.js( |
| 1851 """ |
1875 function (#renamedParameters) { | 1852 function (#renamedParameters) { |
1876 if (#needsThis) | 1853 if (#needsThis) |
1877 var #self = this; | 1854 var #self = this; |
1878 return new #newIterable(function () { | 1855 return new #newIterable(function () { |
1879 if (#hasParameters) { | 1856 if (#hasParameters) { |
1880 #copyParameters; | 1857 #copyParameters; |
1881 } | 1858 } |
1882 #varDecl; | 1859 #varDecl; |
1883 return function #body(#errorCode, #result) { | 1860 return function #body(#errorCode, #result) { |
1884 if (#errorCode === #ERROR) { | 1861 if (#errorCode === #ERROR) { |
1885 #currentError = #result; | 1862 #currentError = #result; |
1886 #goto = #handler; | 1863 #goto = #handler; |
1887 } | 1864 } |
1888 #helperBody; | 1865 #helperBody; |
1889 }; | 1866 }; |
1890 }); | 1867 }); |
1891 } | 1868 } |
1892 """, { | 1869 """, |
1893 "renamedParameters": renamedParameters, | 1870 { |
1894 "needsThis": analysis.hasThis, | 1871 "renamedParameters": renamedParameters, |
1895 "helperBody": rewrittenBody, | 1872 "needsThis": analysis.hasThis, |
1896 "hasParameters": parameters.isNotEmpty, | 1873 "helperBody": rewrittenBody, |
1897 "copyParameters": copyParameters, | 1874 "hasParameters": parameters.isNotEmpty, |
1898 "varDecl": variableDeclarations, | 1875 "copyParameters": copyParameters, |
1899 "errorCode": errorCodeName, | 1876 "varDecl": variableDeclarations, |
1900 "newIterable": newIterable, | 1877 "errorCode": errorCodeName, |
1901 "body": bodyName, | 1878 "newIterable": newIterable, |
1902 "self": selfName, | 1879 "body": bodyName, |
1903 "result": resultName, | 1880 "self": selfName, |
1904 "goto": goto, | 1881 "result": resultName, |
1905 "handler": handler, | 1882 "goto": goto, |
1906 "currentError": currentErrorName, | 1883 "handler": handler, |
1907 "ERROR": js.number(error_codes.ERROR), | 1884 "currentError": currentErrorName, |
1908 }); | 1885 "ERROR": js.number(error_codes.ERROR), |
| 1886 }); |
1909 } | 1887 } |
1910 | 1888 |
1911 void addErrorExit() { | 1889 void addErrorExit() { |
1912 beginLabel(rethrowLabel); | 1890 beginLabel(rethrowLabel); |
1913 addStatement(js.js.statement('return #(#);', | 1891 addStatement(js.js |
1914 [uncaughtErrorExpression, currentError])); | 1892 .statement('return #(#);', [uncaughtErrorExpression, currentError])); |
1915 } | 1893 } |
1916 | 1894 |
1917 /// Returning from a sync* function returns an [endOfIteration] marker. | 1895 /// Returning from a sync* function returns an [endOfIteration] marker. |
1918 void addSuccesExit() { | 1896 void addSuccesExit() { |
1919 if (analysis.hasExplicitReturns) { | 1897 if (analysis.hasExplicitReturns) { |
1920 beginLabel(exitLabel); | 1898 beginLabel(exitLabel); |
1921 } else { | 1899 } else { |
1922 addStatement(new js.Comment("implicit return")); | 1900 addStatement(new js.Comment("implicit return")); |
1923 } | 1901 } |
1924 addStatement(js.js.statement('return #();', [endOfIteration])); | 1902 addStatement(js.js.statement('return #();', [endOfIteration])); |
1925 } | 1903 } |
1926 | 1904 |
1927 @override | 1905 @override |
1928 Iterable<js.VariableInitialization> variableInitializations() { | 1906 Iterable<js.VariableInitialization> variableInitializations() { |
1929 List<js.VariableInitialization> variables = | 1907 List<js.VariableInitialization> variables = |
1930 new List<js.VariableInitialization>(); | 1908 new List<js.VariableInitialization>(); |
1931 return variables; | 1909 return variables; |
1932 } | 1910 } |
1933 | 1911 |
1934 @override | 1912 @override |
1935 js.Statement awaitStatement(js.Expression value) { | 1913 js.Statement awaitStatement(js.Expression value) { |
1936 throw reporter.internalError(spannable, | 1914 throw reporter.internalError( |
1937 "Sync* functions cannot contain await statements."); | 1915 spannable, "Sync* functions cannot contain await statements."); |
1938 } | 1916 } |
1939 | 1917 |
1940 @override | 1918 @override |
1941 void initializeNames() {} | 1919 void initializeNames() {} |
1942 } | 1920 } |
1943 | 1921 |
1944 class AsyncStarRewriter extends AsyncRewriterBase { | 1922 class AsyncStarRewriter extends AsyncRewriterBase { |
1945 | |
1946 bool get isAsyncStar => true; | 1923 bool get isAsyncStar => true; |
1947 | 1924 |
1948 /// The stack of labels of finally blocks to assign to [next] if the | 1925 /// The stack of labels of finally blocks to assign to [next] if the |
1949 /// async* [StreamSubscription] was canceled during a yield. | 1926 /// async* [StreamSubscription] was canceled during a yield. |
1950 js.VariableUse get nextWhenCanceled { | 1927 js.VariableUse get nextWhenCanceled { |
1951 return new js.VariableUse(nextWhenCanceledName); | 1928 return new js.VariableUse(nextWhenCanceledName); |
1952 } | 1929 } |
| 1930 |
1953 String nextWhenCanceledName; | 1931 String nextWhenCanceledName; |
1954 | 1932 |
1955 /// The StreamController that controls an async* function. | 1933 /// The StreamController that controls an async* function. |
1956 String controllerName; | 1934 String controllerName; |
1957 js.VariableUse get controller => new js.VariableUse(controllerName); | 1935 js.VariableUse get controller => new js.VariableUse(controllerName); |
1958 | 1936 |
1959 /// The function called by an async* function to simulate an await, yield or | 1937 /// The function called by an async* function to simulate an await, yield or |
1960 /// yield*. | 1938 /// yield*. |
1961 /// | 1939 /// |
1962 /// For an await/yield/yield* it is called with: | 1940 /// For an await/yield/yield* it is called with: |
(...skipping 24 matching lines...) Expand all Loading... |
1987 /// Called with the value to yield. | 1965 /// Called with the value to yield. |
1988 final js.Expression yieldExpression; | 1966 final js.Expression yieldExpression; |
1989 | 1967 |
1990 /// A JS Expression that creates a marker indication a 'yield*' statement. | 1968 /// A JS Expression that creates a marker indication a 'yield*' statement. |
1991 /// | 1969 /// |
1992 /// Called with the stream to yield from. | 1970 /// Called with the stream to yield from. |
1993 final js.Expression yieldStarExpression; | 1971 final js.Expression yieldStarExpression; |
1994 | 1972 |
1995 final js.Expression wrapBody; | 1973 final js.Expression wrapBody; |
1996 | 1974 |
1997 AsyncStarRewriter(DiagnosticReporter reporter, | 1975 AsyncStarRewriter(DiagnosticReporter reporter, Spannable spannable, |
1998 Spannable spannable, | 1976 {this.asyncStarHelper, |
1999 {this.asyncStarHelper, | 1977 this.streamOfController, |
2000 this.streamOfController, | 1978 this.newController, |
2001 this.newController, | 1979 this.yieldExpression, |
2002 this.yieldExpression, | 1980 this.yieldStarExpression, |
2003 this.yieldStarExpression, | 1981 this.wrapBody, |
2004 this.wrapBody, | 1982 String safeVariableName(String proposedName), |
2005 String safeVariableName(String proposedName), | 1983 js.Name bodyName}) |
2006 js.Name bodyName}) | 1984 : super(reporter, spannable, safeVariableName, bodyName); |
2007 : super(reporter, | |
2008 spannable, | |
2009 safeVariableName, | |
2010 bodyName); | |
2011 | |
2012 | 1985 |
2013 /// Translates a yield/yield* in an async* function. | 1986 /// Translates a yield/yield* in an async* function. |
2014 /// | 1987 /// |
2015 /// yield/yield* in an async* function is translated much like the `await` is | 1988 /// yield/yield* in an async* function is translated much like the `await` is |
2016 /// translated in [visitAwait], only the object is wrapped in a | 1989 /// translated in [visitAwait], only the object is wrapped in a |
2017 /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper] | 1990 /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper] |
2018 /// distinguish them. | 1991 /// distinguish them. |
2019 /// Also [nextWhenCanceled] is set up to contain the finally blocks that | 1992 /// Also [nextWhenCanceled] is set up to contain the finally blocks that |
2020 /// must be run in case the stream was canceled. | 1993 /// must be run in case the stream was canceled. |
2021 @override | 1994 @override |
2022 void addYield(js.DartYield node, js.Expression expression) { | 1995 void addYield(js.DartYield node, js.Expression expression) { |
2023 // Find all the finally blocks that should be performed if the stream is | 1996 // Find all the finally blocks that should be performed if the stream is |
2024 // canceled during the yield. | 1997 // canceled during the yield. |
2025 // At the bottom of the stack is the return label. | 1998 // At the bottom of the stack is the return label. |
2026 List<int> enclosingFinallyLabels = <int>[exitLabel]; | 1999 List<int> enclosingFinallyLabels = <int>[exitLabel]; |
2027 enclosingFinallyLabels.addAll(jumpTargets | 2000 enclosingFinallyLabels.addAll(jumpTargets |
2028 .where((js.Node node) => finallyLabels[node] != null) | 2001 .where((js.Node node) => finallyLabels[node] != null) |
2029 .map((js.Block node) => finallyLabels[node])); | 2002 .map((js.Block node) => finallyLabels[node])); |
2030 addStatement(js.js.statement("# = #;", | 2003 addStatement(js.js.statement("# = #;", [ |
2031 [nextWhenCanceled, new js.ArrayInitializer( | 2004 nextWhenCanceled, |
2032 enclosingFinallyLabels.map(js.number).toList())])); | 2005 new js.ArrayInitializer(enclosingFinallyLabels.map(js.number).toList()) |
2033 addStatement(js.js.statement(""" | 2006 ])); |
| 2007 addStatement(js.js.statement( |
| 2008 """ |
2034 return #asyncStarHelper(#yieldExpression(#expression), #bodyName, | 2009 return #asyncStarHelper(#yieldExpression(#expression), #bodyName, |
2035 #controller);""", { | 2010 #controller);""", |
2036 "asyncStarHelper": asyncStarHelper, | 2011 { |
2037 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression, | 2012 "asyncStarHelper": asyncStarHelper, |
2038 "expression": expression, | 2013 "yieldExpression": |
2039 "bodyName": bodyName, | 2014 node.hasStar ? yieldStarExpression : yieldExpression, |
2040 "controller": controllerName, | 2015 "expression": expression, |
2041 })); | 2016 "bodyName": bodyName, |
| 2017 "controller": controllerName, |
| 2018 })); |
2042 } | 2019 } |
2043 | 2020 |
2044 @override | 2021 @override |
2045 js.Fun finishFunction(List<js.Parameter> parameters, | 2022 js.Fun finishFunction( |
2046 js.Statement rewrittenBody, | 2023 List<js.Parameter> parameters, |
2047 js.VariableDeclarationList variableDeclarations) { | 2024 js.Statement rewrittenBody, |
2048 return js.js(""" | 2025 js.VariableDeclarationList variableDeclarations) { |
| 2026 return js.js( |
| 2027 """ |
2049 function (#parameters) { | 2028 function (#parameters) { |
2050 var #bodyName = #wrapBody(function (#errorCode, #result) { | 2029 var #bodyName = #wrapBody(function (#errorCode, #result) { |
2051 if (#hasYield) { | 2030 if (#hasYield) { |
2052 switch (#errorCode) { | 2031 switch (#errorCode) { |
2053 case #STREAM_WAS_CANCELED: | 2032 case #STREAM_WAS_CANCELED: |
2054 #next = #nextWhenCanceled; | 2033 #next = #nextWhenCanceled; |
2055 #goto = #next.pop(); | 2034 #goto = #next.pop(); |
2056 break; | 2035 break; |
2057 case #ERROR: | 2036 case #ERROR: |
2058 #currentError = #result; | 2037 #currentError = #result; |
2059 #goto = #handler; | 2038 #goto = #handler; |
2060 } | 2039 } |
2061 } else { | 2040 } else { |
2062 if (#errorCode === #ERROR) { | 2041 if (#errorCode === #ERROR) { |
2063 #currentError = #result; | 2042 #currentError = #result; |
2064 #goto = #handler; | 2043 #goto = #handler; |
2065 } | 2044 } |
2066 } | 2045 } |
2067 #rewrittenBody; | 2046 #rewrittenBody; |
2068 }); | 2047 }); |
2069 #variableDeclarations; | 2048 #variableDeclarations; |
2070 return #streamOfController(#controller); | 2049 return #streamOfController(#controller); |
2071 }""", { | 2050 }""", |
| 2051 { |
2072 "parameters": parameters, | 2052 "parameters": parameters, |
2073 "variableDeclarations": variableDeclarations, | 2053 "variableDeclarations": variableDeclarations, |
2074 "STREAM_WAS_CANCELED": js.number(error_codes.STREAM_WAS_CANCELED), | 2054 "STREAM_WAS_CANCELED": js.number(error_codes.STREAM_WAS_CANCELED), |
2075 "ERROR": js.number(error_codes.ERROR), | 2055 "ERROR": js.number(error_codes.ERROR), |
2076 "hasYield": analysis.hasYield, | 2056 "hasYield": analysis.hasYield, |
2077 "rewrittenBody": rewrittenBody, | 2057 "rewrittenBody": rewrittenBody, |
2078 "bodyName": bodyName, | 2058 "bodyName": bodyName, |
2079 "currentError": currentError, | 2059 "currentError": currentError, |
2080 "goto": goto, | 2060 "goto": goto, |
2081 "handler": handler, | 2061 "handler": handler, |
2082 "next": next, | 2062 "next": next, |
2083 "nextWhenCanceled": nextWhenCanceled, | 2063 "nextWhenCanceled": nextWhenCanceled, |
2084 "errorCode": errorCodeName, | 2064 "errorCode": errorCodeName, |
2085 "result": resultName, | 2065 "result": resultName, |
2086 "streamOfController": streamOfController, | 2066 "streamOfController": streamOfController, |
2087 "controller": controllerName, | 2067 "controller": controllerName, |
2088 "wrapBody": wrapBody, | 2068 "wrapBody": wrapBody, |
2089 }); | 2069 }); |
2090 } | 2070 } |
2091 | 2071 |
2092 @override | 2072 @override |
2093 void addErrorExit() { | 2073 void addErrorExit() { |
2094 beginLabel(rethrowLabel); | 2074 beginLabel(rethrowLabel); |
2095 addStatement(js.js.statement( | 2075 addStatement(js.js.statement( |
2096 "return #asyncHelper(#currentError, #errorCode, #controller);", { | 2076 "return #asyncHelper(#currentError, #errorCode, #controller);", { |
2097 "asyncHelper": asyncStarHelper, | 2077 "asyncHelper": asyncStarHelper, |
2098 "errorCode": js.number(error_codes.ERROR), | 2078 "errorCode": js.number(error_codes.ERROR), |
2099 "currentError": currentError, | 2079 "currentError": currentError, |
2100 "controller": controllerName})); | 2080 "controller": controllerName |
| 2081 })); |
2101 } | 2082 } |
2102 | 2083 |
2103 /// Returning from an async* function calls the [streamHelper] with an | 2084 /// Returning from an async* function calls the [streamHelper] with an |
2104 /// [endOfIteration] marker. | 2085 /// [endOfIteration] marker. |
2105 @override | 2086 @override |
2106 void addSuccesExit() { | 2087 void addSuccesExit() { |
2107 beginLabel(exitLabel); | 2088 beginLabel(exitLabel); |
2108 | 2089 |
2109 addStatement(js.js.statement( | 2090 addStatement(js.js |
2110 "return #streamHelper(null, #successCode, #controller);", { | 2091 .statement("return #streamHelper(null, #successCode, #controller);", { |
2111 "streamHelper": asyncStarHelper, | 2092 "streamHelper": asyncStarHelper, |
2112 "successCode": js.number(error_codes.SUCCESS), | 2093 "successCode": js.number(error_codes.SUCCESS), |
2113 "controller": controllerName})); | 2094 "controller": controllerName |
| 2095 })); |
2114 } | 2096 } |
2115 | 2097 |
2116 @override | 2098 @override |
2117 Iterable<js.VariableInitialization> variableInitializations() { | 2099 Iterable<js.VariableInitialization> variableInitializations() { |
2118 List<js.VariableInitialization> variables = | 2100 List<js.VariableInitialization> variables = |
2119 new List<js.VariableInitialization>(); | 2101 new List<js.VariableInitialization>(); |
2120 variables.add(_makeVariableInitializer(controller, | 2102 variables.add(_makeVariableInitializer( |
2121 js.js('#(#)', | 2103 controller, js.js('#(#)', [newController, bodyName]))); |
2122 [newController, bodyName]))); | |
2123 if (analysis.hasYield) { | 2104 if (analysis.hasYield) { |
2124 variables.add(_makeVariableInitializer(nextWhenCanceled, null)); | 2105 variables.add(_makeVariableInitializer(nextWhenCanceled, null)); |
2125 } | 2106 } |
2126 return variables; | 2107 return variables; |
2127 } | 2108 } |
2128 | 2109 |
2129 @override | 2110 @override |
2130 void initializeNames() { | 2111 void initializeNames() { |
2131 controllerName = freshName("controller"); | 2112 controllerName = freshName("controller"); |
2132 nextWhenCanceledName = freshName("nextWhenCanceled"); | 2113 nextWhenCanceledName = freshName("nextWhenCanceled"); |
2133 } | 2114 } |
2134 | 2115 |
2135 @override | 2116 @override |
2136 js.Statement awaitStatement(js.Expression value) { | 2117 js.Statement awaitStatement(js.Expression value) { |
2137 return js.js.statement(""" | 2118 return js.js.statement( |
| 2119 """ |
2138 return #asyncHelper(#value, | 2120 return #asyncHelper(#value, |
2139 #bodyName, | 2121 #bodyName, |
2140 #controller); | 2122 #controller); |
2141 """, { | 2123 """, |
2142 "asyncHelper": asyncStarHelper, | 2124 { |
2143 "value": value, | 2125 "asyncHelper": asyncStarHelper, |
2144 "bodyName": bodyName, | 2126 "value": value, |
2145 "controller": controllerName}); | 2127 "bodyName": bodyName, |
| 2128 "controller": controllerName |
| 2129 }); |
2146 } | 2130 } |
2147 } | 2131 } |
2148 | 2132 |
2149 /// Finds out | 2133 /// Finds out |
2150 /// | 2134 /// |
2151 /// - which expressions have yield or await nested in them. | 2135 /// - which expressions have yield or await nested in them. |
2152 /// - targets of jumps | 2136 /// - targets of jumps |
2153 /// - a set of used names. | 2137 /// - a set of used names. |
2154 /// - if any [This]-expressions are used. | 2138 /// - if any [This]-expressions are used. |
2155 class PreTranslationAnalysis extends js.NodeVisitor<bool> { | 2139 class PreTranslationAnalysis extends js.NodeVisitor<bool> { |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2237 bool containsAwait = false; | 2221 bool containsAwait = false; |
2238 for (js.Statement statement in node.statements) { | 2222 for (js.Statement statement in node.statements) { |
2239 if (visit(statement)) containsAwait = true; | 2223 if (visit(statement)) containsAwait = true; |
2240 } | 2224 } |
2241 return containsAwait; | 2225 return containsAwait; |
2242 } | 2226 } |
2243 | 2227 |
2244 @override | 2228 @override |
2245 bool visitBreak(js.Break node) { | 2229 bool visitBreak(js.Break node) { |
2246 if (node.targetLabel != null) { | 2230 if (node.targetLabel != null) { |
2247 targets[node] = labelledStatements.lastWhere( | 2231 targets[node] = |
2248 (js.LabeledStatement statement) { | 2232 labelledStatements.lastWhere((js.LabeledStatement statement) { |
2249 return statement.label == node.targetLabel; | 2233 return statement.label == node.targetLabel; |
2250 }); | 2234 }); |
2251 } else { | 2235 } else { |
2252 targets[node] = loopsAndSwitches.last; | 2236 targets[node] = loopsAndSwitches.last; |
2253 } | 2237 } |
2254 return false; | 2238 return false; |
2255 } | 2239 } |
2256 | 2240 |
2257 @override | 2241 @override |
2258 bool visitCall(js.Call node) { | 2242 bool visitCall(js.Call node) { |
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2593 return condition || body; | 2577 return condition || body; |
2594 } | 2578 } |
2595 | 2579 |
2596 @override | 2580 @override |
2597 bool visitDartYield(js.DartYield node) { | 2581 bool visitDartYield(js.DartYield node) { |
2598 hasYield = true; | 2582 hasYield = true; |
2599 visit(node.expression); | 2583 visit(node.expression); |
2600 return true; | 2584 return true; |
2601 } | 2585 } |
2602 } | 2586 } |
OLD | NEW |