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

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: Few more fixes Created 5 years, 10 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 jumps to (after going
floitsch 2015/02/25 16:27:59 jump
sigurdm 2015/02/27 09:46:33 Done.
sigurdm 2015/02/27 09:46:33 Done.
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 generateFunction(List<js.Parameter> parameters,
sigurdm 2015/02/25 11:43:18 Needs a better name...
floitsch 2015/02/25 16:27:59 finishFunction ?
sigurdm 2015/02/27 09:46:33 Done.
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 }
827 652
828 List<js.VariableInitialization> inits = <js.VariableInitialization>[]; 653 List<js.VariableInitialization> variables =
654 new List<js.VariableInitialization>();
829 655
830 js.VariableInitialization makeInit(String name, js.Expression initValue) { 656 variables.add(_makeVariableInitializer(goto, js.number(0)));
831 return new js.VariableInitialization( 657 variables.addAll(variableInitializations());
832 new js.VariableDeclaration(name), initValue); 658 variables.add(
833 } 659 _makeVariableInitializer(handler, js.number(rethrowLabel)));
834 660 variables.add(_makeVariableInitializer(currentError, null));
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 // TODO(sigurdm): Explain the difference between syncStar and the other
floitsch 2015/02/25 16:27:59 I think you can remove that todo now.
sigurdm 2015/02/27 09:46:33 Done.
867 function (#params) { 679 // implementations of generateFunction.
868 if (#needsThis) 680 return generateMainFunction(node.params, rewrittenBody, variableDeclarations );
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 } 681 }
939 682
940 @override 683 @override
941 js.Expression visitAccess(js.PropertyAccess node) { 684 js.Expression visitAccess(js.PropertyAccess node) {
942 return withExpression2(node.receiver, node.selector, 685 return withExpression2(node.receiver, node.selector,
943 (receiver, selector) => js.js('#[#]', [receiver, selector])); 686 (receiver, selector) => js.js('#[#]', [receiver, selector]));
944 } 687 }
945 688
946 @override 689 @override
947 js.Expression visitArrayHole(js.ArrayHole node) { 690 js.Expression visitArrayHole(js.ArrayHole node) {
(...skipping 26 matching lines...) Expand all
974 ], (evaluated) { 717 ], (evaluated) {
975 return new js.Assignment.compound( 718 return new js.Assignment.compound(
976 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op, 719 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op,
977 evaluated[2]); 720 evaluated[2]);
978 }); 721 });
979 } else { 722 } else {
980 throw "Unexpected assignment left hand side $leftHandSide"; 723 throw "Unexpected assignment left hand side $leftHandSide";
981 } 724 }
982 } 725 }
983 726
984 /// An await is translated to a call to [asyncHelper]/[streamHelper]. 727 js.Statement awaitStatement(js.Expression value);
728
729 /// An await is translated to a [awaitStatement.
985 /// 730 ///
986 /// See the comments of [visitFun] for an example. 731 /// See the comments of [visitFun] for an example.
987 @override 732 @override
988 js.Expression visitAwait(js.Await node) { 733 js.Expression visitAwait(js.Await node) {
floitsch 2015/02/25 16:27:59 This should probably go into an AsyncRewriterBase.
sigurdm 2015/02/27 09:46:33 Left it for now
989 assert(isAsync || isAsyncStar); 734 assert(isAsync || isAsyncStar);
990 int afterAwait = newLabel("returning from await."); 735 int afterAwait = newLabel("returning from await.");
991 withExpression(node.expression, (js.Expression value) { 736 withExpression(node.expression, (js.Expression value) {
992 addStatement(setGotoVariable(afterAwait)); 737 addStatement(setGotoVariable(afterAwait));
993 addStatement(js.js.statement(""" 738 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); 739 }, store: false);
1004 beginLabel(afterAwait); 740 beginLabel(afterAwait);
1005 return new js.VariableUse(resultName); 741 return result;
1006 } 742 }
1007 743
1008 /// Checks if [node] is the variable named [resultName]. 744 /// Checks if [node] is the variable named [resultName].
1009 /// 745 ///
1010 /// [resultName] is used to hold the result of a transformed computation 746 /// [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 747 /// for example the result of awaiting, or the result of a conditional or
1012 /// short-circuiting expression. 748 /// short-circuiting expression.
1013 /// If the subexpression of some transformed node already is transformed and 749 /// If the subexpression of some transformed node already is transformed and
1014 /// visiting it returns [resultName], it is not redundantly assigned to itself 750 /// visiting it returns [result], it is not redundantly assigned to itself
1015 /// again. 751 /// again.
1016 bool isResult(js.Expression node) { 752 bool isResult(js.Expression node) {
1017 return node is js.VariableUse && node.name == resultName; 753 return node is js.VariableUse && node.name == resultName;
1018 } 754 }
1019 755
1020 @override 756 @override
1021 js.Expression visitBinary(js.Binary node) { 757 js.Expression visitBinary(js.Binary node) {
1022 if (shouldTransform(node.right) && (node.op == "||" || node.op == "&&")) { 758 if (shouldTransform(node.right) && (node.op == "||" || node.op == "&&")) {
1023 int thenLabel = newLabel("then"); 759 int thenLabel = newLabel("then");
1024 int joinLabel = newLabel("join"); 760 int joinLabel = newLabel("join");
1025 withExpression(node.left, (js.Expression left) { 761 withExpression(node.left, (js.Expression left) {
1026 js.Statement assignLeft = isResult(left) 762 js.Statement assignLeft = isResult(left)
1027 ? new js.Block.empty() 763 ? new js.Block.empty()
1028 : js.js.statement('# = #;', [resultName, left]); 764 : js.js.statement('# = #;', [result, left]);
1029 if (node.op == "||") { 765 if (node.op == "||") {
1030 addStatement(js.js.statement('if (#) {#} else #', 766 addStatement(js.js.statement('if (#) {#} else #',
1031 [left, gotoAndBreak(thenLabel), assignLeft])); 767 [left, gotoAndBreak(thenLabel), assignLeft]));
1032 } else { 768 } else {
1033 assert(node.op == "&&"); 769 assert(node.op == "&&");
1034 addStatement(js.js.statement('if (#) {#} else #', 770 addStatement(js.js.statement('if (#) {#} else #',
1035 [left, assignLeft, gotoAndBreak(thenLabel)])); 771 [left, assignLeft, gotoAndBreak(thenLabel)]));
1036 } 772 }
1037 }, store: true); 773 }, store: true);
1038 addGoto(joinLabel); 774 addGoto(joinLabel);
1039 beginLabel(thenLabel); 775 beginLabel(thenLabel);
1040 withExpression(node.right, (js.Expression value) { 776 withExpression(node.right, (js.Expression value) {
1041 if (!isResult(value)) { 777 if (!isResult(value)) {
1042 addStatement(js.js.statement('# = #;', [resultName, value])); 778 addStatement(js.js.statement('# = #;', [result, value]));
1043 } 779 }
1044 }, store: false); 780 }, store: false);
1045 beginLabel(joinLabel); 781 beginLabel(joinLabel);
1046 return new js.VariableUse(resultName); 782 return result;
1047 } 783 }
1048 784
1049 return withExpression2(node.left, node.right, 785 return withExpression2(node.left, node.right,
1050 (left, right) => new js.Binary(node.op, left, right)); 786 (left, right) => new js.Binary(node.op, left, right));
1051 } 787 }
1052 788
1053 @override 789 @override
1054 void visitBlock(js.Block node) { 790 void visitBlock(js.Block node) {
1055 for (js.Statement statement in node.statements) { 791 for (js.Statement statement in node.statements) {
1056 visitStatement(statement); 792 visitStatement(statement);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1097 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) { 833 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
1098 return withExpression(node.condition, (js.Expression condition) { 834 return withExpression(node.condition, (js.Expression condition) {
1099 return js.js('# ? # : #', [condition, node.then, node.otherwise]); 835 return js.js('# ? # : #', [condition, node.then, node.otherwise]);
1100 }); 836 });
1101 } 837 }
1102 int thenLabel = newLabel("then"); 838 int thenLabel = newLabel("then");
1103 int joinLabel = newLabel("join"); 839 int joinLabel = newLabel("join");
1104 int elseLabel = newLabel("else"); 840 int elseLabel = newLabel("else");
1105 withExpression(node.condition, (js.Expression condition) { 841 withExpression(node.condition, (js.Expression condition) {
1106 addStatement(js.js.statement('# = # ? # : #;', 842 addStatement(js.js.statement('# = # ? # : #;',
1107 [gotoName, condition, js.number(thenLabel), js.number(elseLabel)])); 843 [goto, condition, js.number(thenLabel), js.number(elseLabel)]));
1108 }, store: false); 844 }, store: false);
1109 addBreak(); 845 addBreak();
1110 beginLabel(thenLabel); 846 beginLabel(thenLabel);
1111 withExpression(node.then, (js.Expression value) { 847 withExpression(node.then, (js.Expression value) {
1112 if (!isResult(value)) { 848 if (!isResult(value)) {
1113 addStatement(js.js.statement('# = #;', [resultName, value])); 849 addStatement(js.js.statement('# = #;', [result, value]));
1114 } 850 }
1115 }, store: false); 851 }, store: false);
1116 addGoto(joinLabel); 852 addGoto(joinLabel);
1117 beginLabel(elseLabel); 853 beginLabel(elseLabel);
1118 withExpression(node.otherwise, (js.Expression value) { 854 withExpression(node.otherwise, (js.Expression value) {
1119 if (!isResult(value)) { 855 if (!isResult(value)) {
1120 addStatement(js.js.statement('# = #;', [resultName, value])); 856 addStatement(js.js.statement('# = #;', [result, value]));
1121 } 857 }
1122 }, store: false); 858 }, store: false);
1123 beginLabel(joinLabel); 859 beginLabel(joinLabel);
1124 return new js.VariableUse(resultName); 860 return result;
1125 } 861 }
1126 862
1127 @override 863 @override
1128 void visitContinue(js.Continue node) { 864 void visitContinue(js.Continue node) {
1129 js.Node target = analysis.targets[node]; 865 js.Node target = analysis.targets[node];
1130 if (!shouldTransform(target)) { 866 if (!shouldTransform(target)) {
1131 addStatement(node); 867 addStatement(node);
1132 return; 868 return;
1133 } 869 }
1134 translateJump(target, continueLabels[target]); 870 translateJump(target, continueLabels[target]);
1135 } 871 }
1136 872
1137 /// Emits a break statement that exits the big switch statement. 873 /// Emits a break statement that exits the big switch statement.
1138 void addBreak() { 874 void addBreak() {
1139 if (insideUntranslatedBreakable) { 875 if (insideUntranslatedBreakable) {
1140 hasJumpThoughOuterLabel = true; 876 hasJumpThoughOuterLabel = true;
1141 addStatement(new js.Break(outerLabelName)); 877 addStatement(new js.Break(outerLabelName));
1142 } else { 878 } else {
1143 addStatement(new js.Break(null)); 879 addStatement(new js.Break(null));
1144 } 880 }
1145 } 881 }
1146 882
1147 /// Common code for handling break, continue, return. 883 /// Common code for handling break, continue, return.
1148 /// 884 ///
1149 /// It is necessary to run all nesting finally-handlers between the jump and 885 /// 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. 886 /// the target. For that [next] is used as a stack of places to go.
1151 /// 887 ///
1152 /// See also [visitFun]. 888 /// See also [visitFun].
1153 void translateJump(js.Node target, int targetLabel) { 889 void translateJump(js.Node target, int targetLabel) {
1154 // Compute a stack of all the 'finally' nodes that must be visited before 890 // Compute a stack of all the 'finally' nodes that must be visited before
1155 // the jump. 891 // the jump.
1156 // The bottom of the stack is the label where the jump goes to. 892 // The bottom of the stack is the label where the jump goes to.
1157 List<int> jumpStack = new List<int>(); 893 List<int> jumpStack = new List<int>();
1158 for (js.Node node in jumpTargets.reversed) { 894 for (js.Node node in jumpTargets.reversed) {
1159 if (finallyLabels[node] != null) { 895 if (finallyLabels[node] != null) {
1160 jumpStack.add(finallyLabels[node]); 896 jumpStack.add(finallyLabels[node]);
1161 } else if (node == target) { 897 } else if (node == target) {
1162 jumpStack.add(targetLabel); 898 jumpStack.add(targetLabel);
1163 break; 899 break;
1164 } 900 }
1165 // Ignore other nodes. 901 // Ignore other nodes.
1166 } 902 }
1167 jumpStack = jumpStack.reversed.toList(); 903 jumpStack = jumpStack.reversed.toList();
1168 // As the program jumps directly to the top of the stack, it is taken off 904 // As the program jumps directly to the top of the stack, it is taken off
1169 // now. 905 // now.
1170 int firstTarget = jumpStack.removeLast(); 906 int firstTarget = jumpStack.removeLast();
1171 if (jumpStack.isNotEmpty) { 907 if (jumpStack.isNotEmpty) {
1172 js.Expression jsJumpStack = new js.ArrayInitializer( 908 js.Expression jsJumpStack = new js.ArrayInitializer(
1173 jumpStack.map((int label) => js.number(label)).toList()); 909 jumpStack.map((int label) => js.number(label)).toList());
1174 addStatement(js.js.statement("# = #;", [nextName, jsJumpStack])); 910 addStatement(js.js.statement("# = #;", [next, jsJumpStack]));
1175 } 911 }
1176 addGoto(firstTarget); 912 addGoto(firstTarget);
1177 } 913 }
1178 914
1179 @override 915 @override
1180 void visitDefault(js.Default node) => unreachable(node); 916 void visitDefault(js.Default node) => unreachable(node);
1181 917
1182 @override 918 @override
1183 void visitDo(js.Do node) { 919 void visitDo(js.Do node) {
1184 if (!shouldTransform(node)) { 920 if (!shouldTransform(node)) {
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
1309 void visitIf(js.If node) { 1045 void visitIf(js.If node) {
1310 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) { 1046 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
1311 withExpression(node.condition, (js.Expression condition) { 1047 withExpression(node.condition, (js.Expression condition) {
1312 addStatement(new js.If(condition, translateInBlock(node.then), 1048 addStatement(new js.If(condition, translateInBlock(node.then),
1313 translateInBlock(node.otherwise))); 1049 translateInBlock(node.otherwise)));
1314 }, store: false); 1050 }, store: false);
1315 return; 1051 return;
1316 } 1052 }
1317 int thenLabel = newLabel("then"); 1053 int thenLabel = newLabel("then");
1318 int joinLabel = newLabel("join"); 1054 int joinLabel = newLabel("join");
1319 int elseLabel = 1055 int elseLabel = (node.otherwise is js.EmptyStatement)
1320 node.otherwise is js.EmptyStatement ? joinLabel : newLabel("else"); 1056 ? joinLabel
1057 : newLabel("else");
1321 1058
1322 withExpression(node.condition, (js.Expression condition) { 1059 withExpression(node.condition, (js.Expression condition) {
1323 addExpressionStatement( 1060 addExpressionStatement(
1324 new js.Assignment( 1061 new js.Assignment(
1325 new js.VariableUse(gotoName), 1062 goto,
1326 new js.Conditional( 1063 new js.Conditional(
1327 condition, 1064 condition,
1328 js.number(thenLabel), 1065 js.number(thenLabel),
1329 js.number(elseLabel)))); 1066 js.number(elseLabel))));
1330 }, store: false); 1067 }, store: false);
1331 addBreak(); 1068 addBreak();
1332 beginLabel(thenLabel); 1069 beginLabel(thenLabel);
1333 visitStatement(node.then); 1070 visitStatement(node.then);
1334 if (node.otherwise is! js.EmptyStatement) { 1071 if (node.otherwise is! js.EmptyStatement) {
1335 addGoto(joinLabel); 1072 addGoto(joinLabel);
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
1486 return withExpression( 1223 return withExpression(
1487 node.value, (js.Expression value) => new js.Property(node.name, value), 1224 node.value, (js.Expression value) => new js.Property(node.name, value),
1488 store: false); 1225 store: false);
1489 } 1226 }
1490 1227
1491 @override 1228 @override
1492 js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node; 1229 js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node;
1493 1230
1494 @override 1231 @override
1495 void visitReturn(js.Return node) { 1232 void visitReturn(js.Return node) {
1496 assert(node.value == null || !isSyncStar && !isAsyncStar); 1233 assert(node.value == null || (!isSyncStar && !isAsyncStar));
1497 js.Node target = analysis.targets[node]; 1234 js.Node target = analysis.targets[node];
1498 if (node.value != null) { 1235 if (node.value != null) {
1499 withExpression(node.value, (js.Expression value) { 1236 withExpression(node.value, (js.Expression value) {
1500 addStatement(js.js.statement("# = #;", [returnValueName, value])); 1237 addStatement(js.js.statement("# = #;", [returnValue, value]));
1501 }, store: false); 1238 }, store: false);
1502 } 1239 }
1503 translateJump(target, exitLabel); 1240 translateJump(target, exitLabel);
1504 } 1241 }
1505 1242
1506 @override 1243 @override
1507 void visitSwitch(js.Switch node) { 1244 void visitSwitch(js.Switch node) {
1508 if (!node.cases.any(shouldTransform)) { 1245 if (!node.cases.any(shouldTransform)) {
1509 // If only the key has an await, translation can be simplified. 1246 // If only the key has an await, translation can be simplified.
1510 bool oldInsideUntranslated = insideUntranslatedBreakable; 1247 bool oldInsideUntranslated = insideUntranslatedBreakable;
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
1592 for (int i = 0; i < labels.length; i++) { 1329 for (int i = 0; i < labels.length; i++) {
1593 beginLabel(labels[i]); 1330 beginLabel(labels[i]);
1594 visitStatement(node.cases[i].body); 1331 visitStatement(node.cases[i].body);
1595 } 1332 }
1596 beginLabel(after); 1333 beginLabel(after);
1597 jumpTargets.removeLast(); 1334 jumpTargets.removeLast();
1598 } 1335 }
1599 1336
1600 @override 1337 @override
1601 js.Expression visitThis(js.This node) { 1338 js.Expression visitThis(js.This node) {
1602 return new js.VariableUse(selfName); 1339 return self;
1603 } 1340 }
1604 1341
1605 @override 1342 @override
1606 void visitThrow(js.Throw node) { 1343 void visitThrow(js.Throw node) {
1607 withExpression(node.expression, (js.Expression expression) { 1344 withExpression(node.expression, (js.Expression expression) {
1608 addStatement(new js.Throw(expression)); 1345 addStatement(new js.Throw(expression));
1609 }, store: false); 1346 }, store: false);
1610 } 1347 }
1611 1348
1612 setErrorHandler([int errorHandler]) { 1349 setErrorHandler([int errorHandler]) {
1613 addExpressionStatement(new js.Assignment( 1350 js.Expression label = (errorHandler == null)
1614 new js.VariableUse(handlerName), 1351 ? currentErrorHandler
1615 errorHandler == null ? currentErrorHandler : js.number(errorHandler))); 1352 : js.number(errorHandler);
1353 addStatement(js.js.statement('# = #;',[handler, label]));
1616 } 1354 }
1617 1355
1618 List<int> _finalliesUpToAndEnclosingHandler() { 1356 List<int> _finalliesUpToAndEnclosingHandler() {
1619 List<int> result = new List<int>(); 1357 List<int> result = new List<int>();
1620 for (int i = jumpTargets.length - 1; i >= 0; i--) { 1358 for (int i = jumpTargets.length - 1; i >= 0; i--) {
1621 js.Node node = jumpTargets[i]; 1359 js.Node node = jumpTargets[i];
1622 int handlerLabel = handlerLabels[node]; 1360 int handlerLabel = handlerLabels[node];
1623 if (handlerLabel != null) { 1361 if (handlerLabel != null) {
1624 result.add(handlerLabel); 1362 result.add(handlerLabel);
1625 break; 1363 break;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
1671 1409
1672 js.Node last = jumpTargets.removeLast(); 1410 js.Node last = jumpTargets.removeLast();
1673 assert(last == node); 1411 assert(last == node);
1674 1412
1675 if (node.finallyPart == null) { 1413 if (node.finallyPart == null) {
1676 setErrorHandler(); 1414 setErrorHandler();
1677 addGoto(afterFinallyLabel); 1415 addGoto(afterFinallyLabel);
1678 } else { 1416 } else {
1679 // The handler is reset as the first thing in the finally block. 1417 // The handler is reset as the first thing in the finally block.
1680 addStatement( 1418 addStatement(
1681 js.js.statement("# = [#];", 1419 js.js.statement("# = [#];", [next, js.number(afterFinallyLabel)]));
1682 [nextName, js.number(afterFinallyLabel)]));
1683 addGoto(finallyLabel); 1420 addGoto(finallyLabel);
1684 } 1421 }
1685 1422
1686 if (node.catchPart != null) { 1423 if (node.catchPart != null) {
1687 beginLabel(handlerLabel); 1424 beginLabel(handlerLabel);
1688 // [uncaughtLabel] is the handler for the code in the catch-part. 1425 // [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. 1426 // It ensures that [nextName] is set up to run the right finally blocks.
1690 handlerLabels[node.catchPart] = uncaughtLabel; 1427 handlerLabels[node.catchPart] = uncaughtLabel;
1691 jumpTargets.add(node.catchPart); 1428 jumpTargets.add(node.catchPart);
1692 setErrorHandler(); 1429 setErrorHandler();
1693 // The catch declaration name can shadow outer variables, so a fresh name 1430 // The catch declaration name can shadow outer variables, so a fresh name
1694 // is needed to avoid collisions. See Ecma 262, 3rd edition, 1431 // is needed to avoid collisions. See Ecma 262, 3rd edition,
1695 // section 12.14. 1432 // section 12.14.
1696 String errorRename = freshName(node.catchPart.declaration.name); 1433 String errorRename = freshName(node.catchPart.declaration.name);
1697 localVariables.add(new js.VariableDeclaration(errorRename)); 1434 localVariables.add(new js.VariableDeclaration(errorRename));
1698 variableRenamings 1435 variableRenamings
1699 .add(new Pair(node.catchPart.declaration.name, errorRename)); 1436 .add(new Pair(node.catchPart.declaration.name, errorRename));
1700 addExpressionStatement(new js.Assignment( 1437 addStatement(js.js.statement("# = #;", [errorRename, currentError]));
1701 new js.VariableUse(errorRename),
1702 new js.VariableUse(currentErrorName)));
1703 visitStatement(node.catchPart.body); 1438 visitStatement(node.catchPart.body);
1704 variableRenamings.removeLast(); 1439 variableRenamings.removeLast();
1705 if (node.finallyPart != null) { 1440 if (node.finallyPart != null) {
1706 // The error has been caught, so after the finally, continue after the 1441 // The error has been caught, so after the finally, continue after the
1707 // try. 1442 // try.
1708 addStatement(js.js.statement("# = [#];", 1443 addStatement(js.js.statement("# = [#];",
1709 [nextName, js.number(afterFinallyLabel)])); 1444 [next, js.number(afterFinallyLabel)]));
1710 addGoto(finallyLabel); 1445 addGoto(finallyLabel);
1711 } else { 1446 } else {
1712 addGoto(afterFinallyLabel); 1447 addGoto(afterFinallyLabel);
1713 } 1448 }
1714 js.Node last = jumpTargets.removeLast(); 1449 js.Node last = jumpTargets.removeLast();
1715 assert(last == node.catchPart); 1450 assert(last == node.catchPart);
1716 } 1451 }
1717 1452
1718 // The "uncaught"-handler tells the finally-block to continue with 1453 // The "uncaught"-handler tells the finally-block to continue with
1719 // the enclosing finally-blocks until the current catch-handler. 1454 // the enclosing finally-blocks until the current catch-handler.
1720 beginLabel(uncaughtLabel); 1455 beginLabel(uncaughtLabel);
1721 1456
1722 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler(); 1457 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler();
1723 1458
1724 int nextLabel = enclosingFinallies.removeLast(); 1459 int nextLabel = enclosingFinallies.removeLast();
1725 if (enclosingFinallies.isNotEmpty) { 1460 if (enclosingFinallies.isNotEmpty) {
1726 // [enclosingFinallies] can be empty if there is no surrounding finally 1461 // [enclosingFinallies] can be empty if there is no surrounding finally
1727 // blocks. Then [nextLabel] will be [rethrowLabel]. 1462 // blocks. Then [nextLabel] will be [rethrowLabel].
1728 addStatement( 1463 addStatement(
1729 js.js.statement("# = #;", [nextName, new js.ArrayInitializer( 1464 js.js.statement("# = #;", [next, new js.ArrayInitializer(
1730 enclosingFinallies.map(js.number).toList())])); 1465 enclosingFinallies.map(js.number).toList())]));
1731 } 1466 }
1732 if (node.finallyPart == null) { 1467 if (node.finallyPart == null) {
1733 // The finally-block belonging to [node] will be visited because of 1468 // The finally-block belonging to [node] will be visited because of
1734 // fallthrough. If it does not exist, add an explicit goto. 1469 // fallthrough. If it does not exist, add an explicit goto.
1735 addGoto(nextLabel); 1470 addGoto(nextLabel);
1736 } 1471 }
1737 if (node.finallyPart != null) { 1472 if (node.finallyPart != null) {
1738 js.Node last = jumpTargets.removeLast(); 1473 js.Node last = jumpTargets.removeLast();
1739 assert(last == node.finallyPart); 1474 assert(last == node.finallyPart);
1740 1475
1741 beginLabel(finallyLabel); 1476 beginLabel(finallyLabel);
1742 setErrorHandler(); 1477 setErrorHandler();
1743 visitStatement(node.finallyPart); 1478 visitStatement(node.finallyPart);
1744 addStatement(new js.Comment("// goto the next finally handler")); 1479 addStatement(new js.Comment("// goto the next finally handler"));
1745 addStatement(js.js.statement("# = #.pop();", [gotoName, nextName])); 1480 addStatement(js.js.statement("# = #.pop();", [goto, next]));
1746 addBreak(); 1481 addBreak();
1747 } 1482 }
1748 beginLabel(afterFinallyLabel); 1483 beginLabel(afterFinallyLabel);
1749 } 1484 }
1750 1485
1751 @override 1486 @override
1752 visitVariableDeclaration(js.VariableDeclaration node) { 1487 visitVariableDeclaration(js.VariableDeclaration node) {
1753 unreachable(node); 1488 unreachable(node);
1754 } 1489 }
1755 1490
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1807 new js.Prefix("!", condition), gotoAndBreak(afterLabel))); 1542 new js.Prefix("!", condition), gotoAndBreak(afterLabel)));
1808 }, store: false); 1543 }, store: false);
1809 } 1544 }
1810 jumpTargets.add(node); 1545 jumpTargets.add(node);
1811 visitStatement(node.body); 1546 visitStatement(node.body);
1812 jumpTargets.removeLast(); 1547 jumpTargets.removeLast();
1813 addGoto(continueLabel); 1548 addGoto(continueLabel);
1814 beginLabel(afterLabel); 1549 beginLabel(afterLabel);
1815 } 1550 }
1816 1551
1552 addYield(js.DartYield node, js.Expression expression);
1553
1554 @override
1555 void visitDartYield(js.DartYield node) {
floitsch 2015/02/25 16:27:59 Ideally this one, too, would only be shared by the
sigurdm 2015/02/27 09:46:33 Left it for now
1556 print(this.runtimeType);
floitsch 2015/02/25 16:27:59 debug print.
sigurdm 2015/02/27 09:46:33 Done.
1557 assert(isSyncStar || isAsyncStar);
1558 int label = newLabel("after yield");
1559 // Don't do a break here for the goto, but instead a return in either
1560 // addSynYield or addAsyncYield.
1561 withExpression(node.expression, (js.Expression expression) {
1562 addStatement(setGotoVariable(label));
1563 addYield(node, expression);
1564 }, store: false);
1565 beginLabel(label);
1566 }
1567 }
1568
1569 js.VariableInitialization
1570 _makeVariableInitializer(dynamic variable, js.Expression initValue) {
1571 js.VariableDeclaration declaration;
1572 if (variable is js.VariableUse) {
1573 declaration = new js.VariableDeclaration(variable.name);
1574 } else if (variable is String) {
1575 declaration = new js.VariableDeclaration(variable);
1576 } else {
1577 assert(variable is js.VariableDeclaration);
1578 declaration = variable;
1579 }
1580 return new js.VariableInitialization(declaration, initValue);
1581 }
1582
1583 class AsyncRewriter extends AsyncRewriterBase {
1584
1585 bool get isAsync => true;
1586
1587 /// The Completer that will finish an async function.
1588 ///
1589 /// Not used for sync* or async* functions.
1590 String completerName;
1591 js.VariableUse get completer => new js.VariableUse(completerName);
1592
1593 /// The function called by an async function to simulate an await or return.
1594 ///
1595 /// For an await it is called with:
1596 ///
1597 /// - The value to await
1598 /// - The body function [bodyName]
1599 /// - The completer object [completer]
1600 ///
1601 /// For a return it is called with:
1602 ///
1603 /// - The value to complete the completer with.
1604 /// - [error_codes.SUCCESS]
1605 /// - The completer object [completer]
1606 ///
1607 /// For a throw it is called with:
1608 ///
1609 /// - The error to complete the completer with.
1610 /// - [error_codes.ERROR]
1611 /// - The completer object [completer]
1612 final js.Expression asyncHelper;
1613
1614 /// Contructor used to initialize the [completer] variable.
1615 ///
1616 /// Specific to async methods.
1617 final js.Expression newCompleter;
1618
1619
1620 AsyncRewriter(DiagnosticListener diagnosticListener,
1621 spannable,
1622 {this.asyncHelper,
1623 this.newCompleter,
1624 safeVariableName})
1625 : super(diagnosticListener,
1626 spannable,
1627 safeVariableName);
1628
1629 @override
1630 void addYield(js.DartYield node, js.Expression expression) {
1631 diagnosticListener.internalError(spannable,
1632 "Yield in non-generating async function");
1633 }
1634
1635 void addErrorExit() {
1636 beginLabel(rethrowLabel);
1637 addStatement(js.js.statement(
1638 "return #thenHelper(#currentError, #errorCode, #completer);", {
1639 "thenHelper": asyncHelper,
1640 "errorCode": js.number(error_codes.ERROR),
1641 "currentError": currentError,
1642 "completer": completer}));
1643 }
1644
1645 /// Returning from an async method calls the [asyncStarHelper] with the result .
floitsch 2015/02/25 16:27:59 long line.
sigurdm 2015/02/27 09:46:33 Done.
1646 /// (the result might have been stored in [returnValue] by some finally
1647 /// block).
1648 void addSuccesExit() {
1649 if (analysis.hasExplicitReturns) {
1650 beginLabel(exitLabel);
1651 } else {
1652 addStatement(new js.Comment("implicit return"));
1653 }
1654 addStatement(js.js.statement(
1655 "return #runtimeHelper(#returnValue, #successCode, "
1656 "#completer, null);", {
1657 "runtimeHelper": asyncHelper,
1658 "successCode": js.number(error_codes.SUCCESS),
1659 "returnValue": analysis.hasExplicitReturns
1660 ? returnValue
1661 : new js.LiteralNull(),
1662 "completer": completer}));
1663 }
1664
1665 @override
1666 Iterable<js.VariableInitialization> variableInitializations() {
1667 List<js.VariableInitialization> variables =
1668 new List<js.VariableInitialization>();
1669 variables.add(_makeVariableInitializer(completer,
1670 new js.New(newCompleter, [])));
1671 if (analysis.hasExplicitReturns) {
1672 variables.add(_makeVariableInitializer(returnValue, null));
1673 }
1674 return variables;
1675 }
1676
1677 @override
1678 void initializeNames() {
1679 completerName = freshName("completer");
1680 }
1681
1682 @override
1683 js.Statement awaitStatement(js.Expression value) {
1684 return js.js.statement("""
1685 return #asyncHelper(#value,
1686 #body,
1687 #completer);
1688 """, {
1689 "asyncHelper": asyncHelper,
1690 "value": value,
1691 "body": body,
1692 "completer": completer});
1693 }
1694
1695 @override
1696 js.Fun generateMainFunction(List<js.Parameter> parameters,
1697 js.Statement rewrittenBody,
floitsch 2015/02/25 16:27:59 indentation.
sigurdm 2015/02/27 09:46:33 Done.
1698 js.VariableDeclarationList variableDeclarations) {
1699 return js.js("""
1700 function (#parameters) {
1701 #variableDeclarations;
1702 function #bodyName(#errorCode, #result) {
1703 if (#errorCode === #ERROR) {
1704 #currentError = #result;
1705 #goto = #handler;
1706 }
1707 while (true)
floitsch 2015/02/25 16:27:59 In the next? CL please move the "while" to the rew
sigurdm 2015/02/27 09:46:33 Did it here.
1708 #rewrittenBody;
1709 }
1710 return #asyncHelper(null, #bodyName, #completer, null);
1711 }""", {
1712 "parameters": parameters,
1713 "variableDeclarations": variableDeclarations,
1714 "ERROR": js.number(error_codes.ERROR),
1715 "rewrittenBody": rewrittenBody,
1716 "bodyName": bodyName,
1717 "currentError": currentError,
1718 "goto": goto,
1719 "handler": handler,
1720 "errorCode": errorCodeName,
1721 "result": resultName,
1722 "asyncHelper": asyncHelper,
1723 "completer": completer,
1724 });
1725 }
1726 }
1727
1728 class SyncStarRewriter extends AsyncRewriterBase {
1729
1730 bool get isSyncStar => true;
1731
1732 /// Contructor creating the Iterable for a sync* method. Called with
1733 /// [bodyName].
1734 final js.Expression newIterable;
1735
1736 /// A JS Expression that creates a marker showing that iteration is over.
1737 ///
1738 /// Called without arguments.
1739 final js.Expression endOfIteration;
1740
1741 /// A JS Expression that creates a marker indication a 'yield*' statement.
1742 ///
1743 /// Called with the stream to yield from.
1744 final js.Expression yieldStarExpression;
1745
1746 /// Used by sync* functions to throw exeptions.
1747 final js.Expression uncaughtErrorExpression;
1748
1749 SyncStarRewriter(DiagnosticListener diagnosticListener,
1750 spannable,
1751 {this.endOfIteration,
1752 this.newIterable,
1753 this.yieldStarExpression,
1754 this.uncaughtErrorExpression,
1755 safeVariableName})
1756 : super(diagnosticListener,
1757 spannable,
1758 safeVariableName);
1759
1817 /// Translates a yield/yield* in an sync*. 1760 /// Translates a yield/yield* in an sync*.
1818 /// 1761 ///
1819 /// `yield` in a sync* function just returns [value]. 1762 /// `yield` in a sync* function just returns [value].
1820 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it. 1763 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it.
1821 void addSyncYield(js.DartYield node, js.Expression expression) { 1764 @override
1822 assert(isSyncStar); 1765 void addYield(js.DartYield node, js.Expression expression) {
1823 if (node.hasStar) { 1766 if (node.hasStar) {
1824 addStatement( 1767 addStatement(
1825 new js.Return(new js.Call(yieldStarExpression, [expression]))); 1768 new js.Return(new js.Call(yieldStarExpression, [expression])));
1826 } else { 1769 } else {
1827 addStatement(new js.Return(expression)); 1770 addStatement(new js.Return(expression));
1828 } 1771 }
1829 } 1772 }
1830 1773
1774 @override
1775 js.Fun generateMainFunction(List<js.Parameter> params,
1776 js.Statement rewrittenBody,
1777 js.VariableDeclarationList variableDeclarations) {
1778 return js.js("""
1779 function (#params) {
1780 if (#needsThis)
1781 var #self = this;
1782 return new #newIterable(function () {
1783 #varDecl;
1784 return function #body(#errorCode, #result) {
1785 if (#errorCode === #ERROR) {
1786 #currentError = #result;
1787 #goto = #handler;
1788 }
1789 while (true)
1790 #helperBody;
1791 };
1792 });
1793 }
1794 """, {
1795 "params": params,
1796 "needsThis": analysis.hasThis,
1797 "helperBody": rewrittenBody,
1798 "varDecl": variableDeclarations,
1799 "errorCode": errorCodeName,
1800 "newIterable": newIterable,
1801 "body": bodyName,
1802 "self": selfName,
1803 "result": resultName,
1804 "goto": goto,
1805 "handler": handler,
1806 "currentError": currentErrorName,
1807 "ERROR": js.number(error_codes.ERROR),
1808 });
1809 }
1810
1811 void addErrorExit() {
1812 beginLabel(rethrowLabel);
1813 addStatement(js.js.statement('return #(#);',
1814 [uncaughtErrorExpression, currentError]));
1815 }
1816
1817 /// Returning from a sync* function returns an [endOfIteration] marker.
1818 void addSuccesExit() {
1819 if (analysis.hasExplicitReturns) {
1820 beginLabel(exitLabel);
1821 } else {
1822 addStatement(new js.Comment("implicit return"));
1823 }
1824 addStatement(js.js.statement('return #();', [endOfIteration]));
1825 }
1826
1827 @override
1828 Iterable<js.VariableInitialization> variableInitializations() {
1829 List<js.VariableInitialization> variables =
1830 new List<js.VariableInitialization>();
1831 return variables;
1832 }
1833
1834 @override
1835 js.Statement awaitStatement(js.Expression value) {
1836 throw diagnosticListener.internalError(spannable,
1837 "Sync* functions cannot contain await statements.");
1838 }
1839
1840 @override
1841 void initializeNames() {}
1842 }
1843
1844 class AsyncStarRewriter extends AsyncRewriterBase {
1845
1846 bool get isAsyncStar => true;
1847
1848 /// The stack of labels of finally blocks to assign to [next] if the
1849 /// async* [StreamSubscription] was canceled during a yield.
1850 js.VariableUse get nextWhenCanceled {
1851 return new js.VariableUse(nextWhenCanceledName);
1852 }
1853 String nextWhenCanceledName;
1854
1855 /// The StreamController that controls an async* function.
1856 String controllerName;
1857 js.VariableUse get controller => new js.VariableUse(controllerName);
1858
1859 /// The function called by an async* function to simulate an await, yield or
1860 /// yield*.
1861 ///
1862 /// For an await/yield/yield* it is called with:
1863 ///
1864 /// - The value to await/yieldExpression(value to yield)/
1865 /// yieldStarExpression(stream to yield)
1866 /// - The body function [bodyName]
1867 /// - The controller object [controllerName]
1868 ///
1869 /// For a return it is called with:
1870 ///
1871 /// - null
1872 /// - null
1873 /// - The [controllerName]
1874 /// - null.
1875 final js.Expression asyncStarHelper;
1876
1877 /// Contructor used to initialize the [controllerName] variable.
1878 ///
1879 /// Specific to async* methods.
1880 final js.Expression newController;
1881
1882 /// Used to get the `Stream` out of the [controllerName] variable.
1883 final js.Expression streamOfController;
1884
1885 /// A JS Expression that creates a marker indicating a 'yield' statement.
1886 ///
1887 /// Called with the value to yield.
1888 final js.Expression yieldExpression;
1889
1890 /// A JS Expression that creates a marker indication a 'yield*' statement.
1891 ///
1892 /// Called with the stream to yield from.
1893 final js.Expression yieldStarExpression;
1894
1895 AsyncStarRewriter(DiagnosticListener diagnosticListener,
1896 spannable,
1897 {this.asyncStarHelper,
1898 this.streamOfController,
1899 this.newController,
1900 this.yieldExpression,
1901 this.yieldStarExpression,
1902 String safeVariableName(String original)})
1903 : super(diagnosticListener,
1904 spannable,
1905 safeVariableName);
1906
1907
1831 /// Translates a yield/yield* in an async* function. 1908 /// Translates a yield/yield* in an async* function.
1832 /// 1909 ///
1833 /// yield/yield* in an async* function is translated much like the `await` is 1910 /// yield/yield* in an async* function is translated much like the `await` is
1834 /// translated in [visitAwait], only the object is wrapped in a 1911 /// translated in [visitAwait], only the object is wrapped in a
1835 /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper] 1912 /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper]
1836 /// distinguish them. 1913 /// distinguish them.
1837 /// Also [nextWhenCanceledName] is set up to contain the finally blocks that 1914 /// Also [nextWhenCanceled] is set up to contain the finally blocks that
1838 /// must be run in case the stream was canceled. 1915 /// must be run in case the stream was canceled.
1839 void addAsyncYield(js.DartYield node, js.Expression expression) { 1916 @override
1840 assert(isAsyncStar); 1917 void addYield(js.DartYield node, js.Expression expression) {
1841 // Find all the finally blocks that should be performed if the stream is 1918 // Find all the finally blocks that should be performed if the stream is
1842 // canceled during the yield. 1919 // canceled during the yield.
1843 // At the bottom of the stack is the return label. 1920 // At the bottom of the stack is the return label.
1844 List<int> enclosingFinallyLabels = <int>[exitLabel]; 1921 List<int> enclosingFinallyLabels = <int>[exitLabel];
1845 enclosingFinallyLabels.addAll(jumpTargets 1922 enclosingFinallyLabels.addAll(jumpTargets
1846 .where((js.Node node) => finallyLabels[node] != null) 1923 .where((js.Node node) => finallyLabels[node] != null)
1847 .map((js.Block node) => finallyLabels[node])); 1924 .map((js.Block node) => finallyLabels[node]));
1848 addStatement(js.js.statement("# = #;", 1925 addStatement(js.js.statement("# = #;",
1849 [nextWhenCanceledName, new js.ArrayInitializer( 1926 [nextWhenCanceled, new js.ArrayInitializer(
1850 enclosingFinallyLabels.map(js.number).toList())])); 1927 enclosingFinallyLabels.map(js.number).toList())]));
1851 addStatement(js.js.statement(""" 1928 addStatement(js.js.statement("""
1852 return #streamHelper(#yieldExpression(#expression), #body, 1929 return #asyncStarHelper(#yieldExpression(#expression), #body,
1853 #controller);""", { 1930 #controller);""", {
1854 "streamHelper": streamHelper, 1931 "asyncStarHelper": asyncStarHelper,
1855 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression, 1932 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression,
1856 "expression": expression, 1933 "expression": expression,
1857 "body": bodyName, 1934 "body": body,
1858 "controller": controllerName, 1935 "controller": controllerName,
1859 })); 1936 }));
1860 } 1937 }
1861 1938
1862 @override 1939 @override
1863 void visitDartYield(js.DartYield node) { 1940 js.Fun generateMainFunction(List<js.Parameter> parameters,
1864 assert(isSyncStar || isAsyncStar); 1941 js.Statement rewrittenBody,
1865 int label = newLabel("after yield"); 1942 js.VariableDeclarationList variableDeclarations) {
1866 // Don't do a break here for the goto, but instead a return in either 1943 return js.js("""
1867 // addSynYield or addAsyncYield. 1944 function (#parameters) {
1868 withExpression(node.expression, (js.Expression expression) { 1945 #variableDeclarations;
1869 addStatement(setGotoVariable(label)); 1946 function #bodyName(#errorCode, #result) {
1870 if (isSyncStar) { 1947 if (#hasYield)
1871 addSyncYield(node, expression); 1948 switch (#errorCode) {
1872 } else { 1949 case #STREAM_WAS_CANCELED:
1873 addAsyncYield(node, expression); 1950 #next = #nextWhenCanceled;
1874 } 1951 #goto = #next.pop();
1875 }, store: false); 1952 break;
1876 beginLabel(label); 1953 case #ERROR:
1954 #currentError = #result;
1955 #goto = #handler;
1956 }
1957 else
1958 if (#errorCode === #ERROR) {
1959 #currentError = #result;
1960 #goto = #handler;
1961 }
1962 while (true)
1963 #rewrittenBody;
1964 }
1965 return #streamOfController(#controller);
1966 }""", {
1967 "parameters": parameters,
1968 "variableDeclarations": variableDeclarations,
1969 "STREAM_WAS_CANCELED": js.number(error_codes.STREAM_WAS_CANCELED),
1970 "ERROR": js.number(error_codes.ERROR),
1971 "hasYield": analysis.hasYield,
1972 "rewrittenBody": rewrittenBody,
1973 "bodyName": bodyName,
1974 "currentError": currentError,
1975 "goto": goto,
1976 "handler": handler,
1977 "next": next,
1978 "nextWhenCanceled": nextWhenCanceled,
1979 "errorCode": errorCodeName,
1980 "result": resultName,
1981 "streamOfController": streamOfController,
1982 "controller": controllerName,
1983 });
1984 }
1985
1986 @override
1987 void addErrorExit() {
1988 beginLabel(rethrowLabel);
1989 addStatement(js.js.statement(
1990 "return #asyncHelper(#currentError, #errorCode, #controller);", {
1991 "asyncHelper": asyncStarHelper,
1992 "errorCode": js.number(error_codes.ERROR),
1993 "currentError": currentError,
1994 "controller": controllerName}));
1995 }
1996
1997 /// Returning from an async* function calls the [streamHelper] with an
1998 /// [endOfIteration] marker.
1999 @override
2000 void addSuccesExit() {
2001 beginLabel(exitLabel);
2002
2003 addStatement(js.js.statement(
2004 "return #streamHelper(null, #successCode, #controller);", {
2005 "streamHelper": asyncStarHelper,
2006 "successCode": js.number(error_codes.SUCCESS),
2007 "controller": controllerName}));
2008 }
2009
2010 @override
2011 Iterable<js.VariableInitialization> variableInitializations() {
2012 List<js.VariableInitialization> variables =
2013 new List<js.VariableInitialization>();
2014 variables.add(_makeVariableInitializer(controller,
2015 js.js('#(#)', [newController, bodyName])));
2016 if (analysis.hasYield) {
2017 variables.add(_makeVariableInitializer(nextWhenCanceled, null));
2018 }
2019 return variables;
2020 }
2021
2022 @override
2023 void initializeNames() {
2024 controllerName = freshName("controller");
2025 nextWhenCanceledName = freshName("nextWhenCanceled");
2026 }
2027
2028 @override
2029 js.Statement awaitStatement(js.Expression value) {
2030 return js.js.statement("""
2031 return #asyncHelper(#value,
2032 #body,
2033 #controller);
2034 """, {
2035 "asyncHelper": asyncStarHelper,
2036 "value": value,
2037 "body": body,
2038 "controller": controllerName});
1877 } 2039 }
1878 } 2040 }
1879 2041
1880 /// Finds out 2042 /// Finds out
1881 /// 2043 ///
1882 /// - which expressions have yield or await nested in them. 2044 /// - which expressions have yield or await nested in them.
1883 /// - targets of jumps 2045 /// - targets of jumps
1884 /// - a set of used names. 2046 /// - a set of used names.
1885 /// - if any [This]-expressions are used. 2047 /// - if any [This]-expressions are used.
1886 class PreTranslationAnalysis extends js.NodeVisitor<bool> { 2048 class PreTranslationAnalysis extends js.NodeVisitor<bool> {
(...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after
2294 return condition || body; 2456 return condition || body;
2295 } 2457 }
2296 2458
2297 @override 2459 @override
2298 bool visitDartYield(js.DartYield node) { 2460 bool visitDartYield(js.DartYield node) {
2299 hasYield = true; 2461 hasYield = true;
2300 visit(node.expression); 2462 visit(node.expression);
2301 return true; 2463 return true;
2302 } 2464 }
2303 } 2465 }
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