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