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

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

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

Powered by Google App Engine
This is Rietveld 408576698