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

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

Issue 925973002: Fix error handling in dart2js async-await (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed comments. Wrote more tests. Fixed a number of bugs 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/js_backend/backend.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 // TODO(sigurdm): Throws in catch-handlers are handled wrong.
8 // TODO(sigurdm): Avoid using variables in templates. It could blow up memory 7 // TODO(sigurdm): Avoid using variables in templates. It could blow up memory
9 // use. 8 // use.
10 9
11 import "dart:math" show max; 10 import "dart:math" show max;
12 import 'dart:collection'; 11 import 'dart:collection';
13 12
13 import 'package:_internal/compiler/js_lib/shared/async_await_error_codes.dart'
14 as error_codes;
15
14 import "js.dart" as js; 16 import "js.dart" as js;
15 17
16 import '../util/util.dart'; 18 import '../util/util.dart';
17 import '../dart2jslib.dart' show DiagnosticListener; 19 import '../dart2jslib.dart' show DiagnosticListener;
18 20
19 import "../helpers/helpers.dart"; 21 import "../helpers/helpers.dart";
20 22
21 /// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield 23 /// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield
22 /// (with dart-like semantics) to an equivalent function without these. 24 /// (with dart-like semantics) to an equivalent function without these.
23 /// await-for is not handled and must be rewritten before. (Currently handled 25 /// await-for is not handled and must be rewritten before. (Currently handled
24 /// in ssa/builder.dart). 26 /// in ssa/builder.dart).
25 /// 27 ///
26 /// When generating the input to this, special care must be taken that 28 /// When generating the input to this, special care must be taken that
27 /// parameters to sync* functions that are mutated in the body must be boxed. 29 /// parameters to sync* functions that are mutated in the body must be boxed.
28 /// (Currently handled in closure.dart). 30 /// (Currently handled in closure.dart).
29 /// 31 ///
30 /// Look at [visitFun], [visitDartYield] and [visitAwait] for more explanation. 32 /// Look at [visitFun], [visitDartYield] and [visitAwait] for more explanation.
31 class AsyncRewriter extends js.NodeVisitor { 33 class AsyncRewriter extends js.NodeVisitor {
32 34
33 // Local variables are hoisted to the top of the function, so they are 35 // Local variables are hoisted to the top of the function, so they are
34 // collected here. 36 // collected here.
35 List<js.VariableDeclaration> localVariables = 37 List<js.VariableDeclaration> localVariables =
36 new List<js.VariableDeclaration>(); 38 new List<js.VariableDeclaration>();
37 39
38 Map<js.Node, int> continueLabels = new Map<js.Node, int>(); 40 Map<js.Node, int> continueLabels = new Map<js.Node, int>();
39 Map<js.Node, int> breakLabels = new Map<js.Node, int>(); 41 Map<js.Node, int> breakLabels = new Map<js.Node, int>();
40 Map<js.Node, int> finallyLabels = new Map<js.Node, int>(); 42
43 /// The label of a finally part.
44 Map<js.Block, int> finallyLabels = new Map<js.Block, int>();
45
46 /// The label of the catch handler of a [js.Try] or a [js.Fun] or [js.Catch].
floitsch 2015/02/16 18:26:36 Not sure I understand the comment. is it ([js.Try]
sigurdm 2015/02/17 10:29:05 I try to explain more in depth.
47 /// The catch handler of a catch block will redirect to the enclosing handler
48 /// after running all the finally blocks inbetween.
49 Map<js.Node, int> handlerLabels = new Map<js.Node, int>();
50
41 int exitLabel; 51 int exitLabel;
52 int rethrowLabel;
42 53
43 // A stack of all enclosing jump targets (including the function for 54 /// A stack of all enclosing jump targets, pending finally blocks and
44 // representing the target of a return, and all enclosing try-blocks that have 55 /// catch handlers.
45 // finally part, this way ensuring all the finally blocks between a jump and 56 ///
46 // its target are run before the jump. 57 /// The jump-targets include the function for
floitsch 2015/02/16 18:26:36 the function, representing the target of a return.
sigurdm 2015/02/17 10:29:06 Done.
47 List<js.Node> targetsAndTries = new List<js.Node>(); 58 /// representing the target of a return,
59 ///
60 /// The enclosing catch-handlers include the enclosing function for rethrowing
61 /// an uncaught error.
floitsch 2015/02/16 18:26:36 That sounds like the function is used either for t
sigurdm 2015/02/17 10:29:06 Yes, it is used for both.
62 ///
63 /// This is used to ensure all the finally blocks between a jump and
64 /// its target are run before the jump.
65 List<js.Node> targetsCatchesAndFinallies = new List<js.Node>();
floitsch 2015/02/16 18:26:36 /// A stack of all (surrounding) jump targets. ///
sigurdm 2015/02/17 10:29:06 This is not very precise. But might be easier to u
48 66
49 List<int> continueStack = new List<int>(); 67 List<int> continueStack = new List<int>();
50 List<int> breakStack = new List<int>(); 68 List<int> breakStack = new List<int>();
51 List<int> returnStack = new List<int>(); 69 List<int> returnStack = new List<int>();
52 70
53 List<Pair<String, String>> variableRenamings = 71 List<Pair<String, String>> variableRenamings =
54 new List<Pair<String, String>>(); 72 new List<Pair<String, String>>();
55 73
56 PreTranslationAnalysis analysis; 74 PreTranslationAnalysis analysis;
57 75
58 List<int> errorHandlerLabels = new List<int>();
59
60 final Function safeVariableName; 76 final Function safeVariableName;
61 77
62 // All the <x>Name variables are names of Javascript variables used in the 78 // All the <x>Name variables are names of Javascript variables used in the
63 // transformed code. 79 // transformed code.
64 80
65 /// Contains the result of an awaited expression, or a conditional or 81 /// Contains the result of an awaited expression, or a conditional or
66 /// lazy boolean operator. 82 /// lazy boolean operator.
67 /// 83 ///
68 /// For example a conditional expression is roughly translated like: 84 /// For example a conditional expression is roughly translated like:
69 /// [[cond ? a : b]] 85 /// [[cond ? a : b]]
70 /// 86 ///
71 /// Becomes: 87 /// Becomes:
72 /// 88 ///
73 /// while true { // outer while loop 89 /// while true { // outer while loop
74 /// switch (goto) { // Simulates goto 90 /// switch (goto) { // Simulates goto
75 /// ... 91 /// ...
76 /// goto = [[cond]] ? thenLabel : elseLabel 92 /// goto = [[cond]] ? thenLabel : elseLabel
77 /// break; 93 /// break;
78 /// case thenLabel: 94 /// case thenLabel:
79 /// result = [[a]]; 95 /// result = [[a]];
80 /// goto = joinLabel; 96 /// goto = joinLabel;
81 /// case elseLabel: 97 /// case elseLabel:
82 /// result = [[b]]; 98 /// result = [[b]];
83 /// case joinLabel: 99 /// case joinLabel:
84 /// // Now the result of computing the condition is in result. 100 /// // Now the result of computing the condition is in result.
85 /// .... 101 /// ....
86 /// } 102 /// }
87 /// } 103 /// }
88 /// 104 ///
89 /// It is a parameter to the [helperName] function, so that [thenHelper] and 105 /// It is a parameter to the [bodyName] function, so that [asyncHelper] and
90 /// [streamHelper] can call [helperName] with the result of an awaited Future. 106 /// [streamHelper] can call [bodyName] with the result of an awaited Future.
91 String resultName; 107 String resultName;
92 108
109 /// A parameter to the [bodyName] function. Indicating if we are in success
110 /// or error case.
111 String errorCodeName;
112
93 /// The name of the inner function that is scheduled to do each await/yield, 113 /// The name of the inner function that is scheduled to do each await/yield,
94 /// and called to do a new iteration for sync*. 114 /// and called to do a new iteration for sync*.
95 String helperName; 115 String bodyName;
96 116
97 /// The Completer that will finish an async function. 117 /// The Completer that will finish an async function.
98 /// 118 ///
99 /// Not used for sync* or async* functions. 119 /// Not used for sync* or async* functions.
100 String completerName; 120 String completerName;
101 121
102 /// The StreamController that controls an async* function. 122 /// The StreamController that controls an async* function.
103 /// 123 ///
104 /// Not used for async and sync* functions 124 /// Not used for async and sync* functions
105 String controllerName; 125 String controllerName;
106 126
107 /// Used to simulate a goto. 127 /// Used to simulate a goto.
108 /// 128 ///
109 /// To "goto" a label, the label is assigned to this 129 /// To "goto" a label, the label is assigned to this
110 /// variable, and break out of the switch to take another iteration in the 130 /// variable, and break out of the switch to take another iteration in the
111 /// while loop. See [addGoto] 131 /// while loop. See [addGoto]
112 String gotoName; 132 String gotoName;
113 133
114 /// The label of the current error handler. 134 /// The label of the current error handler.
115 String handlerName; 135 String handlerName;
116 136
117 /// Current caught error. 137 /// Current caught error.
118 String errorName; 138 String errorName;
119 139
120 /// A stack of labels of finally blocks to visit, and the label to go to after 140 /// A stack of labels of finally blocks to visit, and the label to go to after
121 /// the last. 141 /// the last.
122 String nextName; 142 String nextName;
123 143
144 /// The stack of labels of finally blocks to assign to [nextName] if the
145 /// async* [StreamSubscription] was canceled during a yield.
146 String nextWhenCanceledName;
147
124 /// The current returned value (a finally block may overwrite it). 148 /// The current returned value (a finally block may overwrite it).
125 String returnValueName; 149 String returnValueName;
126 150
151 /// If we are in the process of handling an error, stores the current error.
152 String currentErrorName;
153
127 /// The label of the outer loop. 154 /// The label of the outer loop.
128 /// 155 ///
129 /// Used if there are untransformed loops containing break or continues to 156 /// Used if there are untransformed loops containing break or continues to
130 /// targets outside the loop. 157 /// targets outside the loop.
131 String outerLabelName; 158 String outerLabelName;
132 159
133 /// If javascript `this` is used, it is accessed via this variable, in the 160 /// If javascript `this` is used, it is accessed via this variable, in the
134 /// [helperName] function. 161 /// [bodyName] function.
135 String selfName; 162 String selfName;
136 163
137 // These expressions are hooks for communicating with the runtime. 164 // These expressions are hooks for communicating with the runtime.
138 165
139 /// The function called by an async function to simulate an await or return. 166 /// The function called by an async function to simulate an await or return.
140 /// 167 ///
141 /// For an await it is called with: 168 /// For an await it is called with:
142 /// 169 ///
143 /// - The value to await 170 /// - The value to await
144 /// - The [helperName] 171 /// - The body function [bodyName]
145 /// - The [completerName] 172 /// - The completer object [completerName]
146 /// - A JavaScript function that is executed if the future completed with
147 /// an error. That function is responsible for executing the right error
148 /// handler and/or finally blocks).
149 /// 173 ///
150 /// For a return it is called with: 174 /// For a return it is called with:
151 /// 175 ///
152 /// - The value to complete the completer with. 176 /// - The value to complete the completer with.
153 /// - null 177 /// - 0
154 /// - The [completerName] 178 /// - The [completerName]
floitsch 2015/02/16 18:26:36 nit: the completer object [completerName]
sigurdm 2015/02/17 10:29:05 Done.
155 /// - null. 179 ///
156 final js.Expression thenHelper; 180 /// For a throw it is called with:
181 ///
182 /// - The value to complete the completer with.
183 /// - 1
184 /// - The [completerName]
floitsch 2015/02/16 18:26:36 ditto.
sigurdm 2015/02/17 10:29:06 Done.
185 final js.Expression asyncHelper;
157 186
158 /// The function called by an async* function to simulate an await, yield or 187 /// The function called by an async* function to simulate an await, yield or
159 /// yield*. 188 /// yield*.
160 /// 189 ///
161 /// For an await/yield/yield* it is called with: 190 /// For an await/yield/yield* it is called with:
162 /// 191 ///
163 /// - The value to await/yieldExpression(value to yield)/ 192 /// - The value to await/yieldExpression(value to yield)/
164 /// yieldStarExpression(stream to yield) 193 /// yieldStarExpression(stream to yield)
165 /// - The [helperName] 194 /// - The body function [bodyName]
166 /// - The [controllerName] 195 /// - The controller object [controllerName]
167 /// - A JavaScript function that is executed if the future completed with
168 /// an error. That function is responsible for executing the right error
169 /// handler and/or finally blocks).
170 /// 196 ///
171 /// For a return it is called with: 197 /// For a return it is called with:
172 /// 198 ///
173 /// - null 199 /// - null
174 /// - null 200 /// - null
175 /// - The [controllerName] 201 /// - The [controllerName]
176 /// - null. 202 /// - null.
177 final js.Expression streamHelper; 203 final js.Expression streamHelper;
178 204
179 /// Contructor used to initialize the [completerName] variable. 205 /// Contructor used to initialize the [completerName] variable.
180 /// 206 ///
181 /// Specific to async methods. 207 /// Specific to async methods.
182 final js.Expression newCompleter; 208 final js.Expression newCompleter;
183 209
184 /// Contructor used to initialize the [controllerName] variable. 210 /// Contructor used to initialize the [controllerName] variable.
185 /// 211 ///
186 /// Specific to async* methods. 212 /// Specific to async* methods.
187 final js.Expression newController; 213 final js.Expression newController;
188 214
189 /// Used to get the `Stream` out of the [controllerName] variable. 215 /// Used to get the `Stream` out of the [controllerName] variable.
190 /// 216 ///
191 /// Specific to async* methods. 217 /// Specific to async* methods.
192 final js.Expression streamOfController; 218 final js.Expression streamOfController;
193 219
194 /// Contructor creating the Iterable for a sync* method. Called with 220 /// Contructor creating the Iterable for a sync* method. Called with
195 /// [helperName]. 221 /// [bodyName].
196 final js.Expression newIterable; 222 final js.Expression newIterable;
197 223
198 /// A JS Expression that creates a marker showing that iteration is over. 224 /// A JS Expression that creates a marker showing that iteration is over.
199 /// 225 ///
200 /// Called without arguments. 226 /// Called without arguments.
201 final js.Expression endOfIteration; 227 final js.Expression endOfIteration;
202 228
203 /// A JS Expression that creates a marker indicating a 'yield' statement. 229 /// A JS Expression that creates a marker indicating a 'yield' statement.
204 /// 230 ///
205 /// Called with the value to yield. 231 /// Called with the value to yield.
(...skipping 22 matching lines...) Expand all
228 254
229 js.AsyncModifier async; 255 js.AsyncModifier async;
230 256
231 bool get isSync => async == const js.AsyncModifier.sync(); 257 bool get isSync => async == const js.AsyncModifier.sync();
232 bool get isAsync => async == const js.AsyncModifier.async(); 258 bool get isAsync => async == const js.AsyncModifier.async();
233 bool get isSyncStar => async == const js.AsyncModifier.syncStar(); 259 bool get isSyncStar => async == const js.AsyncModifier.syncStar();
234 bool get isAsyncStar => async == const js.AsyncModifier.asyncStar(); 260 bool get isAsyncStar => async == const js.AsyncModifier.asyncStar();
235 261
236 AsyncRewriter(this.diagnosticListener, 262 AsyncRewriter(this.diagnosticListener,
237 spannable, 263 spannable,
238 {this.thenHelper, 264 {this.asyncHelper,
239 this.streamHelper, 265 this.streamHelper,
240 this.streamOfController, 266 this.streamOfController,
241 this.newCompleter, 267 this.newCompleter,
242 this.newController, 268 this.newController,
243 this.endOfIteration, 269 this.endOfIteration,
244 this.newIterable, 270 this.newIterable,
245 this.yieldExpression, 271 this.yieldExpression,
246 this.yieldStarExpression, 272 this.yieldStarExpression,
247 this.safeVariableName}) 273 this.safeVariableName})
248 : _spannable = spannable; 274 : _spannable = spannable;
249 275
250 /// Main entry point. 276 /// Main entry point.
251 /// Rewrites a sync*/async/async* function to an equivalent normal function. 277 /// Rewrites a sync*/async/async* function to an equivalent normal function.
252 /// 278 ///
253 /// [spannable] can be passed to have a location for error messages. 279 /// [spannable] can be passed to have a location for error messages.
254 js.Fun rewrite(js.Fun node, [Spannable spannable]) { 280 js.Fun rewrite(js.Fun node, [Spannable spannable]) {
255 _spannable = spannable; 281 _spannable = spannable;
256 282
257 async = node.asyncModifier; 283 async = node.asyncModifier;
258 assert(!isSync); 284 assert(!isSync);
259 285
260 analysis = new PreTranslationAnalysis(unsupported); 286 analysis = new PreTranslationAnalysis(unsupported);
261 analysis.analyze(node); 287 analysis.analyze(node);
262 288
263 // To avoid name collisions with existing names, the fresh names are 289 // To avoid name collisions with existing names, the fresh names are
264 // generated after the analysis. 290 // generated after the analysis.
265 resultName = freshName("result"); 291 resultName = freshName("result");
292 errorCodeName = freshName("errorCode");
266 completerName = freshName("completer"); 293 completerName = freshName("completer");
267 controllerName = freshName("controller"); 294 controllerName = freshName("controller");
268 helperName = freshName("helper"); 295 bodyName = freshName("body");
269 gotoName = freshName("goto"); 296 gotoName = freshName("goto");
270 handlerName = freshName("handler"); 297 handlerName = freshName("handler");
271 errorName = freshName("error"); 298 errorName = freshName("error");
272 nextName = freshName("next"); 299 nextName = freshName("next");
300 nextWhenCanceledName = freshName("nextWhenCanceled");
273 returnValueName = freshName("returnValue"); 301 returnValueName = freshName("returnValue");
302 currentErrorName = freshName("storedError");
floitsch 2015/02/16 18:26:36 freshName("currentError")
sigurdm 2015/02/17 10:29:06 Done.
274 outerLabelName = freshName("outer"); 303 outerLabelName = freshName("outer");
275 selfName = freshName("self"); 304 selfName = freshName("self");
276 305
277 return node.accept(this); 306 return node.accept(this);
278 } 307 }
279 308
280 js.Expression get currentErrorHandler { 309 js.Expression get currentErrorHandler {
281 return errorHandlerLabels.isEmpty 310 return js.number(handlerLabels[targetsCatchesAndFinallies.lastWhere(
282 ? new js.LiteralNull() 311 (node) => handlerLabels[node] != null)]);
283 : js.number(errorHandlerLabels.last);
284 } 312 }
285 313
286 int allocateTempVar() { 314 int allocateTempVar() {
287 assert(tempVarHighWaterMark >= currentTempVarIndex); 315 assert(tempVarHighWaterMark >= currentTempVarIndex);
288 currentTempVarIndex++; 316 currentTempVarIndex++;
289 tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark); 317 tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark);
290 return currentTempVarIndex; 318 return currentTempVarIndex;
291 } 319 }
292 320
293 js.VariableUse useTempVar(int i) { 321 js.VariableUse useTempVar(int i) {
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after
535 })); 563 }));
536 var result = fn(visited); 564 var result = fn(visited);
537 currentTempVarIndex = oldTempVarIndex; 565 currentTempVarIndex = oldTempVarIndex;
538 return result; 566 return result;
539 } 567 }
540 568
541 /// Emits the return block that all returns should jump to (after going 569 /// Emits the return block that all returns should jump to (after going
542 /// through all the enclosing finally blocks). The jump to here is made in 570 /// through all the enclosing finally blocks). The jump to here is made in
543 /// [visitReturn]. 571 /// [visitReturn].
544 /// 572 ///
545 /// Returning from an async method calls the [thenHelper] with the result. 573 /// Returning from an async method calls the [asyncHelper] with the result.
546 /// (the result might have been stored in [returnValueName] by some finally 574 /// (the result might have been stored in [returnValueName] by some finally
547 /// block). 575 /// block).
548 /// 576 ///
549 /// Returning from a sync* function returns an [endOfIteration] marker. 577 /// Returning from a sync* function returns an [endOfIteration] marker.
550 /// 578 ///
551 /// Returning from an async* function calls the [streamHelper] with an 579 /// Returning from an async* function calls the [streamHelper] with an
552 /// [endOfIteration] marker. 580 /// [endOfIteration] marker.
553 void addExit() { 581 void addExit() {
554 if (analysis.hasExplicitReturns || isAsyncStar) { 582 if (analysis.hasExplicitReturns || isAsyncStar) {
555 beginLabel(exitLabel); 583 beginLabel(exitLabel);
556 } else { 584 } else {
557 addStatement(new js.Comment("implicit return")); 585 addStatement(new js.Comment("implicit return"));
558 } 586 }
559 switch (async) { 587 switch (async) {
560 case const js.AsyncModifier.async(): 588 case const js.AsyncModifier.async():
561 String returnValue = 589 String returnValue =
562 analysis.hasExplicitReturns ? returnValueName : "null"; 590 analysis.hasExplicitReturns ? returnValueName : "null";
563 addStatement(js.js.statement( 591 addStatement(js.js.statement(
564 "return #thenHelper($returnValue, null, $completerName, null)", { 592 "return #thenHelper($returnValue, #successCode, "
565 "thenHelper": thenHelper 593 "$completerName, null)", {
566 })); 594 "thenHelper": asyncHelper,
595 "successCode": js.number(error_codes.SUCCESS)}));
567 break; 596 break;
568 case const js.AsyncModifier.syncStar(): 597 case const js.AsyncModifier.syncStar():
569 addStatement(new js.Return(new js.Call(endOfIteration, []))); 598 addStatement(new js.Return(new js.Call(endOfIteration, [])));
570 break; 599 break;
571 case const js.AsyncModifier.asyncStar(): 600 case const js.AsyncModifier.asyncStar():
572 addStatement(js.js.statement( 601 addStatement(js.js.statement(
573 "return #streamHelper(null, null, $controllerName, null)", { 602 "return #streamHelper(null, #successCode, $controllerName)", {
574 "streamHelper": streamHelper 603 "streamHelper": streamHelper,
575 })); 604 "successCode": js.number(error_codes.SUCCESS)}));
576 break; 605 break;
577 default: 606 default:
578 diagnosticListener.internalError( 607 diagnosticListener.internalError(
579 spannable, "Internal error, unexpected asyncmodifier $async"); 608 spannable, "Internal error, unexpected asyncmodifier $async");
580 } 609 }
610 if (isAsync || isAsyncStar) {
611 beginLabel(rethrowLabel);
612 addStatement(js.js.statement(
613 "return #thenHelper($currentErrorName, #errorCode, "
614 "${isAsync ? completerName : controllerName})", {
615 "thenHelper": isAsync ? asyncHelper : streamHelper,
616 "errorCode": js.number(error_codes.ERROR)}));
617 }
581 } 618 }
582 619
583 /// The initial call to [thenHelper]/[streamHelper]. 620 /// The initial call to [asyncHelper]/[streamHelper].
584 /// 621 ///
585 /// There is no value to await/yield, so the first argument is `null` and 622 /// There is no value to await/yield, so the first argument is `null` and
586 /// also the errorCallback is `null`. 623 /// also the errorCallback is `null`.
587 /// 624 ///
588 /// Returns the [Future]/[Stream] coming from [completerName]/ 625 /// Returns the [Future]/[Stream] coming from [completerName]/
589 /// [controllerName]. 626 /// [controllerName].
590 js.Statement generateInitializer() { 627 js.Statement generateInitializer() {
591 if (isAsync) { 628 if (isAsync) {
592 return js.js.statement( 629 return js.js.statement(
593 "return #thenHelper(null, $helperName, $completerName, null);", { 630 "return #asyncHelper(null, $bodyName, $completerName, null);", {
594 "thenHelper": thenHelper 631 "asyncHelper": asyncHelper
595 }); 632 });
596 } else if (isAsyncStar) { 633 } else if (isAsyncStar) {
597 return js.js.statement( 634 return js.js.statement(
598 "return #streamOfController($controllerName);", { 635 "return #streamOfController($controllerName);", {
599 "streamOfController": streamOfController 636 "streamOfController": streamOfController
600 }); 637 });
601 } else { 638 } else {
602 throw diagnosticListener.internalError( 639 throw diagnosticListener.internalError(
603 spannable, "Unexpected asyncModifier: $async"); 640 spannable, "Unexpected asyncModifier: $async");
604 } 641 }
605 } 642 }
606 643
607 /// Rewrites an async/sync*/async* function to a normal Javascript function. 644 /// Rewrites an async/sync*/async* function to a normal Javascript function.
608 /// 645 ///
609 /// The control flow is flattened by simulating 'goto' using a switch in a 646 /// The control flow is flattened by simulating 'goto' using a switch in a
610 /// loop and a state variable [gotoName] inside a helper-function that can be 647 /// loop and a state variable [gotoName] inside a nested function [bodyName]
611 /// called back by [thenHelper]/[streamHelper]/the [Iterator]. 648 /// that can be called back by [asyncHelper]/[asyncStarHelper]/the [Iterator].
612 /// 649 ///
613 /// Local variables are hoisted outside the helper. 650 /// Local variables are hoisted outside the helper.
614 /// 651 ///
615 /// Awaits in async/async* are translated to code that remembers the current 652 /// Awaits in async/async* are translated to code that remembers the current
616 /// location (so the function can resume from where it was) followed by a call 653 /// location (so the function can resume from where it was) followed by a call
617 /// to the [thenHelper]. The helper sets up the waiting for the awaited value 654 /// to the [asyncHelper]. The helper sets up the waiting for the awaited value
618 /// and returns a future which is immediately returned by the translated 655 /// and returns a future which is immediately returned by the translated
619 /// await. 656 /// await.
620 /// Yields in async* are translated to a call to the [streamHelper]. They, 657 /// Yields in async* are translated to a call to the [asyncStarHelper]. They,
621 /// too, need to be prepared to be interrupted in case the stream is paused or 658 /// too, need to be prepared to be interrupted in case the stream is paused or
622 /// canceled. (Currently we always suspend - this is different from the spec, 659 /// canceled. (Currently we always suspend - this is different from the spec,
623 /// see `streamHelper` in `js_helper.dart`). 660 /// see `streamHelper` in `js_helper.dart`).
624 /// 661 ///
625 /// Yield/yield* in a sync* function is translated to a return of the value, 662 /// Yield/yield* in a sync* function is translated to a return of the value,
626 /// wrapped into a "IterationMarker" that signals the type (yield or yield*). 663 /// wrapped into a "IterationMarker" that signals the type (yield or yield*).
627 /// Sync* functions are executed on demand (when the user requests a value) by 664 /// Sync* functions are executed on demand (when the user requests a value) by
628 /// the Iterable that knows how to handle these values. 665 /// the Iterable that knows how to handle these values.
629 /// 666 ///
630 /// Simplified examples (not the exact translation, but intended to show the 667 /// Simplified examples (not the exact translation, but intended to show the
631 /// ideas): 668 /// ideas):
632 /// 669 ///
633 /// function (x, y, z) async { 670 /// function (x, y, z) async {
634 /// var p = await foo(); 671 /// var p = await foo();
635 /// return bar(p); 672 /// return bar(p);
636 /// } 673 /// }
637 /// 674 ///
638 /// Becomes: 675 /// Becomes (without error handling):
639 /// 676 ///
640 /// function(x, y, z) { 677 /// function(x, y, z) {
641 /// var goto = 0, returnValue, completer = new Completer(), p; 678 /// var goto = 0, returnValue, completer = new Completer(), p;
642 /// function helper(result) { 679 /// function helper(result) {
643 /// while (true) { 680 /// while (true) {
644 /// switch (goto) { 681 /// switch (goto) {
645 /// case 0: 682 /// case 0:
646 /// goto = 1 // Remember where to continue when the future succeeds. 683 /// goto = 1 // Remember where to continue when the future succeeds.
647 /// return thenHelper(foo(), helper, completer, null); 684 /// return thenHelper(foo(), helper, completer);
648 /// case 1: 685 /// case 1:
649 /// p = result; 686 /// p = result;
650 /// returnValue = bar(p); 687 /// returnValue = bar(p);
651 /// goto = 2; 688 /// goto = 2;
652 /// break; 689 /// break;
653 /// case 2: 690 /// case 2:
654 /// return thenHelper(returnValue, null, completer, null) 691 /// return thenHelper(returnValue, null, completer)
655 /// } 692 /// }
656 /// } 693 /// }
657 /// return thenHelper(null, helper, completer, null); 694 /// return thenHelper(null, helper, completer);
658 /// } 695 /// }
659 /// } 696 /// }
660 /// 697 ///
661 /// Try/catch is implemented by maintaining [handlerName] to contain the label 698 /// Try/catch is implemented by maintaining [handlerName] to contain the label
662 /// of the current handler. The switch is nested inside a try/catch that will 699 /// of the current handler. The switch is nested inside a try/catch that will
663 /// redirect the flow to the current handler. 700 /// redirect the flow to the current handler.
664 /// 701 ///
665 /// A `finally` clause is compiled similar to normal code, with the additional 702 /// A `finally` clause is compiled similar to normal code, with the additional
666 /// complexity that `finally` clauses need to know where to jump to after the 703 /// complexity that `finally` clauses need to know where to jump to after the
667 /// clause is done. In the translation, each flow-path that enters a `finally` 704 /// clause is done. In the translation, each flow-path that enters a `finally`
(...skipping 14 matching lines...) Expand all
682 /// } 719 /// }
683 /// } 720 /// }
684 /// 721 ///
685 /// Translates into (besides the fact that structures not containing 722 /// Translates into (besides the fact that structures not containing
686 /// await/yield/yield* are left intact): 723 /// await/yield/yield* are left intact):
687 /// 724 ///
688 /// function(x, y, z) { 725 /// function(x, y, z) {
689 /// var goto = 0; 726 /// var goto = 0;
690 /// var returnValue; 727 /// var returnValue;
691 /// var completer = new Completer(); 728 /// var completer = new Completer();
692 /// var handler = null; 729 /// var handler = 8; // Outside try-blocks go to the rethrow label.
693 /// var p; 730 /// var p;
731 /// var storedError;
694 /// // The result can be either the result of an awaited future, or an 732 /// // The result can be either the result of an awaited future, or an
695 /// // error if the future completed with an error. 733 /// // error if the future completed with an error.
696 /// function helper(result) { 734 /// function helper(errorCode, result) {
735 /// if (errorCode == 1) {
736 /// storedError = result;
737 /// goto = handler;
738 /// }
697 /// while (true) { 739 /// while (true) {
698 /// try { 740 /// try {
699 /// switch (goto) { 741 /// switch (goto) {
700 /// case 0: 742 /// case 0:
701 /// handler = 4; // The outer catch-handler 743 /// handler = 4; // The outer catch-handler
702 /// handler = 1; // The inner (implicit) catch-handler 744 /// handler = 1; // The inner (implicit) catch-handler
703 /// throw "error"; 745 /// throw "error";
704 /// next = [3]; 746 /// next = [3];
705 /// // After the finally (2) continue normally after the try. 747 /// // After the finally (2) continue normally after the try.
706 /// goto = 2; 748 /// goto = 2;
707 /// break; 749 /// break;
708 /// case 1: // (implicit) catch handler for inner try. 750 /// case 1: // (implicit) catch handler for inner try.
709 /// next = [3]; // destination after the finally. 751 /// next = [3]; // destination after the finally.
710 /// // fall-though to the finally handler. 752 /// // fall-though to the finally handler.
711 /// case 2: // finally for inner try 753 /// case 2: // finally for inner try
712 /// handler = 4; // catch-handler for outer try. 754 /// handler = 4; // catch-handler for outer try.
713 /// finalize1(); 755 /// finalize1();
714 /// goto = next.pop(); 756 /// goto = next.pop();
715 /// break; 757 /// break;
716 /// case 3: // exiting inner try. 758 /// case 3: // exiting inner try.
717 /// next = [6]; 759 /// next = [6];
718 /// goto = 5; // finally handler for outer try. 760 /// goto = 5; // finally handler for outer try.
719 /// break; 761 /// break;
720 /// case 4: // catch handler for outer try. 762 /// case 4: // catch handler for outer try.
721 /// e = result; 763 /// handler = 5; // If the handler throws, do the finally ..
764 /// next = [8] // ... and rethrow.
765 /// e = storedError;
722 /// handle(e); 766 /// handle(e);
723 /// // Fall through to finally. 767 /// // Fall through to finally.
724 /// case 5: // finally handler for outer try. 768 /// case 5: // finally handler for outer try.
725 /// handler = null; 769 /// handler = null;
726 /// finalize2(); 770 /// finalize2();
727 /// goto = next.pop(); 771 /// goto = next.pop();
728 /// break; 772 /// break;
729 /// case 6: // Exiting outer try. 773 /// case 6: // Exiting outer try.
730 /// case 7: // return 774 /// case 7: // return
731 /// return thenHelper(returnValue, null, completer, null); 775 /// return thenHelper(returnValue, 0, completer);
776 /// case 8: // Rethrow
777 /// return thenHelper(storedError, 1, completer);
732 /// } 778 /// }
733 /// } catch (error) { 779 /// } catch (error) {
734 /// result = error; 780 /// storedError = error;
735 /// goto = handler; 781 /// goto = handler;
736 /// } 782 /// }
737 /// } 783 /// }
738 /// return thenHelper(null, helper, completer, null); 784 /// return thenHelper(null, helper, completer);
739 /// } 785 /// }
740 /// } 786 /// }
741 /// 787 ///
742 @override 788 @override
743 js.Expression visitFun(js.Fun node) { 789 js.Expression visitFun(js.Fun node) {
744 if (isSync) return node; 790 if (isSync) return node;
745 791
746 beginLabel(newLabel("Function start")); 792 beginLabel(newLabel("Function start"));
747 // AsyncStar needs a returnlabel for its handling of cancelation. See 793 // AsyncStar needs a returnlabel for its handling of cancelation. See
748 // [visitDartYield]. 794 // [visitDartYield].
749 exitLabel = 795 exitLabel =
750 analysis.hasExplicitReturns || isAsyncStar ? newLabel("return") : null; 796 analysis.hasExplicitReturns || isAsyncStar ? newLabel("return") : null;
797 rethrowLabel = newLabel("rethrow");
798 handlerLabels[node] = rethrowLabel;
751 js.Statement body = node.body; 799 js.Statement body = node.body;
752 targetsAndTries.add(node); 800 targetsCatchesAndFinallies.add(node);
753 visitStatement(body); 801 visitStatement(body);
754 targetsAndTries.removeLast(); 802 targetsCatchesAndFinallies.removeLast();
755 addExit(); 803 addExit();
756 804
757 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { 805 List<js.SwitchClause> clauses = labelledParts.keys.map((label) {
758 return new js.Case(js.number(label), new js.Block(labelledParts[label])); 806 return new js.Case(js.number(label), new js.Block(labelledParts[label]));
759 }).toList(); 807 }).toList();
760 js.Statement helperBody = 808 js.Statement helperBody =
761 new js.Switch(new js.VariableUse(gotoName), clauses); 809 new js.Switch(new js.VariableUse(gotoName), clauses);
762 if (hasJumpThoughOuterLabel) { 810 if (hasJumpThoughOuterLabel) {
763 helperBody = js.js.statement("$outerLabelName: #", [helperBody]); 811 helperBody = js.js.statement("$outerLabelName: #", [helperBody]);
764 } 812 }
765 if (hasTryBlocks) { 813 helperBody = js.js.statement("""
766 helperBody = js.js.statement(""" 814 try {
767 try { 815 #body
768 #body 816 } catch ($errorName){
769 } catch ($errorName){ 817 $currentErrorName = $errorName;
770 if ($handlerName === null) 818 $gotoName = $handlerName;
771 throw $errorName; 819 }""", {"body": helperBody});
772 $resultName = $errorName;
773 $gotoName = $handlerName;
774 }""", {"body": helperBody});
775 }
776 List<js.VariableInitialization> inits = <js.VariableInitialization>[]; 820 List<js.VariableInitialization> inits = <js.VariableInitialization>[];
777 821
778 js.VariableInitialization makeInit(String name, js.Expression initValue) { 822 js.VariableInitialization makeInit(String name, js.Expression initValue) {
779 return new js.VariableInitialization( 823 return new js.VariableInitialization(
780 new js.VariableDeclaration(name), initValue); 824 new js.VariableDeclaration(name), initValue);
781 } 825 }
782 826
783 inits.add(makeInit(gotoName, js.number(0))); 827 inits.add(makeInit(gotoName, js.number(0)));
784 if (isAsync) { 828 if (isAsync) {
785 inits.add(makeInit(completerName, new js.New(newCompleter, []))); 829 inits.add(makeInit(completerName, new js.New(newCompleter, [])));
786 } else if (isAsyncStar) { 830 } else if (isAsyncStar) {
787 inits.add(makeInit(controllerName, 831 inits.add(makeInit(controllerName,
788 new js.Call(newController, [new js.VariableUse(helperName)]))); 832 new js.Call(newController, [new js.VariableUse(bodyName)])));
789 } 833 }
790 if (hasTryBlocks) { 834 inits.add(makeInit(handlerName, js.number(rethrowLabel)));
791 inits.add(makeInit(handlerName, new js.LiteralNull())); 835 inits.add(makeInit(currentErrorName, null));
792 }
793 if (hasJumpThroughFinally || analysis.hasYield) { 836 if (hasJumpThroughFinally || analysis.hasYield) {
794 inits.add(makeInit(nextName, null)); 837 inits.add(makeInit(nextName, null));
795 } 838 }
796 if (analysis.hasExplicitReturns && isAsync) { 839 if (analysis.hasExplicitReturns && isAsync) {
797 inits.add(makeInit(returnValueName, null)); 840 inits.add(makeInit(returnValueName, null));
798 } 841 }
842 if (isSyncStar) {
843 inits.add(makeInit(resultName, null));
844 }
799 if (analysis.hasThis && !isSyncStar) { 845 if (analysis.hasThis && !isSyncStar) {
800 // Sync* functions must remember `this` on the level of the outer 846 // Sync* functions must remember `this` on the level of the outer
801 // function. 847 // function.
802 inits.add(makeInit(selfName, new js.This())); 848 inits.add(makeInit(selfName, new js.This()));
803 } 849 }
804 inits.addAll(localVariables.map((js.VariableDeclaration decl) { 850 inits.addAll(localVariables.map((js.VariableDeclaration decl) {
805 return new js.VariableInitialization(decl, null); 851 return new js.VariableInitialization(decl, null);
806 })); 852 }));
807 inits.addAll(new Iterable.generate(tempVarHighWaterMark, 853 inits.addAll(new Iterable.generate(tempVarHighWaterMark,
808 (int i) => makeInit(useTempVar(i + 1).name, null))); 854 (int i) => makeInit(useTempVar(i + 1).name, null)));
809 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits); 855 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits);
856 // TODO(sigurdm): Explain the difference between these cases.
810 if (isSyncStar) { 857 if (isSyncStar) {
811 return js.js(""" 858 return js.js("""
812 function (#params) { 859 function (#params) {
813 if (#needsThis) 860 if (#needsThis)
814 var $selfName = this; 861 var $selfName = this;
815 return new #newIterable(function () { 862 return new #newIterable(function () {
816 #varDecl; 863 #varDecl;
817 return function $helperName($resultName) { 864 return function $bodyName() {
818 while (true) 865 while (true)
819 #helperBody; 866 #helperBody;
820 }; 867 };
821 }); 868 });
822 } 869 }
823 """, { 870 """, {
824 "params": node.params, 871 "params": node.params,
825 "needsThis": analysis.hasThis, 872 "needsThis": analysis.hasThis,
826 "helperBody": helperBody, 873 "helperBody": helperBody,
827 "varDecl": varDecl, 874 "varDecl": varDecl,
828 "newIterable": newIterable 875 "newIterable": newIterable
829 }); 876 });
830 } 877 }
831 return js.js(""" 878 return js.js("""
832 function (#params) { 879 function (#params) {
833 #varDecl; 880 #varDecl;
834 function $helperName($resultName) { 881 function $bodyName($errorCodeName, $resultName) {
882 if (#hasYield)
883 switch ($errorCodeName) {
884 case #streamWasCanceled:
885 $nextName = $nextWhenCanceledName;
886 $gotoName = $nextName.pop();
887 break;
888 case #errorCode:
889 $currentErrorName = $resultName;
890 $gotoName = $handlerName;
891 }
892 else
893 if ($errorCodeName == #errorCode) {
894 $currentErrorName = $resultName;
895 $gotoName = $handlerName;
896 }
835 while (true) 897 while (true)
836 #helperBody; 898 #helperBody;
837 } 899 }
838 #init; 900 #init;
839 }""", { 901 }""", {
840 "params": node.params, 902 "params": node.params,
903 "varDecl": varDecl,
904 "streamWasCanceled": js.number(error_codes.STREAM_WAS_CANCELED),
905 "errorCode": js.number(error_codes.ERROR),
906 "hasYield": analysis.hasYield,
841 "helperBody": helperBody, 907 "helperBody": helperBody,
842 "varDecl": varDecl,
843 "init": generateInitializer() 908 "init": generateInitializer()
844 }); 909 });
845 } 910 }
846 911
847 @override 912 @override
848 js.Expression visitAccess(js.PropertyAccess node) { 913 js.Expression visitAccess(js.PropertyAccess node) {
849 return withExpression2(node.receiver, node.selector, 914 return withExpression2(node.receiver, node.selector,
850 (receiver, selector) => new js.PropertyAccess(receiver, selector)); 915 (receiver, selector) => new js.PropertyAccess(receiver, selector));
851 } 916 }
852 917
(...skipping 28 matching lines...) Expand all
881 ], (evaluated) { 946 ], (evaluated) {
882 return new js.Assignment.compound( 947 return new js.Assignment.compound(
883 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op, 948 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op,
884 evaluated[2]); 949 evaluated[2]);
885 }); 950 });
886 } else { 951 } else {
887 throw "Unexpected assignment left hand side $leftHandSide"; 952 throw "Unexpected assignment left hand side $leftHandSide";
888 } 953 }
889 } 954 }
890 955
891 /// An await is translated to a call to [thenHelper]/[streamHelper]. 956 /// An await is translated to a call to [asyncHelper]/[streamHelper].
892 /// 957 ///
893 /// See the comments of [visitFun] for an example. 958 /// See the comments of [visitFun] for an example.
894 @override 959 @override
895 js.Expression visitAwait(js.Await node) { 960 js.Expression visitAwait(js.Await node) {
896 assert(isAsync || isAsyncStar); 961 assert(isAsync || isAsyncStar);
897 int afterAwait = newLabel("returning from await."); 962 int afterAwait = newLabel("returning from await.");
898 withExpression(node.expression, (js.Expression value) { 963 withExpression(node.expression, (js.Expression value) {
899 addStatement(setGotoVariable(afterAwait)); 964 addStatement(setGotoVariable(afterAwait));
900 js.Expression errorCallback = errorHandlerLabels.isEmpty
901 ? new js.LiteralNull()
902 : js.js("""
903 function($errorName) {
904 $gotoName = #currentHandler;
905 $helperName($errorName);
906 }""", {"currentHandler": currentErrorHandler});
907
908 addStatement(js.js.statement(""" 965 addStatement(js.js.statement("""
909 return #thenHelper(#value, 966 return #asyncHelper(#value,
910 $helperName, 967 $bodyName,
floitsch 2015/02/16 18:26:36 nit: indentation.
sigurdm 2015/02/17 10:29:06 Done.
911 ${isAsync ? completerName : controllerName}, 968 ${isAsync ? completerName : controllerName});
912 #errorCallback);
913 """, { 969 """, {
914 "thenHelper": isAsync ? thenHelper : streamHelper, 970 "asyncHelper": isAsync ? asyncHelper : streamHelper,
915 "value": value, 971 "value": value,
916 "errorCallback": errorCallback
917 })); 972 }));
918 }, store: false); 973 }, store: false);
919 beginLabel(afterAwait); 974 beginLabel(afterAwait);
920 return new js.VariableUse(resultName); 975 return new js.VariableUse(resultName);
921 } 976 }
922 977
923 /// Checks if [node] is the variable named [resultName]. 978 /// Checks if [node] is the variable named [resultName].
924 /// 979 ///
925 /// [resultName] is used to hold the result of a transformed computation 980 /// [resultName] is used to hold the result of a transformed computation
926 /// for example the result of awaiting, or the result of a conditional or 981 /// for example the result of awaiting, or the result of a conditional or
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
1066 /// 1121 ///
1067 /// It is necessary to run all nesting finally-handlers between the jump and 1122 /// It is necessary to run all nesting finally-handlers between the jump and
1068 /// the target. For that [nextName] is used as a stack of places to go. 1123 /// the target. For that [nextName] is used as a stack of places to go.
1069 /// 1124 ///
1070 /// See also [visitFun]. 1125 /// See also [visitFun].
1071 void translateJump(js.Node target, int targetLabel) { 1126 void translateJump(js.Node target, int targetLabel) {
1072 // Compute a stack of all the 'finally' nodes that must be visited before 1127 // Compute a stack of all the 'finally' nodes that must be visited before
1073 // the jump. 1128 // the jump.
1074 // The bottom of the stack is the label where the jump goes to. 1129 // The bottom of the stack is the label where the jump goes to.
1075 List<int> jumpStack = new List<int>(); 1130 List<int> jumpStack = new List<int>();
1076 for (js.Node node in targetsAndTries.reversed) { 1131 for (js.Node node in targetsCatchesAndFinallies.reversed) {
1077 if (node is js.Try) { 1132 if (finallyLabels[node] != null) {
1078 assert(node.finallyPart != null);
1079 jumpStack.add(finallyLabels[node]); 1133 jumpStack.add(finallyLabels[node]);
1080 } else if (node == target) { 1134 } else if (node == target) {
1081 jumpStack.add(targetLabel); 1135 jumpStack.add(targetLabel);
1082 break; 1136 break;
1083 } 1137 }
1084 // Ignore other nodes. 1138 // Ignore other nodes.
1085 } 1139 }
1086 jumpStack = jumpStack.reversed.toList(); 1140 jumpStack = jumpStack.reversed.toList();
1087 // As the program jumps directly to the top of the stack, it is taken off 1141 // As the program jumps directly to the top of the stack, it is taken off
1088 // now. 1142 // now.
(...skipping 24 matching lines...) Expand all
1113 int startLabel = newLabel("do body"); 1167 int startLabel = newLabel("do body");
1114 1168
1115 int continueLabel = newLabel("do condition"); 1169 int continueLabel = newLabel("do condition");
1116 continueLabels[node] = continueLabel; 1170 continueLabels[node] = continueLabel;
1117 1171
1118 int afterLabel = newLabel("after do"); 1172 int afterLabel = newLabel("after do");
1119 breakLabels[node] = afterLabel; 1173 breakLabels[node] = afterLabel;
1120 1174
1121 beginLabel(startLabel); 1175 beginLabel(startLabel);
1122 1176
1123 targetsAndTries.add(node); 1177 targetsCatchesAndFinallies.add(node);
1124 visitStatement(node.body); 1178 visitStatement(node.body);
1125 targetsAndTries.removeLast(); 1179 targetsCatchesAndFinallies.removeLast();
1126 1180
1127 beginLabel(continueLabel); 1181 beginLabel(continueLabel);
1128 withExpression(node.condition, (js.Expression condition) { 1182 withExpression(node.condition, (js.Expression condition) {
1129 addStatement(new js.If.noElse(condition, gotoAndBreak(startLabel))); 1183 addStatement(new js.If.noElse(condition, gotoAndBreak(startLabel)));
1130 }, store: false); 1184 }, store: false);
1131 beginLabel(afterLabel); 1185 beginLabel(afterLabel);
1132 } 1186 }
1133 1187
1134 @override 1188 @override
1135 void visitEmptyStatement(js.EmptyStatement node) { 1189 void visitEmptyStatement(js.EmptyStatement node) {
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
1184 js.Expression condition = node.condition; 1238 js.Expression condition = node.condition;
1185 if (condition == null || 1239 if (condition == null ||
1186 (condition is js.LiteralBool && condition.value == true)) { 1240 (condition is js.LiteralBool && condition.value == true)) {
1187 addStatement(new js.Comment("trivial condition")); 1241 addStatement(new js.Comment("trivial condition"));
1188 } else { 1242 } else {
1189 withExpression(condition, (js.Expression condition) { 1243 withExpression(condition, (js.Expression condition) {
1190 addStatement(new js.If.noElse( 1244 addStatement(new js.If.noElse(
1191 new js.Prefix("!", condition), gotoAndBreak(afterLabel))); 1245 new js.Prefix("!", condition), gotoAndBreak(afterLabel)));
1192 }, store: false); 1246 }, store: false);
1193 } 1247 }
1194 targetsAndTries.add(node); 1248 targetsCatchesAndFinallies.add(node);
1195 visitStatement(node.body); 1249 visitStatement(node.body);
1196 targetsAndTries.removeLast(); 1250 targetsCatchesAndFinallies.removeLast();
1197 if (node.update != null) { 1251 if (node.update != null) {
1198 beginLabel(continueLabel); 1252 beginLabel(continueLabel);
1199 visitExpressionIgnoreResult(node.update); 1253 visitExpressionIgnoreResult(node.update);
1200 } 1254 }
1201 addGoto(startLabel); 1255 addGoto(startLabel);
1202 beginLabel(afterLabel); 1256 beginLabel(afterLabel);
1203 } 1257 }
1204 1258
1205 @override 1259 @override
1206 void visitForIn(js.ForIn node) { 1260 void visitForIn(js.ForIn node) {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
1287 addStatement( 1341 addStatement(
1288 new js.LabeledStatement(node.label, translateInBlock(node.body))); 1342 new js.LabeledStatement(node.label, translateInBlock(node.body)));
1289 return; 1343 return;
1290 } 1344 }
1291 int breakLabel = newLabel("break ${node.label}"); 1345 int breakLabel = newLabel("break ${node.label}");
1292 int continueLabel = newLabel("continue ${node.label}"); 1346 int continueLabel = newLabel("continue ${node.label}");
1293 breakLabels[node] = breakLabel; 1347 breakLabels[node] = breakLabel;
1294 continueLabels[node] = continueLabel; 1348 continueLabels[node] = continueLabel;
1295 1349
1296 beginLabel(continueLabel); 1350 beginLabel(continueLabel);
1297 targetsAndTries.add(node); 1351 targetsCatchesAndFinallies.add(node);
1298 visitStatement(node.body); 1352 visitStatement(node.body);
1299 targetsAndTries.removeLast(); 1353 targetsCatchesAndFinallies.removeLast();
1300 beginLabel(breakLabel); 1354 beginLabel(breakLabel);
1301 } 1355 }
1302 1356
1303 @override 1357 @override
1304 js.Expression visitLiteralBool(js.LiteralBool node) => node; 1358 js.Expression visitLiteralBool(js.LiteralBool node) => node;
1305 1359
1306 @override 1360 @override
1307 visitLiteralExpression(js.LiteralExpression node) => unsupported(node); 1361 visitLiteralExpression(js.LiteralExpression node) => unsupported(node);
1308 1362
1309 @override 1363 @override
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
1495 i++; 1549 i++;
1496 } 1550 }
1497 withExpression(node.key, (js.Expression key) { 1551 withExpression(node.key, (js.Expression key) {
1498 addStatement(new js.Switch(key, clauses)); 1552 addStatement(new js.Switch(key, clauses));
1499 }, store: false); 1553 }, store: false);
1500 if (!hasDefault) { 1554 if (!hasDefault) {
1501 addGoto(after); 1555 addGoto(after);
1502 } 1556 }
1503 } 1557 }
1504 1558
1505 targetsAndTries.add(node); 1559 targetsCatchesAndFinallies.add(node);
1506 for (int i = 0; i < labels.length; i++) { 1560 for (int i = 0; i < labels.length; i++) {
1507 beginLabel(labels[i]); 1561 beginLabel(labels[i]);
1508 visitStatement(node.cases[i].body); 1562 visitStatement(node.cases[i].body);
1509 } 1563 }
1510 beginLabel(after); 1564 beginLabel(after);
1511 targetsAndTries.removeLast(); 1565 targetsCatchesAndFinallies.removeLast();
1512 } 1566 }
1513 1567
1514 @override 1568 @override
1515 js.Expression visitThis(js.This node) { 1569 js.Expression visitThis(js.This node) {
1516 return new js.VariableUse(selfName); 1570 return new js.VariableUse(selfName);
1517 } 1571 }
1518 1572
1519 @override 1573 @override
1520 void visitThrow(js.Throw node) { 1574 void visitThrow(js.Throw node) {
1521 withExpression(node.expression, (js.Expression expression) { 1575 withExpression(node.expression, (js.Expression expression) {
1522 addStatement(new js.Throw(expression)); 1576 addStatement(new js.Throw(expression));
1523 }, store: false); 1577 }, store: false);
1524 } 1578 }
1525 1579
1526 setErrorHandler() { 1580 setErrorHandler([int errorHandler]) {
1527 addExpressionStatement(new js.Assignment( 1581 addExpressionStatement(new js.Assignment(
1528 new js.VariableUse(handlerName), currentErrorHandler)); 1582 new js.VariableUse(handlerName),
1583 errorHandler == null ? currentErrorHandler : js.number(errorHandler)));
1529 } 1584 }
1530 1585
1531 @override 1586 List<int> _finalliesUpToAndEnclosingHandler() {
1587 List<int> result = new List<int>();
1588 for (int i = targetsCatchesAndFinallies.length - 1; i >= 0; i--) {
1589 js.Node node = targetsCatchesAndFinallies[i];
1590 int handlerLabel = handlerLabels[node];
1591 if (handlerLabel != null) {
1592 result.add(handlerLabel);
1593 break;
1594 }
1595 int finallyLabel = finallyLabels[node];
1596 if (finallyLabel != null) {
1597 result.add(finallyLabel);
1598 }
1599 }
1600 return result.reversed.toList();
1601 }
1602
1603 /// See the comments of [visitFun] for more explanation.
1532 void visitTry(js.Try node) { 1604 void visitTry(js.Try node) {
1533 if (!shouldTransform(node)) { 1605 if (!shouldTransform(node)) {
1534 js.Block body = translateInBlock(node.body); 1606 js.Block body = translateInBlock(node.body);
1535 js.Catch catchPart = (node.catchPart == null) 1607 js.Catch catchPart = (node.catchPart == null)
1536 ? null 1608 ? null
1537 : new js.Catch(node.catchPart.declaration, 1609 : new js.Catch(node.catchPart.declaration,
1538 translateInBlock(node.catchPart.body)); 1610 translateInBlock(node.catchPart.body));
1539 js.Block finallyPart = (node.finallyPart == null) 1611 js.Block finallyPart = (node.finallyPart == null)
1540 ? null 1612 ? null
1541 : translateInBlock(node.finallyPart); 1613 : translateInBlock(node.finallyPart);
1542 addStatement(new js.Try(body, catchPart, finallyPart)); 1614 addStatement(new js.Try(body, catchPart, finallyPart));
1543 return; 1615 return;
1544 } 1616 }
1617
1545 hasTryBlocks = true; 1618 hasTryBlocks = true;
1546 int handlerLabel = newLabel("catch"); 1619 int uncaughtLabel = newLabel("uncaught");
1620 int handlerLabel = (node.catchPart == null)
1621 ? uncaughtLabel
1622 : newLabel("catch");
1623
1547 int finallyLabel = newLabel("finally"); 1624 int finallyLabel = newLabel("finally");
1548 int afterFinallyLabel = newLabel("after finally"); 1625 int afterFinallyLabel = newLabel("after finally");
1549 errorHandlerLabels.add(handlerLabel); 1626 if (node.finallyPart != null) {
1627 finallyLabels[node.finallyPart] = finallyLabel;
1628 targetsCatchesAndFinallies.add(node.finallyPart);
1629 }
1630 if (node.catchPart != null) {
1631 handlerLabels[node.catchPart] = uncaughtLabel;
floitsch 2015/02/16 18:26:36 This needs a comment. Could you delay this until l
sigurdm 2015/02/17 10:29:05 Comment added
1632 }
1633
1634 handlerLabels[node] = handlerLabel;
1635 targetsCatchesAndFinallies.add(node);
1636
1550 // Set the error handler here. It must be cleared on every path out; 1637 // Set the error handler here. It must be cleared on every path out;
1551 // normal and error exit. 1638 // normal and error exit.
1552 setErrorHandler(); 1639 setErrorHandler();
1553 if (node.finallyPart != null) { 1640
1554 finallyLabels[node] = finallyLabel;
1555 targetsAndTries.add(node);
1556 }
1557 visitStatement(node.body); 1641 visitStatement(node.body);
1558 errorHandlerLabels.removeLast(); 1642
1559 addStatement( 1643 js.Node last = targetsCatchesAndFinallies.removeLast();
1560 js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)])); 1644 assert(last == node);
1645
1561 if (node.finallyPart == null) { 1646 if (node.finallyPart == null) {
1562 setErrorHandler(); 1647 setErrorHandler();
1563 addGoto(afterFinallyLabel); 1648 addGoto(afterFinallyLabel);
1564 } else { 1649 } else {
1565 // The handler is set as the first thing in the finally block. 1650 // The handler is reset as the first thing in the finally block.
1651 addStatement(
1652 js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)]));
1566 addGoto(finallyLabel); 1653 addGoto(finallyLabel);
1567 } 1654 }
1568 beginLabel(handlerLabel); 1655
1569 if (node.catchPart != null) { 1656 if (node.catchPart != null) {
1657 beginLabel(handlerLabel);
1658 targetsCatchesAndFinallies.add(node.catchPart);
1570 setErrorHandler(); 1659 setErrorHandler();
1571 // The catch declaration name can shadow outer variables, so a fresh name 1660 // The catch declaration name can shadow outer variables, so a fresh name
1572 // is needed to avoid collisions. See Ecma 262, 3rd edition, 1661 // is needed to avoid collisions. See Ecma 262, 3rd edition,
1573 // section 12.14. 1662 // section 12.14.
1574 String errorRename = freshName(node.catchPart.declaration.name); 1663 String errorRename = freshName(node.catchPart.declaration.name);
1575 localVariables.add(new js.VariableDeclaration(errorRename)); 1664 localVariables.add(new js.VariableDeclaration(errorRename));
1576 variableRenamings 1665 variableRenamings
1577 .add(new Pair(node.catchPart.declaration.name, errorRename)); 1666 .add(new Pair(node.catchPart.declaration.name, errorRename));
1578 addExpressionStatement(new js.Assignment( 1667 addExpressionStatement(new js.Assignment(
1579 new js.VariableUse(errorRename), new js.VariableUse(resultName))); 1668 new js.VariableUse(errorRename),
1669 new js.VariableUse(currentErrorName)));
1580 visitStatement(node.catchPart.body); 1670 visitStatement(node.catchPart.body);
1581 variableRenamings.removeLast(); 1671 variableRenamings.removeLast();
1672 if (node.finallyPart != null) {
1673 // The error has been caught, so after the finally, continue after the
1674 // try.
1675 addStatement(js.js.statement("$nextName = [#];",
1676 [js.number(afterFinallyLabel)]));
1677 addGoto(finallyLabel);
1678 } else {
1679 addGoto(afterFinallyLabel);
1680 }
1681 js.Node last = targetsCatchesAndFinallies.removeLast();
1682 assert(last == node.catchPart);
1683 }
1684
1685 // The "uncaught"-handler tells the finally-block to continue with
1686 // the enclosing finally-blocks until the current catch-handler.
1687 beginLabel(uncaughtLabel);
1688
1689 List<int> enclosingFinallies = _finalliesUpToAndEnclosingHandler();
1690
1691 int nextLabel = enclosingFinallies.removeLast();
1692 if (enclosingFinallies.isNotEmpty) {
1693 // enclosingFinallies can be empty if
floitsch 2015/02/16 18:26:36 Unfinished comment.
sigurdm 2015/02/17 10:29:06 Done.
sigurdm 2015/02/17 10:29:06 Done.
1694 addStatement(
1695 js.js.statement("$nextName = #;", new js.ArrayInitializer(
1696 enclosingFinallies.map(js.number).toList())));
1697 }
1698 if (node.finallyPart == null) {
1699 // The finally-block belonging to [node] will be visited because of
1700 // fallthrough. If it does not exist, add an explicit goto.
1701 addGoto(nextLabel);
1582 } 1702 }
1583 if (node.finallyPart != null) { 1703 if (node.finallyPart != null) {
1584 targetsAndTries.removeLast(); 1704 js.Node last = targetsCatchesAndFinallies.removeLast();
1585 setErrorHandler(); 1705 assert(last == node.finallyPart);
1586 // This belongs to the catch-part, but is only needed if there is a 1706
1587 // `finally`. Therefore it is in this branch.
1588 // This is needed even if there is no explicit catch-branch, because
1589 // if an exception is raised the finally part has to be run.
1590 addStatement(
1591 js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)]));
1592 beginLabel(finallyLabel); 1707 beginLabel(finallyLabel);
1593 setErrorHandler(); 1708 setErrorHandler();
1594 visitStatement(node.finallyPart); 1709 visitStatement(node.finallyPart);
1595 addStatement(new js.Comment("// goto the next finally handler")); 1710 addStatement(new js.Comment("// goto the next finally handler"));
1596 addStatement(js.js.statement("$gotoName = $nextName.pop();")); 1711 addStatement(js.js.statement("$gotoName = $nextName.pop();"));
1597 addBreak(); 1712 addBreak();
1598 } 1713 }
1599 beginLabel(afterFinallyLabel); 1714 beginLabel(afterFinallyLabel);
1600 } 1715 }
1601 1716
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
1651 int afterLabel = newLabel("after while"); 1766 int afterLabel = newLabel("after while");
1652 breakLabels[node] = afterLabel; 1767 breakLabels[node] = afterLabel;
1653 js.Expression condition = node.condition; 1768 js.Expression condition = node.condition;
1654 // If the condition is `true`, a test is not needed. 1769 // If the condition is `true`, a test is not needed.
1655 if (!(condition is js.LiteralBool && condition.value == true)) { 1770 if (!(condition is js.LiteralBool && condition.value == true)) {
1656 withExpression(node.condition, (js.Expression condition) { 1771 withExpression(node.condition, (js.Expression condition) {
1657 addStatement(new js.If.noElse( 1772 addStatement(new js.If.noElse(
1658 new js.Prefix("!", condition), gotoAndBreak(afterLabel))); 1773 new js.Prefix("!", condition), gotoAndBreak(afterLabel)));
1659 }, store: false); 1774 }, store: false);
1660 } 1775 }
1661 targetsAndTries.add(node); 1776 targetsCatchesAndFinallies.add(node);
1662 visitStatement(node.body); 1777 visitStatement(node.body);
1663 targetsAndTries.removeLast(); 1778 targetsCatchesAndFinallies.removeLast();
1664 addGoto(continueLabel); 1779 addGoto(continueLabel);
1665 beginLabel(afterLabel); 1780 beginLabel(afterLabel);
1666 } 1781 }
1667 1782
1668 /// Translates a yield/yield* in an sync*. 1783 /// Translates a yield/yield* in an sync*.
1669 /// 1784 ///
1670 /// `yield` in a sync* function just returns [value]. 1785 /// `yield` in a sync* function just returns [value].
1671 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it. 1786 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it.
1672 void addSyncYield(js.DartYield node, js.Expression expression) { 1787 void addSyncYield(js.DartYield node, js.Expression expression) {
1673 assert(isSyncStar); 1788 assert(isSyncStar);
1674 if (node.hasStar) { 1789 if (node.hasStar) {
1675 addStatement( 1790 addStatement(
1676 new js.Return(new js.Call(yieldStarExpression, [expression]))); 1791 new js.Return(new js.Call(yieldStarExpression, [expression])));
1677 } else { 1792 } else {
1678 addStatement(new js.Return(expression)); 1793 addStatement(new js.Return(expression));
1679 } 1794 }
1680 } 1795 }
1681 1796
1682 /// Translates a yield/yield* in an async* function. 1797 /// Translates a yield/yield* in an async* function.
1683 /// 1798 ///
1684 /// yield/yield* in an async* function is translated much like the `await` is 1799 /// yield/yield* in an async* function is translated much like the `await` is
1685 /// translated in [visitAwait], only the object is wrapped in a 1800 /// translated in [visitAwait], only the object is wrapped in a
1686 /// [yieldExpression]/[yieldStarExpression] to let [streamHelper] distinguish. 1801 /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper]
1687 /// 1802 /// distinguish them.
1688 /// Because there is no Future that can fail (as there is in await) null is 1803 /// Also a [nextWhenCanceledName] is set up to
floitsch 2015/02/16 18:26:36 Unfinished sentence.
sigurdm 2015/02/17 10:29:05 Done.
1689 /// passed as the errorCallback.
1690 void addAsyncYield(js.DartYield node, js.Expression expression) { 1804 void addAsyncYield(js.DartYield node, js.Expression expression) {
1691 assert(isAsyncStar); 1805 assert(isAsyncStar);
1692 // Find all the finally blocks that should be performed if the stream is 1806 // Find all the finally blocks that should be performed if the stream is
1693 // canceled during the yield. 1807 // canceled during the yield.
1694 // At the bottom of the stack is the return label. 1808 // At the bottom of the stack is the return label.
1695 List<int> enclosingFinallyLabels = <int>[exitLabel]; 1809 List<int> enclosingFinallyLabels = <int>[exitLabel];
1696 enclosingFinallyLabels.addAll(targetsAndTries 1810 enclosingFinallyLabels.addAll(targetsCatchesAndFinallies
1697 .where((js.Node node) => node is js.Try) 1811 .where((js.Node node) => finallyLabels[node] != null)
1698 .map((js.Try node) => finallyLabels[node])); 1812 .map((js.Block node) => finallyLabels[node]));
1699 int destinationOnCancel = enclosingFinallyLabels.removeLast(); 1813 addStatement(js.js.statement("$nextWhenCanceledName = #",
1700 js.ArrayInitializer finallyListInitializer = new js.ArrayInitializer( 1814 [new js.ArrayInitializer(enclosingFinallyLabels.map(js.number)
1701 enclosingFinallyLabels.map(js.number).toList()); 1815 .toList())]));
1702 addStatement(js.js.statement(""" 1816 addStatement(js.js.statement("""
1703 return #streamHelper(#yieldExpression(#expression), 1817 return #streamHelper(#yieldExpression(#expression),
1704 $helperName, $controllerName, function () { 1818 $bodyName, $controllerName);""", {
1705 if (#notEmptyFinallyList)
1706 $nextName = #finallyList;
1707 $gotoName = #destinationOnCancel;
1708 $helperName();
1709 });""", {
1710 "streamHelper": streamHelper, 1819 "streamHelper": streamHelper,
1711 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression, 1820 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression,
1712 "expression": expression, 1821 "expression": expression,
1713 "notEmptyFinallyList": enclosingFinallyLabels.isNotEmpty,
1714 "finallyList": finallyListInitializer,
1715 "destinationOnCancel": js.number(destinationOnCancel)
1716 })); 1822 }));
1717 } 1823 }
1718 1824
1719 @override 1825 @override
1720 void visitDartYield(js.DartYield node) { 1826 void visitDartYield(js.DartYield node) {
1721 assert(isSyncStar || isAsyncStar); 1827 assert(isSyncStar || isAsyncStar);
1722 int label = newLabel("after yield"); 1828 int label = newLabel("after yield");
1723 // Don't do a break here for the goto, but instead a return in either 1829 // Don't do a break here for the goto, but instead a return in either
1724 // addSynYield or addAsyncYield. 1830 // addSynYield or addAsyncYield.
1725 withExpression(node.expression, (js.Expression expression) { 1831 withExpression(node.expression, (js.Expression expression) {
(...skipping 417 matching lines...) Expand 10 before | Expand all | Expand 10 after
2143 return condition || body; 2249 return condition || body;
2144 } 2250 }
2145 2251
2146 @override 2252 @override
2147 bool visitDartYield(js.DartYield node) { 2253 bool visitDartYield(js.DartYield node) {
2148 hasYield = true; 2254 hasYield = true;
2149 visit(node.expression); 2255 visit(node.expression);
2150 return true; 2256 return true;
2151 } 2257 }
2152 } 2258 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/js_backend/backend.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698