Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: pkg/compiler/lib/src/js/rewrite_async.dart

Issue 953283003: Refactor rewrite_async (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: One more Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/compiler/lib/src/ssa/builder.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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:_internal/compiler/js_lib/shared/async_await_error_codes.dart' 10 import 'package:_internal/compiler/js_lib/shared/async_await_error_codes.dart'
11 as error_codes; 11 as error_codes;
12 12
13 import "js.dart" as js; 13 import "js.dart" as js;
14 14
15 import '../util/util.dart'; 15 import '../util/util.dart';
16 import '../dart2jslib.dart' show DiagnosticListener; 16 import '../dart2jslib.dart' show DiagnosticListener;
17 17
18 import "../helpers/helpers.dart"; 18 import "../helpers/helpers.dart";
19 19
20 /// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield 20 /// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield
21 /// (with dart-like semantics) to an equivalent function without these. 21 /// (with dart-like semantics) to an equivalent function without these.
22 /// await-for is not handled and must be rewritten before. (Currently handled 22 /// await-for is not handled and must be rewritten before. (Currently handled
23 /// in ssa/builder.dart). 23 /// in ssa/builder.dart).
24 /// 24 ///
25 /// When generating the input to this, special care must be taken that 25 /// When generating the input to this, special care must be taken that
26 /// parameters to sync* functions that are mutated in the body must be boxed. 26 /// parameters to sync* functions that are mutated in the body must be boxed.
27 /// (Currently handled in closure.dart). 27 /// (Currently handled in closure.dart).
28 /// 28 ///
29 /// Look at [visitFun], [visitDartYield] and [visitAwait] for more explanation. 29 /// Look at [visitFun], [visitDartYield] and [visitAwait] for more explanation.
30 class AsyncRewriter extends js.NodeVisitor { 30 abstract class AsyncRewriterBase extends js.NodeVisitor {
31 31
32 // Local variables are hoisted to the top of the function, so they are 32 // Local variables are hoisted to the top of the function, so they are
33 // collected here. 33 // collected here.
34 List<js.VariableDeclaration> localVariables = 34 List<js.VariableDeclaration> localVariables =
35 new List<js.VariableDeclaration>(); 35 new List<js.VariableDeclaration>();
36 36
37 Map<js.Node, int> continueLabels = new Map<js.Node, int>(); 37 Map<js.Node, int> continueLabels = new Map<js.Node, int>();
38 Map<js.Node, int> breakLabels = new Map<js.Node, int>(); 38 Map<js.Node, int> breakLabels = new Map<js.Node, int>();
39 39
40 /// The label of a finally part. 40 /// The label of a finally part.
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 /// result = [[a]]; 101 /// result = [[a]];
102 /// goto = joinLabel; 102 /// goto = joinLabel;
103 /// case elseLabel: 103 /// case elseLabel:
104 /// result = [[b]]; 104 /// result = [[b]];
105 /// case joinLabel: 105 /// case joinLabel:
106 /// // Now the result of computing the condition is in result. 106 /// // Now the result of computing the condition is in result.
107 /// .... 107 /// ....
108 /// } 108 /// }
109 /// } 109 /// }
110 /// 110 ///
111 /// It is a parameter to the [bodyName] function, so that [asyncHelper] and 111 /// It is a parameter to the [body] function, so that [awaitStatement] can
112 /// [streamHelper] can call [bodyName] with the result of an awaited Future. 112 /// call [body] with the result of an awaited Future.
113 js.VariableUse get result => new js.VariableUse(resultName);
113 String resultName; 114 String resultName;
114 115
115 /// A parameter to the [bodyName] function. Indicating if we are in success 116 /// A parameter to the [bodyName] function. Indicating if we are in success
116 /// or error case. 117 /// or error case.
117 String errorCodeName; 118 String errorCodeName;
118 119
119 /// The name of the inner function that is scheduled to do each await/yield, 120 /// The inner function that is scheduled to do each await/yield,
120 /// and called to do a new iteration for sync*. 121 /// and called to do a new iteration for sync*.
122 js.VariableUse get body => new js.VariableUse(bodyName);
121 String bodyName; 123 String bodyName;
122 124
123 /// The Completer that will finish an async function.
124 ///
125 /// Not used for sync* or async* functions.
126 String completerName;
127
128 /// The StreamController that controls an async* function.
129 ///
130 /// Not used for async and sync* functions
131 String controllerName;
132
133 /// Used to simulate a goto. 125 /// Used to simulate a goto.
134 /// 126 ///
135 /// To "goto" a label, the label is assigned to this 127 /// To "goto" a label, the label is assigned to this variable, and break out
136 /// variable, and break out of the switch to take another iteration in the 128 /// of the switch to take another iteration in the while loop. See [addGoto]
137 /// while loop. See [addGoto] 129 js.VariableUse get goto => new js.VariableUse(gotoName);
138 String gotoName; 130 String gotoName;
139 131
140 /// The label of the current error handler. 132 /// Variable containing the label of the current error handler.
133 js.VariableUse get handler => new js.VariableUse(handlerName);
141 String handlerName; 134 String handlerName;
142 135
143 /// Current caught error.
144 String errorName;
145
146 /// A stack of labels of finally blocks to visit, and the label to go to after 136 /// A stack of labels of finally blocks to visit, and the label to go to after
147 /// the last. 137 /// the last.
138 js.VariableUse get next => new js.VariableUse(nextName);
148 String nextName; 139 String nextName;
149 140
150 /// The stack of labels of finally blocks to assign to [nextName] if the
151 /// async* [StreamSubscription] was canceled during a yield.
152 String nextWhenCanceledName;
153
154 /// The current returned value (a finally block may overwrite it). 141 /// The current returned value (a finally block may overwrite it).
142 js.VariableUse get returnValue => new js.VariableUse(returnValueName);
155 String returnValueName; 143 String returnValueName;
156 144
157 /// If we are in the process of handling an error, stores the current error. 145 /// Stores the current error when we are in the process of handling an error.
146 js.VariableUse get currentError => new js.VariableUse(currentErrorName);
158 String currentErrorName; 147 String currentErrorName;
159 148
160 /// The label of the outer loop. 149 /// The label of the outer loop.
161 /// 150 ///
162 /// Used if there are untransformed loops containing break or continues to 151 /// Used if there are untransformed loops containing break or continues to
163 /// targets outside the loop. 152 /// targets outside the loop.
164 String outerLabelName; 153 String outerLabelName;
165 154
166 /// If javascript `this` is used, it is accessed via this variable, in the 155 /// If javascript `this` is used, it is accessed via this variable, in the
167 /// [bodyName] function. 156 /// [bodyName] function.
157 js.VariableUse get self => new js.VariableUse(selfName);
168 String selfName; 158 String selfName;
169 159
170 // These expressions are hooks for communicating with the runtime.
171
172 /// The function called by an async function to simulate an await or return.
173 ///
174 /// For an await it is called with:
175 ///
176 /// - The value to await
177 /// - The body function [bodyName]
178 /// - The completer object [completerName]
179 ///
180 /// For a return it is called with:
181 ///
182 /// - The value to complete the completer with.
183 /// - [error_codes.SUCCESS]
184 /// - The completer object [completerName]
185 ///
186 /// For a throw it is called with:
187 ///
188 /// - The error to complete the completer with.
189 /// - [error_codes.ERROR]
190 /// - The completer object [completerName]
191 final js.Expression asyncHelper;
192
193 /// The function called by an async* function to simulate an await, yield or
194 /// yield*.
195 ///
196 /// For an await/yield/yield* it is called with:
197 ///
198 /// - The value to await/yieldExpression(value to yield)/
199 /// yieldStarExpression(stream to yield)
200 /// - The body function [bodyName]
201 /// - The controller object [controllerName]
202 ///
203 /// For a return it is called with:
204 ///
205 /// - null
206 /// - null
207 /// - The [controllerName]
208 /// - null.
209 final js.Expression streamHelper;
210
211 /// Contructor used to initialize the [completerName] variable.
212 ///
213 /// Specific to async methods.
214 final js.Expression newCompleter;
215
216 /// Contructor used to initialize the [controllerName] variable.
217 ///
218 /// Specific to async* methods.
219 final js.Expression newController;
220
221 /// Used to get the `Stream` out of the [controllerName] variable.
222 ///
223 /// Specific to async* methods.
224 final js.Expression streamOfController;
225
226 /// Contructor creating the Iterable for a sync* method. Called with
227 /// [bodyName].
228 final js.Expression newIterable;
229
230 /// A JS Expression that creates a marker showing that iteration is over.
231 ///
232 /// Called without arguments.
233 final js.Expression endOfIteration;
234
235 /// A JS Expression that creates a marker indicating a 'yield' statement.
236 ///
237 /// Called with the value to yield.
238 final js.Expression yieldExpression;
239
240 /// A JS Expression that creates a marker indication a 'yield*' statement.
241 ///
242 /// Called with the stream to yield from.
243 final js.Expression yieldStarExpression;
244
245 /// Used by sync* functions to throw exeptions.
246 final js.Expression uncaughtErrorExpression;
247
248 final DiagnosticListener diagnosticListener; 160 final DiagnosticListener diagnosticListener;
249 // For error reporting only. 161 // For error reporting only.
250 Spannable get spannable { 162 Spannable get spannable {
251 return (_spannable == null) ? NO_LOCATION_SPANNABLE : _spannable; 163 return (_spannable == null) ? NO_LOCATION_SPANNABLE : _spannable;
252 } 164 }
253 165
254 Spannable _spannable; 166 Spannable _spannable;
255 167
256 int _currentLabel = 0; 168 int _currentLabel = 0;
257 169
258 // The highest temporary variable index currently in use. 170 // The highest temporary variable index currently in use.
259 int currentTempVarIndex = 0; 171 int currentTempVarIndex = 0;
260 // The highest temporary variable index ever in use in this function. 172 // The highest temporary variable index ever in use in this function.
261 int tempVarHighWaterMark = 0; 173 int tempVarHighWaterMark = 0;
262 Map<int, js.Expression> tempVarNames = new Map<int, js.Expression>(); 174 Map<int, js.Expression> tempVarNames = new Map<int, js.Expression>();
263 175
264 js.AsyncModifier async; 176 bool get isAsync => false;
177 bool get isSyncStar => false;
178 bool get isAsyncStar => false;
265 179
266 bool get isSync => async == const js.AsyncModifier.sync(); 180 AsyncRewriterBase(this.diagnosticListener,
267 bool get isAsync => async == const js.AsyncModifier.async(); 181 spannable,
268 bool get isSyncStar => async == const js.AsyncModifier.syncStar(); 182 this.safeVariableName)
269 bool get isAsyncStar => async == const js.AsyncModifier.asyncStar(); 183 : _spannable = spannable;
270 184
271 AsyncRewriter(this.diagnosticListener, 185 /// Initialize names used by the subClass.
272 spannable, 186 void initializeNames();
273 {this.asyncHelper,
274 this.streamHelper,
275 this.streamOfController,
276 this.newCompleter,
277 this.newController,
278 this.endOfIteration,
279 this.newIterable,
280 this.yieldExpression,
281 this.yieldStarExpression,
282 this.uncaughtErrorExpression,
283 this.safeVariableName})
284 : _spannable = spannable;
285 187
286 /// Main entry point. 188 /// Main entry point.
287 /// Rewrites a sync*/async/async* function to an equivalent normal function. 189 /// Rewrites a sync*/async/async* function to an equivalent normal function.
288 /// 190 ///
289 /// [spannable] can be passed to have a location for error messages. 191 /// [spannable] can be passed to have a location for error messages.
290 js.Fun rewrite(js.Fun node, [Spannable spannable]) { 192 js.Fun rewrite(js.Fun node, [Spannable spannable]) {
291 _spannable = spannable; 193 _spannable = spannable;
292 194
293 async = node.asyncModifier;
294 assert(!isSync);
295
296 analysis = new PreTranslationAnalysis(unsupported); 195 analysis = new PreTranslationAnalysis(unsupported);
297 analysis.analyze(node); 196 analysis.analyze(node);
298 197
299 // To avoid name collisions with existing names, the fresh names are 198 // To avoid name collisions with existing names, the fresh names are
300 // generated after the analysis. 199 // generated after the analysis.
301 resultName = freshName("result"); 200 resultName = freshName("result");
302 errorCodeName = freshName("errorCode"); 201 errorCodeName = freshName("errorCode");
303 completerName = freshName("completer");
304 controllerName = freshName("controller");
305 bodyName = freshName("body"); 202 bodyName = freshName("body");
306 gotoName = freshName("goto"); 203 gotoName = freshName("goto");
307 handlerName = freshName("handler"); 204 handlerName = freshName("handler");
308 errorName = freshName("error");
309 nextName = freshName("next"); 205 nextName = freshName("next");
310 nextWhenCanceledName = freshName("nextWhenCanceled");
311 returnValueName = freshName("returnValue"); 206 returnValueName = freshName("returnValue");
312 currentErrorName = freshName("currentError"); 207 currentErrorName = freshName("currentError");
313 outerLabelName = freshName("outer"); 208 outerLabelName = freshName("outer");
314 selfName = freshName("self"); 209 selfName = freshName("self");
210 // Initialize names specific to the subclass.
211 initializeNames();
315 212
316 return node.accept(this); 213 return node.accept(this);
317 } 214 }
318 215
319 js.Expression get currentErrorHandler { 216 js.Expression get currentErrorHandler {
320 return js.number(handlerLabels[jumpTargets.lastWhere( 217 return js.number(handlerLabels[jumpTargets.lastWhere(
321 (node) => handlerLabels[node] != null)]); 218 (node) => handlerLabels[node] != null)]);
322 } 219 }
323 220
324 int allocateTempVar() { 221 int allocateTempVar() {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 assert(!labelledParts.containsKey(label)); 292 assert(!labelledParts.containsKey(label));
396 currentStatementBuffer = new List<js.Statement>(); 293 currentStatementBuffer = new List<js.Statement>();
397 labelledParts[label] = currentStatementBuffer; 294 labelledParts[label] = currentStatementBuffer;
398 addStatement(new js.Comment(labelComments[label])); 295 addStatement(new js.Comment(labelComments[label]));
399 } 296 }
400 297
401 /// Returns a statement assigning to the variable named [gotoName]. 298 /// Returns a statement assigning to the variable named [gotoName].
402 /// This should be followed by a break for the goto to be executed. Use 299 /// This should be followed by a break for the goto to be executed. Use
403 /// [gotoWithBreak] or [addGoto] for this. 300 /// [gotoWithBreak] or [addGoto] for this.
404 js.Statement setGotoVariable(int label) { 301 js.Statement setGotoVariable(int label) {
405 return js.js.statement('# = #;', [gotoName, js.number(label)]); 302 return js.js.statement('# = #;', [goto, js.number(label)]);
406 } 303 }
407 304
408 /// Returns a block that has a goto to [label] including the break. 305 /// Returns a block that has a goto to [label] including the break.
409 /// 306 ///
410 /// Also inserts a comment describing the label if available. 307 /// Also inserts a comment describing the label if available.
411 js.Block gotoAndBreak(int label) { 308 js.Block gotoAndBreak(int label) {
412 List<js.Statement> statements = new List<js.Statement>(); 309 List<js.Statement> statements = new List<js.Statement>();
413 if (labelComments.containsKey(label)) { 310 if (labelComments.containsKey(label)) {
414 statements.add(new js.Comment("goto ${labelComments[label]}")); 311 statements.add(new js.Comment("goto ${labelComments[label]}"));
415 } 312 }
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
555 // All expressions before that must be stored in temp-vars. 452 // All expressions before that must be stored in temp-vars.
556 int lastTransformIndex = 0; 453 int lastTransformIndex = 0;
557 for (int i = nodes.length - 1; i >= 0; --i) { 454 for (int i = nodes.length - 1; i >= 0; --i) {
558 if (nodes[i] == null) continue; 455 if (nodes[i] == null) continue;
559 if (shouldTransform(nodes[i])) { 456 if (shouldTransform(nodes[i])) {
560 lastTransformIndex = i; 457 lastTransformIndex = i;
561 break; 458 break;
562 } 459 }
563 } 460 }
564 List<js.Node> visited = nodes.take(lastTransformIndex).map((js.Node node) { 461 List<js.Node> visited = nodes.take(lastTransformIndex).map((js.Node node) {
565 return node == null ? null : _storeIfNecessary(visitExpression(node)); 462 return (node == null) ? null : _storeIfNecessary(visitExpression(node));
566 }).toList(); 463 }).toList();
567 visited.addAll(nodes.skip(lastTransformIndex).map((js.Node node) { 464 visited.addAll(nodes.skip(lastTransformIndex).map((js.Node node) {
568 return node == null ? null : visitExpression(node); 465 return (node == null) ? null : visitExpression(node);
569 })); 466 }));
570 var result = fn(visited); 467 var result = fn(visited);
571 currentTempVarIndex = oldTempVarIndex; 468 currentTempVarIndex = oldTempVarIndex;
572 return result; 469 return result;
573 } 470 }
574 471
575 /// Emits the return block that all returns should jump to (after going 472 /// Emits the return block that all returns jump to (after going
576 /// through all the enclosing finally blocks). The jump to here is made in 473 /// through all the enclosing finally blocks). The jump to here is made in
577 /// [visitReturn]. 474 /// [visitReturn].
578 /// 475 void addSuccesExit();
579 /// Returning from an async method calls the [asyncHelper] with the result. 476
580 /// (the result might have been stored in [returnValueName] by some finally 477 /// Emits the block that control flows to if an error has been thrown
581 /// block). 478 /// but not caught. (after going through all the enclosing finally blocks).
582 /// 479 void addErrorExit();
583 /// Returning from a sync* function returns an [endOfIteration] marker. 480
584 /// 481 void addFunctionExits() {
585 /// Returning from an async* function calls the [streamHelper] with an 482 addSuccesExit();
586 /// [endOfIteration] marker. 483 addErrorExit();
587 void addExit() {
588 if (analysis.hasExplicitReturns || isAsyncStar) {
589 beginLabel(exitLabel);
590 } else {
591 addStatement(new js.Comment("implicit return"));
592 }
593 switch (async) {
594 case const js.AsyncModifier.async():
595 addStatement(js.js.statement(
596 "return #runtimeHelper(#returnValue, #successCode, "
597 "#completer, null);", {
598 "runtimeHelper": asyncHelper,
599 "successCode": js.number(error_codes.SUCCESS),
600 "returnValue": analysis.hasExplicitReturns
601 ? returnValueName
602 : new js.LiteralNull(),
603 "completer": completerName}));
604 break;
605 case const js.AsyncModifier.syncStar():
606 addStatement(js.js.statement('return #();', [endOfIteration]));
607 break;
608 case const js.AsyncModifier.asyncStar():
609 addStatement(js.js.statement(
610 "return #streamHelper(null, #successCode, #controller);", {
611 "streamHelper": streamHelper,
612 "successCode": js.number(error_codes.SUCCESS),
613 "controller": controllerName}));
614 break;
615 default:
616 diagnosticListener.internalError(
617 spannable, "Internal error, unexpected asyncmodifier $async");
618 }
619 if (isAsync || isAsyncStar) {
620 beginLabel(rethrowLabel);
621 addStatement(js.js.statement(
622 "return #thenHelper(#currentError, #errorCode, #controller);", {
623 "thenHelper": isAsync ? asyncHelper : streamHelper,
624 "errorCode": js.number(error_codes.ERROR),
625 "currentError": currentErrorName,
626 "controller": isAsync ? completerName : controllerName}));
627 } else {
628 assert(isSyncStar);
629 beginLabel(rethrowLabel);
630 addStatement(js.js.statement('return #(#);',
631 [uncaughtErrorExpression, currentErrorName]));
632 }
633 } 484 }
634 485
635 /// The initial call to [asyncHelper]/[streamHelper]. 486 /// Returns the rewritten function.
636 /// 487 js.Fun finishFunction(List<js.Parameter> parameters,
637 /// There is no value to await/yield, so the first argument is `null` and 488 js.Statement rewrittenBody,
638 /// also the errorCallback is `null`. 489 js.VariableDeclarationList variableDeclarations);
639 /// 490
640 /// Returns the [Future]/[Stream] coming from [completerName]/ 491 Iterable<js.VariableInitialization> variableInitializations();
641 /// [controllerName].
642 js.Statement generateInitializer() {
643 if (isAsync) {
644 return js.js.statement(
645 "return #asyncHelper(null, #body, #completer, null);", {
646 "asyncHelper": asyncHelper,
647 "body": bodyName,
648 "completer": completerName,
649 });
650 } else if (isAsyncStar) {
651 return js.js.statement(
652 "return #streamOfController(#controller);", {
653 "streamOfController": streamOfController,
654 "controller": controllerName,
655 });
656 } else {
657 throw diagnosticListener.internalError(
658 spannable, "Unexpected asyncModifier: $async");
659 }
660 }
661 492
662 /// Rewrites an async/sync*/async* function to a normal Javascript function. 493 /// Rewrites an async/sync*/async* function to a normal Javascript function.
663 /// 494 ///
664 /// The control flow is flattened by simulating 'goto' using a switch in a 495 /// The control flow is flattened by simulating 'goto' using a switch in a
665 /// loop and a state variable [gotoName] inside a nested function [bodyName] 496 /// loop and a state variable [goto] inside a nested function [body]
666 /// that can be called back by [asyncHelper]/[asyncStarHelper]/the [Iterator]. 497 /// that can be called back by [asyncStarHelper]/[asyncStarHelper]/the
498 /// [Iterator].
667 /// 499 ///
668 /// Local variables are hoisted outside the helper. 500 /// Local variables are hoisted outside the helper.
669 /// 501 ///
670 /// Awaits in async/async* are translated to code that remembers the current 502 /// Awaits in async/async* are translated to code that remembers the current
671 /// location (so the function can resume from where it was) followed by a call 503 /// location (so the function can resume from where it was) followed by a
672 /// to the [asyncHelper]. The helper sets up the waiting for the awaited value 504 /// [awaitStatement]. The helper sets up the waiting for the awaited
673 /// and returns a future which is immediately returned by the translated 505 /// value and returns a future which is immediately returned by the
674 /// await. 506 /// [awaitStatement].
675 /// Yields in async* are translated to a call to the [asyncStarHelper]. They,
676 /// too, need to be prepared to be interrupted in case the stream is paused or
677 /// canceled. (Currently we always suspend - this is different from the spec,
678 /// see `streamHelper` in `js_helper.dart`).
679 /// 507 ///
680 /// Yield/yield* in a sync* function is translated to a return of the value, 508 /// Yields in sync*/async* are translated to a calls to helper functions.
681 /// wrapped into a "IterationMarker" that signals the type (yield or yield*). 509 /// (see [visitYield])
682 /// Sync* functions are executed on demand (when the user requests a value) by
683 /// the Iterable that knows how to handle these values.
684 /// 510 ///
685 /// Simplified examples (not the exact translation, but intended to show the 511 /// Simplified examples (not the exact translation, but intended to show the
686 /// ideas): 512 /// ideas):
687 /// 513 ///
688 /// function (x, y, z) async { 514 /// function (x, y, z) async {
689 /// var p = await foo(); 515 /// var p = await foo();
690 /// return bar(p); 516 /// return bar(p);
691 /// } 517 /// }
692 /// 518 ///
693 /// Becomes (without error handling): 519 /// Becomes (without error handling):
(...skipping 12 matching lines...) Expand all
706 /// goto = 2; 532 /// goto = 2;
707 /// break; 533 /// break;
708 /// case 2: 534 /// case 2:
709 /// return thenHelper(returnValue, null, completer) 535 /// return thenHelper(returnValue, null, completer)
710 /// } 536 /// }
711 /// } 537 /// }
712 /// return thenHelper(null, helper, completer); 538 /// return thenHelper(null, helper, completer);
713 /// } 539 /// }
714 /// } 540 /// }
715 /// 541 ///
716 /// Try/catch is implemented by maintaining [handlerName] to contain the label 542 /// Try/catch is implemented by maintaining [handler] to contain the label
717 /// of the current handler. If [bodyName] throws, the caller should catch the 543 /// of the current handler. If [body] throws, the caller should catch the
718 /// error and recall [bodyName] with first argument [error_codes.ERROR] and 544 /// error and recall [body] with first argument [error_codes.ERROR] and
719 /// second argument the error. 545 /// second argument the error.
720 /// 546 ///
721 /// A `finally` clause is compiled similar to normal code, with the additional 547 /// A `finally` clause is compiled similar to normal code, with the additional
722 /// complexity that `finally` clauses need to know where to jump to after the 548 /// complexity that `finally` clauses need to know where to jump to after the
723 /// clause is done. In the translation, each flow-path that enters a `finally` 549 /// clause is done. In the translation, each flow-path that enters a `finally`
724 /// sets up the variable [nextName] with a stack of finally-blocks and a final 550 /// sets up the variable [next] with a stack of finally-blocks and a final
725 /// jump-target (exit, catch, ...). 551 /// jump-target (exit, catch, ...).
726 /// 552 ///
727 /// function(x, y, z) async { 553 /// function(x, y, z) async {
728 /// try { 554 /// try {
729 /// try { 555 /// try {
730 /// throw "error"; 556 /// throw "error";
731 /// } finally { 557 /// } finally {
732 /// finalize1(); 558 /// finalize1();
733 /// } 559 /// }
734 /// } catch (e) { 560 /// } catch (e) {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
794 /// case 8: // Rethrow 620 /// case 8: // Rethrow
795 /// return thenHelper(currentError, 1, completer); 621 /// return thenHelper(currentError, 1, completer);
796 /// } 622 /// }
797 /// } 623 /// }
798 /// return thenHelper(null, helper, completer); 624 /// return thenHelper(null, helper, completer);
799 /// } 625 /// }
800 /// } 626 /// }
801 /// 627 ///
802 @override 628 @override
803 js.Expression visitFun(js.Fun node) { 629 js.Expression visitFun(js.Fun node) {
804 if (isSync) return node;
805
806 beginLabel(newLabel("Function start")); 630 beginLabel(newLabel("Function start"));
807 // AsyncStar needs a returnlabel for its handling of cancelation. See 631 // AsyncStar needs a returnlabel for its handling of cancelation. See
808 // [visitDartYield]. 632 // [visitDartYield].
809 exitLabel = 633 exitLabel = (analysis.hasExplicitReturns || isAsyncStar)
810 analysis.hasExplicitReturns || isAsyncStar ? newLabel("return") : null; 634 ? newLabel("return")
635 : null;
811 rethrowLabel = newLabel("rethrow"); 636 rethrowLabel = newLabel("rethrow");
812 handlerLabels[node] = rethrowLabel; 637 handlerLabels[node] = rethrowLabel;
813 js.Statement body = node.body; 638 js.Statement body = node.body;
814 jumpTargets.add(node); 639 jumpTargets.add(node);
815 visitStatement(body); 640 visitStatement(body);
816 jumpTargets.removeLast(); 641 jumpTargets.removeLast();
817 addExit(); 642 addFunctionExits();
818 643
819 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { 644 List<js.SwitchClause> clauses = labelledParts.keys.map((label) {
820 return new js.Case(js.number(label), new js.Block(labelledParts[label])); 645 return new js.Case(js.number(label), new js.Block(labelledParts[label]));
821 }).toList(); 646 }).toList();
822 js.Statement helperBody = 647 js.Statement rewrittenBody =
823 new js.Switch(new js.VariableUse(gotoName), clauses); 648 new js.Switch(goto, clauses);
824 if (hasJumpThoughOuterLabel) { 649 if (hasJumpThoughOuterLabel) {
825 helperBody = new js.LabeledStatement(outerLabelName, helperBody); 650 rewrittenBody = new js.LabeledStatement(outerLabelName, rewrittenBody);
826 } 651 }
652 rewrittenBody = js.js.statement('while (true) {#}', rewrittenBody);
653 List<js.VariableInitialization> variables =
654 new List<js.VariableInitialization>();
827 655
828 List<js.VariableInitialization> inits = <js.VariableInitialization>[]; 656 variables.add(_makeVariableInitializer(goto, js.number(0)));
829 657 variables.addAll(variableInitializations());
830 js.VariableInitialization makeInit(String name, js.Expression initValue) { 658 variables.add(
831 return new js.VariableInitialization( 659 _makeVariableInitializer(handler, js.number(rethrowLabel)));
832 new js.VariableDeclaration(name), initValue); 660 variables.add(_makeVariableInitializer(currentError, null));
833 }
834
835 inits.add(makeInit(gotoName, js.number(0)));
836 if (isAsync) {
837 inits.add(makeInit(completerName, new js.New(newCompleter, [])));
838 } else if (isAsyncStar) {
839 inits.add(makeInit(controllerName,
840 js.js('#(#)', [newController, bodyName])));
841 }
842 inits.add(makeInit(handlerName, js.number(rethrowLabel)));
843 inits.add(makeInit(currentErrorName, null));
844 if (analysis.hasFinally || (isAsyncStar && analysis.hasYield)) { 661 if (analysis.hasFinally || (isAsyncStar && analysis.hasYield)) {
845 inits.add(makeInit(nextName, null)); 662 variables.add(_makeVariableInitializer(next, null));
846 }
847 if (isAsyncStar && analysis.hasYield) {
848 inits.add(makeInit(nextWhenCanceledName, null));
849 }
850 if (analysis.hasExplicitReturns && isAsync) {
851 inits.add(makeInit(returnValueName, null));
852 } 663 }
853 if (analysis.hasThis && !isSyncStar) { 664 if (analysis.hasThis && !isSyncStar) {
854 // Sync* functions must remember `this` on the level of the outer 665 // Sync* functions must remember `this` on the level of the outer
855 // function. 666 // function.
856 inits.add(makeInit(selfName, js.js('this'))); 667 variables.add(_makeVariableInitializer(self, js.js('this')));
857 } 668 }
858 inits.addAll(localVariables.map((js.VariableDeclaration decl) { 669 variables.addAll(localVariables.map(
859 return new js.VariableInitialization(decl, null); 670 (js.VariableDeclaration declaration) {
671 return new js.VariableInitialization(declaration, null);
860 })); 672 }));
861 inits.addAll(new Iterable.generate(tempVarHighWaterMark, 673 variables.addAll(new Iterable.generate(tempVarHighWaterMark,
862 (int i) => makeInit(useTempVar(i + 1).name, null))); 674 (int i) => _makeVariableInitializer(useTempVar(i + 1).name, null)));
863 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits); 675 js.VariableDeclarationList variableDeclarations =
864 // TODO(sigurdm): Explain the difference between these cases. 676 new js.VariableDeclarationList(variables);
865 if (isSyncStar) { 677
866 return js.js(""" 678 return finishFunction(node.params, rewrittenBody, variableDeclarations);
867 function (#params) {
868 if (#needsThis)
869 var #self = this;
870 return new #newIterable(function () {
871 #varDecl;
872 return function #body(#errorCode, #result) {
873 if (#errorCode === #ERROR) {
874 #currentError = #result;
875 #goto = #handler;
876 }
877 while (true)
878 #helperBody;
879 };
880 });
881 }
882 """, {
883 "params": node.params,
884 "needsThis": analysis.hasThis,
885 "helperBody": helperBody,
886 "varDecl": varDecl,
887 "errorCode": errorCodeName,
888 "newIterable": newIterable,
889 "body": bodyName,
890 "self": selfName,
891 "result": resultName,
892 "goto": gotoName,
893 "handler": handlerName,
894 "currentError": currentErrorName,
895 "ERROR": js.number(error_codes.ERROR),
896 });
897 }
898 return js.js("""
899 function (#params) {
900 #varDecl;
901 function #bodyName(#errorCode, #result) {
902 if (#hasYield)
903 switch (#errorCode) {
904 case #STREAM_WAS_CANCELED:
905 #next = #nextWhenCanceled;
906 #goto = #next.pop();
907 break;
908 case #ERROR:
909 #currentError = #result;
910 #goto = #handler;
911 }
912 else
913 if (#errorCode === #ERROR) {
914 #currentError = #result;
915 #goto = #handler;
916 }
917 while (true)
918 #helperBody;
919 }
920 #init;
921 }""", {
922 "params": node.params,
923 "varDecl": varDecl,
924 "STREAM_WAS_CANCELED": js.number(error_codes.STREAM_WAS_CANCELED),
925 "ERROR": js.number(error_codes.ERROR),
926 "hasYield": analysis.hasYield,
927 "helperBody": helperBody,
928 "init": generateInitializer(),
929 "bodyName": bodyName,
930 "currentError": currentErrorName,
931 "goto": gotoName,
932 "handler": handlerName,
933 "next": nextName,
934 "nextWhenCanceled": nextWhenCanceledName,
935 "errorCode": errorCodeName,
936 "result": resultName,
937 });
938 } 679 }
939 680
940 @override 681 @override
941 js.Expression visitAccess(js.PropertyAccess node) { 682 js.Expression visitAccess(js.PropertyAccess node) {
942 return withExpression2(node.receiver, node.selector, 683 return withExpression2(node.receiver, node.selector,
943 (receiver, selector) => js.js('#[#]', [receiver, selector])); 684 (receiver, selector) => js.js('#[#]', [receiver, selector]));
944 } 685 }
945 686
946 @override 687 @override
947 js.Expression visitArrayHole(js.ArrayHole node) { 688 js.Expression visitArrayHole(js.ArrayHole node) {
(...skipping 26 matching lines...) Expand all
974 ], (evaluated) { 715 ], (evaluated) {
975 return new js.Assignment.compound( 716 return new js.Assignment.compound(
976 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op, 717 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op,
977 evaluated[2]); 718 evaluated[2]);
978 }); 719 });
979 } else { 720 } else {
980 throw "Unexpected assignment left hand side $leftHandSide"; 721 throw "Unexpected assignment left hand side $leftHandSide";
981 } 722 }
982 } 723 }
983 724
984 /// An await is translated to a call to [asyncHelper]/[streamHelper]. 725 js.Statement awaitStatement(js.Expression value);
726
727 /// An await is translated to an [awaitStatement].
985 /// 728 ///
986 /// See the comments of [visitFun] for an example. 729 /// See the comments of [visitFun] for an example.
987 @override 730 @override
988 js.Expression visitAwait(js.Await node) { 731 js.Expression visitAwait(js.Await node) {
989 assert(isAsync || isAsyncStar); 732 assert(isAsync || isAsyncStar);
990 int afterAwait = newLabel("returning from await."); 733 int afterAwait = newLabel("returning from await.");
991 withExpression(node.expression, (js.Expression value) { 734 withExpression(node.expression, (js.Expression value) {
992 addStatement(setGotoVariable(afterAwait)); 735 addStatement(setGotoVariable(afterAwait));
993 addStatement(js.js.statement(""" 736 addStatement(awaitStatement(value));
994 return #asyncHelper(#value,
995 #body,
996 #controller);
997 """, {
998 "asyncHelper": isAsync ? asyncHelper : streamHelper,
999 "value": value,
1000 "body": bodyName,
1001 "controller": isAsync ? completerName : controllerName,
1002 }));
1003 }, store: false); 737 }, store: false);
1004 beginLabel(afterAwait); 738 beginLabel(afterAwait);
1005 return new js.VariableUse(resultName); 739 return result;
1006 } 740 }
1007 741
1008 /// Checks if [node] is the variable named [resultName]. 742 /// Checks if [node] is the variable named [resultName].
1009 /// 743 ///
1010 /// [resultName] is used to hold the result of a transformed computation 744 /// [result] is used to hold the result of a transformed computation
1011 /// for example the result of awaiting, or the result of a conditional or 745 /// for example the result of awaiting, or the result of a conditional or
1012 /// short-circuiting expression. 746 /// short-circuiting expression.
1013 /// If the subexpression of some transformed node already is transformed and 747 /// If the subexpression of some transformed node already is transformed and
1014 /// visiting it returns [resultName], it is not redundantly assigned to itself 748 /// visiting it returns [result], it is not redundantly assigned to itself
1015 /// again. 749 /// again.
1016 bool isResult(js.Expression node) { 750 bool isResult(js.Expression node) {
1017 return node is js.VariableUse && node.name == resultName; 751 return node is js.VariableUse && node.name == resultName;
1018 } 752 }
1019 753
1020 @override 754 @override
1021 js.Expression visitBinary(js.Binary node) { 755 js.Expression visitBinary(js.Binary node) {
1022 if (shouldTransform(node.right) && (node.op == "||" || node.op == "&&")) { 756 if (shouldTransform(node.right) && (node.op == "||" || node.op == "&&")) {
1023 int thenLabel = newLabel("then"); 757 int thenLabel = newLabel("then");
1024 int joinLabel = newLabel("join"); 758 int joinLabel = newLabel("join");
1025 withExpression(node.left, (js.Expression left) { 759 withExpression(node.left, (js.Expression left) {
1026 js.Statement assignLeft = isResult(left) 760 js.Statement assignLeft = isResult(left)
1027 ? new js.Block.empty() 761 ? new js.Block.empty()
1028 : js.js.statement('# = #;', [resultName, left]); 762 : js.js.statement('# = #;', [result, left]);
1029 if (node.op == "||") { 763 if (node.op == "||") {
1030 addStatement(js.js.statement('if (#) {#} else #', 764 addStatement(js.js.statement('if (#) {#} else #',
1031 [left, gotoAndBreak(thenLabel), assignLeft])); 765 [left, gotoAndBreak(thenLabel), assignLeft]));
1032 } else { 766 } else {
1033 assert(node.op == "&&"); 767 assert(node.op == "&&");
1034 addStatement(js.js.statement('if (#) {#} else #', 768 addStatement(js.js.statement('if (#) {#} else #',
1035 [left, assignLeft, gotoAndBreak(thenLabel)])); 769 [left, assignLeft, gotoAndBreak(thenLabel)]));
1036 } 770 }
1037 }, store: true); 771 }, store: true);
1038 addGoto(joinLabel); 772 addGoto(joinLabel);
1039 beginLabel(thenLabel); 773 beginLabel(thenLabel);
1040 withExpression(node.right, (js.Expression value) { 774 withExpression(node.right, (js.Expression value) {
1041 if (!isResult(value)) { 775 if (!isResult(value)) {
1042 addStatement(js.js.statement('# = #;', [resultName, value])); 776 addStatement(js.js.statement('# = #;', [result, value]));
1043 } 777 }
1044 }, store: false); 778 }, store: false);
1045 beginLabel(joinLabel); 779 beginLabel(joinLabel);
1046 return new js.VariableUse(resultName); 780 return result;
1047 } 781 }
1048 782
1049 return withExpression2(node.left, node.right, 783 return withExpression2(node.left, node.right,
1050 (left, right) => new js.Binary(node.op, left, right)); 784 (left, right) => new js.Binary(node.op, left, right));
1051 } 785 }
1052 786
1053 @override 787 @override
1054 void visitBlock(js.Block node) { 788 void visitBlock(js.Block node) {
1055 for (js.Statement statement in node.statements) { 789 for (js.Statement statement in node.statements) {
1056 visitStatement(statement); 790 visitStatement(statement);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1097 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) { 831 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
1098 return withExpression(node.condition, (js.Expression condition) { 832 return withExpression(node.condition, (js.Expression condition) {
1099 return js.js('# ? # : #', [condition, node.then, node.otherwise]); 833 return js.js('# ? # : #', [condition, node.then, node.otherwise]);
1100 }); 834 });
1101 } 835 }
1102 int thenLabel = newLabel("then"); 836 int thenLabel = newLabel("then");
1103 int joinLabel = newLabel("join"); 837 int joinLabel = newLabel("join");
1104 int elseLabel = newLabel("else"); 838 int elseLabel = newLabel("else");
1105 withExpression(node.condition, (js.Expression condition) { 839 withExpression(node.condition, (js.Expression condition) {
1106 addStatement(js.js.statement('# = # ? # : #;', 840 addStatement(js.js.statement('# = # ? # : #;',
1107 [gotoName, condition, js.number(thenLabel), js.number(elseLabel)])); 841 [goto, condition, js.number(thenLabel), js.number(elseLabel)]));
1108 }, store: false); 842 }, store: false);
1109 addBreak(); 843 addBreak();
1110 beginLabel(thenLabel); 844 beginLabel(thenLabel);
1111 withExpression(node.then, (js.Expression value) { 845 withExpression(node.then, (js.Expression value) {
1112 if (!isResult(value)) { 846 if (!isResult(value)) {
1113 addStatement(js.js.statement('# = #;', [resultName, value])); 847 addStatement(js.js.statement('# = #;', [result, value]));
1114 } 848 }
1115 }, store: false); 849 }, store: false);
1116 addGoto(joinLabel); 850 addGoto(joinLabel);
1117 beginLabel(elseLabel); 851 beginLabel(elseLabel);
1118 withExpression(node.otherwise, (js.Expression value) { 852 withExpression(node.otherwise, (js.Expression value) {
1119 if (!isResult(value)) { 853 if (!isResult(value)) {
1120 addStatement(js.js.statement('# = #;', [resultName, value])); 854 addStatement(js.js.statement('# = #;', [result, value]));
1121 } 855 }
1122 }, store: false); 856 }, store: false);
1123 beginLabel(joinLabel); 857 beginLabel(joinLabel);
1124 return new js.VariableUse(resultName); 858 return result;
1125 } 859 }
1126 860
1127 @override 861 @override
1128 void visitContinue(js.Continue node) { 862 void visitContinue(js.Continue node) {
1129 js.Node target = analysis.targets[node]; 863 js.Node target = analysis.targets[node];
1130 if (!shouldTransform(target)) { 864 if (!shouldTransform(target)) {
1131 addStatement(node); 865 addStatement(node);
1132 return; 866 return;
1133 } 867 }
1134 translateJump(target, continueLabels[target]); 868 translateJump(target, continueLabels[target]);
1135 } 869 }
1136 870
1137 /// Emits a break statement that exits the big switch statement. 871 /// Emits a break statement that exits the big switch statement.
1138 void addBreak() { 872 void addBreak() {
1139 if (insideUntranslatedBreakable) { 873 if (insideUntranslatedBreakable) {
1140 hasJumpThoughOuterLabel = true; 874 hasJumpThoughOuterLabel = true;
1141 addStatement(new js.Break(outerLabelName)); 875 addStatement(new js.Break(outerLabelName));
1142 } else { 876 } else {
1143 addStatement(new js.Break(null)); 877 addStatement(new js.Break(null));
1144 } 878 }
1145 } 879 }
1146 880
1147 /// Common code for handling break, continue, return. 881 /// Common code for handling break, continue, return.
1148 /// 882 ///
1149 /// It is necessary to run all nesting finally-handlers between the jump and 883 /// It is necessary to run all nesting finally-handlers between the jump and
1150 /// the target. For that [nextName] is used as a stack of places to go. 884 /// the target. For that [next] is used as a stack of places to go.
1151 /// 885 ///
1152 /// See also [visitFun]. 886 /// See also [visitFun].
1153 void translateJump(js.Node target, int targetLabel) { 887 void translateJump(js.Node target, int targetLabel) {
1154 // Compute a stack of all the 'finally' nodes that must be visited before 888 // Compute a stack of all the 'finally' nodes that must be visited before
1155 // the jump. 889 // the jump.
1156 // The bottom of the stack is the label where the jump goes to. 890 // The bottom of the stack is the label where the jump goes to.
1157 List<int> jumpStack = new List<int>(); 891 List<int> jumpStack = new List<int>();
1158 for (js.Node node in jumpTargets.reversed) { 892 for (js.Node node in jumpTargets.reversed) {
1159 if (finallyLabels[node] != null) { 893 if (finallyLabels[node] != null) {
1160 jumpStack.add(finallyLabels[node]); 894 jumpStack.add(finallyLabels[node]);
1161 } else if (node == target) { 895 } else if (node == target) {
1162 jumpStack.add(targetLabel); 896 jumpStack.add(targetLabel);
1163 break; 897 break;
1164 } 898 }
1165 // Ignore other nodes. 899 // Ignore other nodes.
1166 } 900 }
1167 jumpStack = jumpStack.reversed.toList(); 901 jumpStack = jumpStack.reversed.toList();
1168 // As the program jumps directly to the top of the stack, it is taken off 902 // As the program jumps directly to the top of the stack, it is taken off
1169 // now. 903 // now.
1170 int firstTarget = jumpStack.removeLast(); 904 int firstTarget = jumpStack.removeLast();
1171 if (jumpStack.isNotEmpty) { 905 if (jumpStack.isNotEmpty) {
1172 js.Expression jsJumpStack = new js.ArrayInitializer( 906 js.Expression jsJumpStack = new js.ArrayInitializer(
1173 jumpStack.map((int label) => js.number(label)).toList()); 907 jumpStack.map((int label) => js.number(label)).toList());
1174 addStatement(js.js.statement("# = #;", [nextName, jsJumpStack])); 908 addStatement(js.js.statement("# = #;", [next, jsJumpStack]));
1175 } 909 }
1176 addGoto(firstTarget); 910 addGoto(firstTarget);
1177 } 911 }
1178 912
1179 @override 913 @override
1180 void visitDefault(js.Default node) => unreachable(node); 914 void visitDefault(js.Default node) => unreachable(node);
1181 915
1182 @override 916 @override
1183 void visitDo(js.Do node) { 917 void visitDo(js.Do node) {
1184 if (!shouldTransform(node)) { 918 if (!shouldTransform(node)) {
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
1309 void visitIf(js.If node) { 1043 void visitIf(js.If node) {
1310 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) { 1044 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
1311 withExpression(node.condition, (js.Expression condition) { 1045 withExpression(node.condition, (js.Expression condition) {
1312 addStatement(new js.If(condition, translateInBlock(node.then), 1046 addStatement(new js.If(condition, translateInBlock(node.then),
1313 translateInBlock(node.otherwise))); 1047 translateInBlock(node.otherwise)));
1314 }, store: false); 1048 }, store: false);
1315 return; 1049 return;
1316 } 1050 }
1317 int thenLabel = newLabel("then"); 1051 int thenLabel = newLabel("then");
1318 int joinLabel = newLabel("join"); 1052 int joinLabel = newLabel("join");
1319 int elseLabel = 1053 int elseLabel = (node.otherwise is js.EmptyStatement)
1320 node.otherwise is js.EmptyStatement ? joinLabel : newLabel("else"); 1054 ? joinLabel
1055 : newLabel("else");
1321 1056
1322 withExpression(node.condition, (js.Expression condition) { 1057 withExpression(node.condition, (js.Expression condition) {
1323 addExpressionStatement( 1058 addExpressionStatement(
1324 new js.Assignment( 1059 new js.Assignment(
1325 new js.VariableUse(gotoName), 1060 goto,
1326 new js.Conditional( 1061 new js.Conditional(
1327 condition, 1062 condition,
1328 js.number(thenLabel), 1063 js.number(thenLabel),
1329 js.number(elseLabel)))); 1064 js.number(elseLabel))));
1330 }, store: false); 1065 }, store: false);
1331 addBreak(); 1066 addBreak();
1332 beginLabel(thenLabel); 1067 beginLabel(thenLabel);
1333 visitStatement(node.then); 1068 visitStatement(node.then);
1334 if (node.otherwise is! js.EmptyStatement) { 1069 if (node.otherwise is! js.EmptyStatement) {
1335 addGoto(joinLabel); 1070 addGoto(joinLabel);
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
1486 return withExpression( 1221 return withExpression(
1487 node.value, (js.Expression value) => new js.Property(node.name, value), 1222 node.value, (js.Expression value) => new js.Property(node.name, value),
1488 store: false); 1223 store: false);
1489 } 1224 }
1490 1225
1491 @override 1226 @override
1492 js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node; 1227 js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node;
1493 1228
1494 @override 1229 @override
1495 void visitReturn(js.Return node) { 1230 void visitReturn(js.Return node) {
1496 assert(node.value == null || !isSyncStar && !isAsyncStar); 1231 assert(node.value == null || (!isSyncStar && !isAsyncStar));
1497 js.Node target = analysis.targets[node]; 1232 js.Node target = analysis.targets[node];
1498 if (node.value != null) { 1233 if (node.value != null) {
1499 withExpression(node.value, (js.Expression value) { 1234 withExpression(node.value, (js.Expression value) {
1500 addStatement(js.js.statement("# = #;", [returnValueName, value])); 1235 addStatement(js.js.statement("# = #;", [returnValue, value]));
1501 }, store: false); 1236 }, store: false);
1502 } 1237 }
1503 translateJump(target, exitLabel); 1238 translateJump(target, exitLabel);
1504 } 1239 }
1505 1240
1506 @override 1241 @override
1507 void visitSwitch(js.Switch node) { 1242 void visitSwitch(js.Switch node) {
1508 if (!node.cases.any(shouldTransform)) { 1243 if (!node.cases.any(shouldTransform)) {
1509 // If only the key has an await, translation can be simplified. 1244 // If only the key has an await, translation can be simplified.
1510 bool oldInsideUntranslated = insideUntranslatedBreakable; 1245 bool oldInsideUntranslated = insideUntranslatedBreakable;
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
1592 for (int i = 0; i < labels.length; i++) { 1327 for (int i = 0; i < labels.length; i++) {
1593 beginLabel(labels[i]); 1328 beginLabel(labels[i]);
1594 visitStatement(node.cases[i].body); 1329 visitStatement(node.cases[i].body);
1595 } 1330 }
1596 beginLabel(after); 1331 beginLabel(after);
1597 jumpTargets.removeLast(); 1332 jumpTargets.removeLast();
1598 } 1333 }
1599 1334
1600 @override 1335 @override
1601 js.Expression visitThis(js.This node) { 1336 js.Expression visitThis(js.This node) {
1602 return new js.VariableUse(selfName); 1337 return self;
1603 } 1338 }
1604 1339
1605 @override 1340 @override
1606 void visitThrow(js.Throw node) { 1341 void visitThrow(js.Throw node) {
1607 withExpression(node.expression, (js.Expression expression) { 1342 withExpression(node.expression, (js.Expression expression) {
1608 addStatement(new js.Throw(expression)); 1343 addStatement(new js.Throw(expression));
1609 }, store: false); 1344 }, store: false);
1610 } 1345 }
1611 1346
1612 setErrorHandler([int errorHandler]) { 1347 setErrorHandler([int errorHandler]) {
1613 addExpressionStatement(new js.Assignment( 1348 js.Expression label = (errorHandler == null)
1614 new js.VariableUse(handlerName), 1349 ? currentErrorHandler
1615 errorHandler == null ? currentErrorHandler : js.number(errorHandler))); 1350 : js.number(errorHandler);
1351 addStatement(js.js.statement('# = #;',[handler, label]));
1616 } 1352 }
1617 1353
1618 List<int> _finalliesUpToAndEnclosingHandler() { 1354 List<int> _finalliesUpToAndEnclosingHandler() {
1619 List<int> result = new List<int>(); 1355 List<int> result = new List<int>();
1620 for (int i = jumpTargets.length - 1; i >= 0; i--) { 1356 for (int i = jumpTargets.length - 1; i >= 0; i--) {
1621 js.Node node = jumpTargets[i]; 1357 js.Node node = jumpTargets[i];
1622 int handlerLabel = handlerLabels[node]; 1358 int handlerLabel = handlerLabels[node];
1623 if (handlerLabel != null) { 1359 if (handlerLabel != null) {
1624 result.add(handlerLabel); 1360 result.add(handlerLabel);
1625 break; 1361 break;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
1671 1407
1672 js.Node last = jumpTargets.removeLast(); 1408 js.Node last = jumpTargets.removeLast();
1673 assert(last == node); 1409 assert(last == node);
1674 1410
1675 if (node.finallyPart == null) { 1411 if (node.finallyPart == null) {
1676 setErrorHandler(); 1412 setErrorHandler();
1677 addGoto(afterFinallyLabel); 1413 addGoto(afterFinallyLabel);
1678 } else { 1414 } else {
1679 // The handler is reset as the first thing in the finally block. 1415 // The handler is reset as the first thing in the finally block.
1680 addStatement( 1416 addStatement(
1681 js.js.statement("# = [#];", 1417 js.js.statement("# = [#];", [next, js.number(afterFinallyLabel)]));
1682 [nextName, js.number(afterFinallyLabel)]));
1683 addGoto(finallyLabel); 1418 addGoto(finallyLabel);
1684 } 1419 }
1685 1420
1686 if (node.catchPart != null) { 1421 if (node.catchPart != null) {
1687 beginLabel(handlerLabel); 1422 beginLabel(handlerLabel);
1688 // [uncaughtLabel] is the handler for the code in the catch-part. 1423 // [uncaughtLabel] is the handler for the code in the catch-part.
1689 // It ensures that [nextName] is set up to run the right finally blocks. 1424 // It ensures that [nextName] is set up to run the right finally blocks.
1690 handlerLabels[node.catchPart] = uncaughtLabel; 1425 handlerLabels[node.catchPart] = uncaughtLabel;
1691 jumpTargets.add(node.catchPart); 1426 jumpTargets.add(node.catchPart);
1692 setErrorHandler(); 1427 setErrorHandler();
1693 // The catch declaration name can shadow outer variables, so a fresh name 1428 // The catch declaration name can shadow outer variables, so a fresh name
1694 // is needed to avoid collisions. See Ecma 262, 3rd edition, 1429 // is needed to avoid collisions. See Ecma 262, 3rd edition,
1695 // section 12.14. 1430 // section 12.14.
1696 String errorRename = freshName(node.catchPart.declaration.name); 1431 String errorRename = freshName(node.catchPart.declaration.name);
1697 localVariables.add(new js.VariableDeclaration(errorRename)); 1432 localVariables.add(new js.VariableDeclaration(errorRename));
1698 variableRenamings 1433 variableRenamings
1699 .add(new Pair(node.catchPart.declaration.name, errorRename)); 1434 .add(new Pair(node.catchPart.declaration.name, errorRename));
1700 addExpressionStatement(new js.Assignment( 1435 addStatement(js.js.statement("# = #;", [errorRename, currentError]));
1701 new js.VariableUse(errorRename),
1702 new js.VariableUse(currentErrorName)));
1703 visitStatement(node.catchPart.body); 1436 visitStatement(node.catchPart.body);
1704 variableRenamings.removeLast(); 1437 variableRenamings.removeLast();
1705 if (node.finallyPart != null) { 1438 if (node.finallyPart != null) {
1706 // The error has been caught, so after the finally, continue after the 1439 // The error has been caught, so after the finally, continue after the
1707 // try. 1440 // try.
1708 addStatement(js.js.statement("# = [#];", 1441 addStatement(js.js.statement("# = [#];",
1709 [nextName, js.number(afterFinallyLabel)])); 1442 [next, js.number(afterFinallyLabel)]));
1710 addGoto(finallyLabel); 1443 addGoto(finallyLabel);
1711 } else { 1444 } else {
1712 addGoto(afterFinallyLabel); 1445 addGoto(afterFinallyLabel);
1713 } 1446 }
1714 js.Node last = jumpTargets.removeLast(); 1447 js.Node last = jumpTargets.removeLast();
1715 assert(last == node.catchPart); 1448 assert(last == node.catchPart);
1716 } 1449 }
1717 1450
1718 // The "uncaught"-handler tells the finally-block to continue with 1451 // The "uncaught"-handler tells the finally-block to continue with
1719 // the enclosing finally-blocks until the current catch-handler. 1452 // the enclosing finally-blocks until the current catch-handler.
1720 beginLabel(uncaughtLabel); 1453 beginLabel(uncaughtLabel);
1721 1454
1722 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler(); 1455 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler();
1723 1456
1724 int nextLabel = enclosingFinallies.removeLast(); 1457 int nextLabel = enclosingFinallies.removeLast();
1725 if (enclosingFinallies.isNotEmpty) { 1458 if (enclosingFinallies.isNotEmpty) {
1726 // [enclosingFinallies] can be empty if there is no surrounding finally 1459 // [enclosingFinallies] can be empty if there is no surrounding finally
1727 // blocks. Then [nextLabel] will be [rethrowLabel]. 1460 // blocks. Then [nextLabel] will be [rethrowLabel].
1728 addStatement( 1461 addStatement(
1729 js.js.statement("# = #;", [nextName, new js.ArrayInitializer( 1462 js.js.statement("# = #;", [next, new js.ArrayInitializer(
1730 enclosingFinallies.map(js.number).toList())])); 1463 enclosingFinallies.map(js.number).toList())]));
1731 } 1464 }
1732 if (node.finallyPart == null) { 1465 if (node.finallyPart == null) {
1733 // The finally-block belonging to [node] will be visited because of 1466 // The finally-block belonging to [node] will be visited because of
1734 // fallthrough. If it does not exist, add an explicit goto. 1467 // fallthrough. If it does not exist, add an explicit goto.
1735 addGoto(nextLabel); 1468 addGoto(nextLabel);
1736 } 1469 }
1737 if (node.finallyPart != null) { 1470 if (node.finallyPart != null) {
1738 js.Node last = jumpTargets.removeLast(); 1471 js.Node last = jumpTargets.removeLast();
1739 assert(last == node.finallyPart); 1472 assert(last == node.finallyPart);
1740 1473
1741 beginLabel(finallyLabel); 1474 beginLabel(finallyLabel);
1742 setErrorHandler(); 1475 setErrorHandler();
1743 visitStatement(node.finallyPart); 1476 visitStatement(node.finallyPart);
1744 addStatement(new js.Comment("// goto the next finally handler")); 1477 addStatement(new js.Comment("// goto the next finally handler"));
1745 addStatement(js.js.statement("# = #.pop();", [gotoName, nextName])); 1478 addStatement(js.js.statement("# = #.pop();", [goto, next]));
1746 addBreak(); 1479 addBreak();
1747 } 1480 }
1748 beginLabel(afterFinallyLabel); 1481 beginLabel(afterFinallyLabel);
1749 } 1482 }
1750 1483
1751 @override 1484 @override
1752 visitVariableDeclaration(js.VariableDeclaration node) { 1485 visitVariableDeclaration(js.VariableDeclaration node) {
1753 unreachable(node); 1486 unreachable(node);
1754 } 1487 }
1755 1488
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1807 new js.Prefix("!", condition), gotoAndBreak(afterLabel))); 1540 new js.Prefix("!", condition), gotoAndBreak(afterLabel)));
1808 }, store: false); 1541 }, store: false);
1809 } 1542 }
1810 jumpTargets.add(node); 1543 jumpTargets.add(node);
1811 visitStatement(node.body); 1544 visitStatement(node.body);
1812 jumpTargets.removeLast(); 1545 jumpTargets.removeLast();
1813 addGoto(continueLabel); 1546 addGoto(continueLabel);
1814 beginLabel(afterLabel); 1547 beginLabel(afterLabel);
1815 } 1548 }
1816 1549
1550 addYield(js.DartYield node, js.Expression expression);
1551
1552 @override
1553 void visitDartYield(js.DartYield node) {
1554 assert(isSyncStar || isAsyncStar);
1555 int label = newLabel("after yield");
1556 // Don't do a break here for the goto, but instead a return in either
1557 // addSynYield or addAsyncYield.
1558 withExpression(node.expression, (js.Expression expression) {
1559 addStatement(setGotoVariable(label));
1560 addYield(node, expression);
1561 }, store: false);
1562 beginLabel(label);
1563 }
1564 }
1565
1566 js.VariableInitialization
1567 _makeVariableInitializer(dynamic variable, js.Expression initValue) {
1568 js.VariableDeclaration declaration;
1569 if (variable is js.VariableUse) {
1570 declaration = new js.VariableDeclaration(variable.name);
1571 } else if (variable is String) {
1572 declaration = new js.VariableDeclaration(variable);
1573 } else {
1574 assert(variable is js.VariableDeclaration);
1575 declaration = variable;
1576 }
1577 return new js.VariableInitialization(declaration, initValue);
1578 }
1579
1580 class AsyncRewriter extends AsyncRewriterBase {
1581
1582 bool get isAsync => true;
1583
1584 /// The Completer that will finish an async function.
1585 ///
1586 /// Not used for sync* or async* functions.
1587 String completerName;
1588 js.VariableUse get completer => new js.VariableUse(completerName);
1589
1590 /// The function called by an async function to simulate an await or return.
1591 ///
1592 /// For an await it is called with:
1593 ///
1594 /// - The value to await
1595 /// - The body function [bodyName]
1596 /// - The completer object [completer]
1597 ///
1598 /// For a return it is called with:
1599 ///
1600 /// - The value to complete the completer with.
1601 /// - [error_codes.SUCCESS]
1602 /// - The completer object [completer]
1603 ///
1604 /// For a throw it is called with:
1605 ///
1606 /// - The error to complete the completer with.
1607 /// - [error_codes.ERROR]
1608 /// - The completer object [completer]
1609 final js.Expression asyncHelper;
1610
1611 /// Contructor used to initialize the [completer] variable.
1612 ///
1613 /// Specific to async methods.
1614 final js.Expression newCompleter;
1615
1616
1617 AsyncRewriter(DiagnosticListener diagnosticListener,
1618 spannable,
1619 {this.asyncHelper,
1620 this.newCompleter,
1621 safeVariableName})
1622 : super(diagnosticListener,
1623 spannable,
1624 safeVariableName);
1625
1626 @override
1627 void addYield(js.DartYield node, js.Expression expression) {
1628 diagnosticListener.internalError(spannable,
1629 "Yield in non-generating async function");
1630 }
1631
1632 void addErrorExit() {
1633 beginLabel(rethrowLabel);
1634 addStatement(js.js.statement(
1635 "return #thenHelper(#currentError, #errorCode, #completer);", {
1636 "thenHelper": asyncHelper,
1637 "errorCode": js.number(error_codes.ERROR),
1638 "currentError": currentError,
1639 "completer": completer}));
1640 }
1641
1642 /// Returning from an async method calls [asyncStarHelper] with the result.
1643 /// (the result might have been stored in [returnValue] by some finally
1644 /// block).
1645 void addSuccesExit() {
1646 if (analysis.hasExplicitReturns) {
1647 beginLabel(exitLabel);
1648 } else {
1649 addStatement(new js.Comment("implicit return"));
1650 }
1651 addStatement(js.js.statement(
1652 "return #runtimeHelper(#returnValue, #successCode, "
1653 "#completer, null);", {
1654 "runtimeHelper": asyncHelper,
1655 "successCode": js.number(error_codes.SUCCESS),
1656 "returnValue": analysis.hasExplicitReturns
1657 ? returnValue
1658 : new js.LiteralNull(),
1659 "completer": completer}));
1660 }
1661
1662 @override
1663 Iterable<js.VariableInitialization> variableInitializations() {
1664 List<js.VariableInitialization> variables =
1665 new List<js.VariableInitialization>();
1666 variables.add(_makeVariableInitializer(completer,
1667 new js.New(newCompleter, [])));
1668 if (analysis.hasExplicitReturns) {
1669 variables.add(_makeVariableInitializer(returnValue, null));
1670 }
1671 return variables;
1672 }
1673
1674 @override
1675 void initializeNames() {
1676 completerName = freshName("completer");
1677 }
1678
1679 @override
1680 js.Statement awaitStatement(js.Expression value) {
1681 return js.js.statement("""
1682 return #asyncHelper(#value,
1683 #body,
1684 #completer);
1685 """, {
1686 "asyncHelper": asyncHelper,
1687 "value": value,
1688 "body": body,
1689 "completer": completer});
1690 }
1691
1692 @override
1693 js.Fun finishFunction(List<js.Parameter> parameters,
1694 js.Statement rewrittenBody,
1695 js.VariableDeclarationList variableDeclarations) {
1696 return js.js("""
1697 function (#parameters) {
1698 #variableDeclarations;
1699 function #bodyName(#errorCode, #result) {
1700 if (#errorCode === #ERROR) {
1701 #currentError = #result;
1702 #goto = #handler;
1703 }
1704 #rewrittenBody;
1705 }
1706 return #asyncHelper(null, #bodyName, #completer, null);
1707 }""", {
1708 "parameters": parameters,
1709 "variableDeclarations": variableDeclarations,
1710 "ERROR": js.number(error_codes.ERROR),
1711 "rewrittenBody": rewrittenBody,
1712 "bodyName": bodyName,
1713 "currentError": currentError,
1714 "goto": goto,
1715 "handler": handler,
1716 "errorCode": errorCodeName,
1717 "result": resultName,
1718 "asyncHelper": asyncHelper,
1719 "completer": completer,
1720 });
1721 }
1722 }
1723
1724 class SyncStarRewriter extends AsyncRewriterBase {
1725
1726 bool get isSyncStar => true;
1727
1728 /// Contructor creating the Iterable for a sync* method. Called with
1729 /// [bodyName].
1730 final js.Expression newIterable;
1731
1732 /// A JS Expression that creates a marker showing that iteration is over.
1733 ///
1734 /// Called without arguments.
1735 final js.Expression endOfIteration;
1736
1737 /// A JS Expression that creates a marker indication a 'yield*' statement.
1738 ///
1739 /// Called with the stream to yield from.
1740 final js.Expression yieldStarExpression;
1741
1742 /// Used by sync* functions to throw exeptions.
1743 final js.Expression uncaughtErrorExpression;
1744
1745 SyncStarRewriter(DiagnosticListener diagnosticListener,
1746 spannable,
1747 {this.endOfIteration,
1748 this.newIterable,
1749 this.yieldStarExpression,
1750 this.uncaughtErrorExpression,
1751 safeVariableName})
1752 : super(diagnosticListener,
1753 spannable,
1754 safeVariableName);
1755
1817 /// Translates a yield/yield* in an sync*. 1756 /// Translates a yield/yield* in an sync*.
1818 /// 1757 ///
1819 /// `yield` in a sync* function just returns [value]. 1758 /// `yield` in a sync* function just returns [value].
1820 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it. 1759 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it.
1821 void addSyncYield(js.DartYield node, js.Expression expression) { 1760 @override
1822 assert(isSyncStar); 1761 void addYield(js.DartYield node, js.Expression expression) {
1823 if (node.hasStar) { 1762 if (node.hasStar) {
1824 addStatement( 1763 addStatement(
1825 new js.Return(new js.Call(yieldStarExpression, [expression]))); 1764 new js.Return(new js.Call(yieldStarExpression, [expression])));
1826 } else { 1765 } else {
1827 addStatement(new js.Return(expression)); 1766 addStatement(new js.Return(expression));
1828 } 1767 }
1829 } 1768 }
1830 1769
1770 @override
1771 js.Fun finishFunction(List<js.Parameter> params,
1772 js.Statement rewrittenBody,
1773 js.VariableDeclarationList variableDeclarations) {
1774 return js.js("""
1775 function (#params) {
1776 if (#needsThis)
1777 var #self = this;
1778 return new #newIterable(function () {
1779 #varDecl;
1780 return function #body(#errorCode, #result) {
1781 if (#errorCode === #ERROR) {
1782 #currentError = #result;
1783 #goto = #handler;
1784 }
1785 #helperBody;
1786 };
1787 });
1788 }
1789 """, {
1790 "params": params,
1791 "needsThis": analysis.hasThis,
1792 "helperBody": rewrittenBody,
1793 "varDecl": variableDeclarations,
1794 "errorCode": errorCodeName,
1795 "newIterable": newIterable,
1796 "body": bodyName,
1797 "self": selfName,
1798 "result": resultName,
1799 "goto": goto,
1800 "handler": handler,
1801 "currentError": currentErrorName,
1802 "ERROR": js.number(error_codes.ERROR),
1803 });
1804 }
1805
1806 void addErrorExit() {
1807 beginLabel(rethrowLabel);
1808 addStatement(js.js.statement('return #(#);',
1809 [uncaughtErrorExpression, currentError]));
1810 }
1811
1812 /// Returning from a sync* function returns an [endOfIteration] marker.
1813 void addSuccesExit() {
1814 if (analysis.hasExplicitReturns) {
1815 beginLabel(exitLabel);
1816 } else {
1817 addStatement(new js.Comment("implicit return"));
1818 }
1819 addStatement(js.js.statement('return #();', [endOfIteration]));
1820 }
1821
1822 @override
1823 Iterable<js.VariableInitialization> variableInitializations() {
1824 List<js.VariableInitialization> variables =
1825 new List<js.VariableInitialization>();
1826 return variables;
1827 }
1828
1829 @override
1830 js.Statement awaitStatement(js.Expression value) {
1831 throw diagnosticListener.internalError(spannable,
1832 "Sync* functions cannot contain await statements.");
1833 }
1834
1835 @override
1836 void initializeNames() {}
1837 }
1838
1839 class AsyncStarRewriter extends AsyncRewriterBase {
1840
1841 bool get isAsyncStar => true;
1842
1843 /// The stack of labels of finally blocks to assign to [next] if the
1844 /// async* [StreamSubscription] was canceled during a yield.
1845 js.VariableUse get nextWhenCanceled {
1846 return new js.VariableUse(nextWhenCanceledName);
1847 }
1848 String nextWhenCanceledName;
1849
1850 /// The StreamController that controls an async* function.
1851 String controllerName;
1852 js.VariableUse get controller => new js.VariableUse(controllerName);
1853
1854 /// The function called by an async* function to simulate an await, yield or
1855 /// yield*.
1856 ///
1857 /// For an await/yield/yield* it is called with:
1858 ///
1859 /// - The value to await/yieldExpression(value to yield)/
1860 /// yieldStarExpression(stream to yield)
1861 /// - The body function [bodyName]
1862 /// - The controller object [controllerName]
1863 ///
1864 /// For a return it is called with:
1865 ///
1866 /// - null
1867 /// - null
1868 /// - The [controllerName]
1869 /// - null.
1870 final js.Expression asyncStarHelper;
1871
1872 /// Contructor used to initialize the [controllerName] variable.
1873 ///
1874 /// Specific to async* methods.
1875 final js.Expression newController;
1876
1877 /// Used to get the `Stream` out of the [controllerName] variable.
1878 final js.Expression streamOfController;
1879
1880 /// A JS Expression that creates a marker indicating a 'yield' statement.
1881 ///
1882 /// Called with the value to yield.
1883 final js.Expression yieldExpression;
1884
1885 /// A JS Expression that creates a marker indication a 'yield*' statement.
1886 ///
1887 /// Called with the stream to yield from.
1888 final js.Expression yieldStarExpression;
1889
1890 AsyncStarRewriter(DiagnosticListener diagnosticListener,
1891 spannable,
1892 {this.asyncStarHelper,
1893 this.streamOfController,
1894 this.newController,
1895 this.yieldExpression,
1896 this.yieldStarExpression,
1897 String safeVariableName(String original)})
1898 : super(diagnosticListener,
1899 spannable,
1900 safeVariableName);
1901
1902
1831 /// Translates a yield/yield* in an async* function. 1903 /// Translates a yield/yield* in an async* function.
1832 /// 1904 ///
1833 /// yield/yield* in an async* function is translated much like the `await` is 1905 /// yield/yield* in an async* function is translated much like the `await` is
1834 /// translated in [visitAwait], only the object is wrapped in a 1906 /// translated in [visitAwait], only the object is wrapped in a
1835 /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper] 1907 /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper]
1836 /// distinguish them. 1908 /// distinguish them.
1837 /// Also [nextWhenCanceledName] is set up to contain the finally blocks that 1909 /// Also [nextWhenCanceled] is set up to contain the finally blocks that
1838 /// must be run in case the stream was canceled. 1910 /// must be run in case the stream was canceled.
1839 void addAsyncYield(js.DartYield node, js.Expression expression) { 1911 @override
1840 assert(isAsyncStar); 1912 void addYield(js.DartYield node, js.Expression expression) {
1841 // Find all the finally blocks that should be performed if the stream is 1913 // Find all the finally blocks that should be performed if the stream is
1842 // canceled during the yield. 1914 // canceled during the yield.
1843 // At the bottom of the stack is the return label. 1915 // At the bottom of the stack is the return label.
1844 List<int> enclosingFinallyLabels = <int>[exitLabel]; 1916 List<int> enclosingFinallyLabels = <int>[exitLabel];
1845 enclosingFinallyLabels.addAll(jumpTargets 1917 enclosingFinallyLabels.addAll(jumpTargets
1846 .where((js.Node node) => finallyLabels[node] != null) 1918 .where((js.Node node) => finallyLabels[node] != null)
1847 .map((js.Block node) => finallyLabels[node])); 1919 .map((js.Block node) => finallyLabels[node]));
1848 addStatement(js.js.statement("# = #;", 1920 addStatement(js.js.statement("# = #;",
1849 [nextWhenCanceledName, new js.ArrayInitializer( 1921 [nextWhenCanceled, new js.ArrayInitializer(
1850 enclosingFinallyLabels.map(js.number).toList())])); 1922 enclosingFinallyLabels.map(js.number).toList())]));
1851 addStatement(js.js.statement(""" 1923 addStatement(js.js.statement("""
1852 return #streamHelper(#yieldExpression(#expression), #body, 1924 return #asyncStarHelper(#yieldExpression(#expression), #body,
1853 #controller);""", { 1925 #controller);""", {
1854 "streamHelper": streamHelper, 1926 "asyncStarHelper": asyncStarHelper,
1855 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression, 1927 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression,
1856 "expression": expression, 1928 "expression": expression,
1857 "body": bodyName, 1929 "body": body,
1858 "controller": controllerName, 1930 "controller": controllerName,
1859 })); 1931 }));
1860 } 1932 }
1861 1933
1862 @override 1934 @override
1863 void visitDartYield(js.DartYield node) { 1935 js.Fun finishFunction(List<js.Parameter> parameters,
1864 assert(isSyncStar || isAsyncStar); 1936 js.Statement rewrittenBody,
1865 int label = newLabel("after yield"); 1937 js.VariableDeclarationList variableDeclarations) {
1866 // Don't do a break here for the goto, but instead a return in either 1938 return js.js("""
1867 // addSynYield or addAsyncYield. 1939 function (#parameters) {
1868 withExpression(node.expression, (js.Expression expression) { 1940 #variableDeclarations;
1869 addStatement(setGotoVariable(label)); 1941 function #bodyName(#errorCode, #result) {
1870 if (isSyncStar) { 1942 if (#hasYield) {
1871 addSyncYield(node, expression); 1943 switch (#errorCode) {
1872 } else { 1944 case #STREAM_WAS_CANCELED:
1873 addAsyncYield(node, expression); 1945 #next = #nextWhenCanceled;
1874 } 1946 #goto = #next.pop();
1875 }, store: false); 1947 break;
1876 beginLabel(label); 1948 case #ERROR:
1949 #currentError = #result;
1950 #goto = #handler;
1951 }
1952 } else {
1953 if (#errorCode === #ERROR) {
1954 #currentError = #result;
1955 #goto = #handler;
1956 }
1957 }
1958 #rewrittenBody;
1959 }
1960 return #streamOfController(#controller);
1961 }""", {
1962 "parameters": parameters,
1963 "variableDeclarations": variableDeclarations,
1964 "STREAM_WAS_CANCELED": js.number(error_codes.STREAM_WAS_CANCELED),
1965 "ERROR": js.number(error_codes.ERROR),
1966 "hasYield": analysis.hasYield,
1967 "rewrittenBody": rewrittenBody,
1968 "bodyName": bodyName,
1969 "currentError": currentError,
1970 "goto": goto,
1971 "handler": handler,
1972 "next": next,
1973 "nextWhenCanceled": nextWhenCanceled,
1974 "errorCode": errorCodeName,
1975 "result": resultName,
1976 "streamOfController": streamOfController,
1977 "controller": controllerName,
1978 });
1979 }
1980
1981 @override
1982 void addErrorExit() {
1983 beginLabel(rethrowLabel);
1984 addStatement(js.js.statement(
1985 "return #asyncHelper(#currentError, #errorCode, #controller);", {
1986 "asyncHelper": asyncStarHelper,
1987 "errorCode": js.number(error_codes.ERROR),
1988 "currentError": currentError,
1989 "controller": controllerName}));
1990 }
1991
1992 /// Returning from an async* function calls the [streamHelper] with an
1993 /// [endOfIteration] marker.
1994 @override
1995 void addSuccesExit() {
1996 beginLabel(exitLabel);
1997
1998 addStatement(js.js.statement(
1999 "return #streamHelper(null, #successCode, #controller);", {
2000 "streamHelper": asyncStarHelper,
2001 "successCode": js.number(error_codes.SUCCESS),
2002 "controller": controllerName}));
2003 }
2004
2005 @override
2006 Iterable<js.VariableInitialization> variableInitializations() {
2007 List<js.VariableInitialization> variables =
2008 new List<js.VariableInitialization>();
2009 variables.add(_makeVariableInitializer(controller,
2010 js.js('#(#)', [newController, bodyName])));
2011 if (analysis.hasYield) {
2012 variables.add(_makeVariableInitializer(nextWhenCanceled, null));
2013 }
2014 return variables;
2015 }
2016
2017 @override
2018 void initializeNames() {
2019 controllerName = freshName("controller");
2020 nextWhenCanceledName = freshName("nextWhenCanceled");
2021 }
2022
2023 @override
2024 js.Statement awaitStatement(js.Expression value) {
2025 return js.js.statement("""
2026 return #asyncHelper(#value,
2027 #body,
2028 #controller);
2029 """, {
2030 "asyncHelper": asyncStarHelper,
2031 "value": value,
2032 "body": body,
2033 "controller": controllerName});
1877 } 2034 }
1878 } 2035 }
1879 2036
1880 /// Finds out 2037 /// Finds out
1881 /// 2038 ///
1882 /// - which expressions have yield or await nested in them. 2039 /// - which expressions have yield or await nested in them.
1883 /// - targets of jumps 2040 /// - targets of jumps
1884 /// - a set of used names. 2041 /// - a set of used names.
1885 /// - if any [This]-expressions are used. 2042 /// - if any [This]-expressions are used.
1886 class PreTranslationAnalysis extends js.NodeVisitor<bool> { 2043 class PreTranslationAnalysis extends js.NodeVisitor<bool> {
(...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after
2294 return condition || body; 2451 return condition || body;
2295 } 2452 }
2296 2453
2297 @override 2454 @override
2298 bool visitDartYield(js.DartYield node) { 2455 bool visitDartYield(js.DartYield node) {
2299 hasYield = true; 2456 hasYield = true;
2300 visit(node.expression); 2457 visit(node.expression);
2301 return true; 2458 return true;
2302 } 2459 }
2303 } 2460 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/ssa/builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698