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

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: Added missing files (test and shared library). 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
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'
floitsch 2015/02/13 15:30:06 I think we import it relative for embedded names.
sigurdm 2015/02/17 08:43:10 At least in js_backend/js_backend.dart it is impor
14 as error_codes;
floitsch 2015/02/13 15:30:07 not 100% about the naming convention of imports. W
sigurdm 2015/02/17 08:43:10 The style guide says lower_case_underscores.
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 Map<js.Node, int> finallyLabels = new Map<js.Node, int>();
43 Map<js.Node, int> handlerLabels = new Map<js.Node, int>();
41 int exitLabel; 44 int exitLabel;
45 int rethrowLabel;
42 46
43 // A stack of all enclosing jump targets (including the function for 47 // A stack of all enclosing jump targets (including the function for
44 // representing the target of a return, and all enclosing try-blocks that have 48 // representing the target of a return, and all enclosing try-blocks that have
45 // finally part, this way ensuring all the finally blocks between a jump and 49 // finally part, this way ensuring all the finally blocks between a jump and
46 // its target are run before the jump. 50 // its target are run before the jump.
47 List<js.Node> targetsAndTries = new List<js.Node>(); 51 List<js.Node> targetsAndTries = new List<js.Node>();
48 52
49 List<int> continueStack = new List<int>(); 53 List<int> continueStack = new List<int>();
50 List<int> breakStack = new List<int>(); 54 List<int> breakStack = new List<int>();
51 List<int> returnStack = new List<int>(); 55 List<int> returnStack = new List<int>();
(...skipping 27 matching lines...) Expand all
79 /// result = [[a]]; 83 /// result = [[a]];
80 /// goto = joinLabel; 84 /// goto = joinLabel;
81 /// case elseLabel: 85 /// case elseLabel:
82 /// result = [[b]]; 86 /// result = [[b]];
83 /// case joinLabel: 87 /// case joinLabel:
84 /// // Now the result of computing the condition is in result. 88 /// // Now the result of computing the condition is in result.
85 /// .... 89 /// ....
86 /// } 90 /// }
87 /// } 91 /// }
88 /// 92 ///
89 /// It is a parameter to the [helperName] function, so that [thenHelper] and 93 /// It is a parameter to the [bodyName] function, so that [asyncHelper] and
90 /// [streamHelper] can call [helperName] with the result of an awaited Future. 94 /// [streamHelper] can call [bodyName] with the result of an awaited Future.
91 String resultName; 95 String resultName;
92 96
97 /// A parameter to the [bodyName] function. Indicating if we are in success
98 /// or error case.
99 String errorCodeName;
100
93 /// The name of the inner function that is scheduled to do each await/yield, 101 /// The name of the inner function that is scheduled to do each await/yield,
94 /// and called to do a new iteration for sync*. 102 /// and called to do a new iteration for sync*.
95 String helperName; 103 String bodyName;
96 104
97 /// The Completer that will finish an async function. 105 /// The Completer that will finish an async function.
98 /// 106 ///
99 /// Not used for sync* or async* functions. 107 /// Not used for sync* or async* functions.
100 String completerName; 108 String completerName;
101 109
102 /// The StreamController that controls an async* function. 110 /// The StreamController that controls an async* function.
103 /// 111 ///
104 /// Not used for async and sync* functions 112 /// Not used for async and sync* functions
105 String controllerName; 113 String controllerName;
106 114
107 /// Used to simulate a goto. 115 /// Used to simulate a goto.
108 /// 116 ///
109 /// To "goto" a label, the label is assigned to this 117 /// To "goto" a label, the label is assigned to this
110 /// variable, and break out of the switch to take another iteration in the 118 /// variable, and break out of the switch to take another iteration in the
111 /// while loop. See [addGoto] 119 /// while loop. See [addGoto]
112 String gotoName; 120 String gotoName;
113 121
114 /// The label of the current error handler. 122 /// The label of the current error handler.
115 String handlerName; 123 String handlerName;
116 124
117 /// Current caught error. 125 /// Current caught error.
118 String errorName; 126 String errorName;
119 127
120 /// A stack of labels of finally blocks to visit, and the label to go to after 128 /// A stack of labels of finally blocks to visit, and the label to go to after
121 /// the last. 129 /// the last.
122 String nextName; 130 String nextName;
123 131
132 /// The stack of labels of finally blocks to assign to [nextName] if the
133 /// async* [StreamSubscription] was canceled during a yield.
134 String yieldNextName;
floitsch 2015/02/13 15:30:06 nextWhenCanceledName ?
sigurdm 2015/02/17 08:43:09 Better, Donde
135
124 /// The current returned value (a finally block may overwrite it). 136 /// The current returned value (a finally block may overwrite it).
125 String returnValueName; 137 String returnValueName;
126 138
139 /// The thrown error (it might need to be rethrown after a finally block).
floitsch 2015/02/13 15:30:06 currentErrorName ? /// If we are in the process o
sigurdm 2015/02/17 08:43:09 Done.
140 String storedErrorName;
141
127 /// The label of the outer loop. 142 /// The label of the outer loop.
128 /// 143 ///
129 /// Used if there are untransformed loops containing break or continues to 144 /// Used if there are untransformed loops containing break or continues to
130 /// targets outside the loop. 145 /// targets outside the loop.
131 String outerLabelName; 146 String outerLabelName;
132 147
133 /// If javascript `this` is used, it is accessed via this variable, in the 148 /// If javascript `this` is used, it is accessed via this variable, in the
134 /// [helperName] function. 149 /// [bodyName] function.
135 String selfName; 150 String selfName;
136 151
137 // These expressions are hooks for communicating with the runtime. 152 // These expressions are hooks for communicating with the runtime.
138 153
139 /// The function called by an async function to simulate an await or return. 154 /// The function called by an async function to simulate an await or return.
140 /// 155 ///
141 /// For an await it is called with: 156 /// For an await it is called with:
142 /// 157 ///
143 /// - The value to await 158 /// - The value to await
144 /// - The [helperName] 159 /// - The [bodyName]
145 /// - The [completerName] 160 /// - The [completerName]
146 /// - A JavaScript function that is executed if the future completed with 161 /// - A JavaScript function that is executed if the future completed with
147 /// an error. That function is responsible for executing the right error 162 /// an error. That function is responsible for executing the right error
148 /// handler and/or finally blocks). 163 /// handler and/or finally blocks).
149 /// 164 ///
150 /// For a return it is called with: 165 /// For a return it is called with:
151 /// 166 ///
152 /// - The value to complete the completer with. 167 /// - The value to complete the completer with.
153 /// - null 168 /// - null
154 /// - The [completerName] 169 /// - The [completerName]
155 /// - null. 170 /// - null.
156 final js.Expression thenHelper; 171 final js.Expression asyncHelper;
157 172
158 /// The function called by an async* function to simulate an await, yield or 173 /// The function called by an async* function to simulate an await, yield or
159 /// yield*. 174 /// yield*.
160 /// 175 ///
161 /// For an await/yield/yield* it is called with: 176 /// For an await/yield/yield* it is called with:
162 /// 177 ///
163 /// - The value to await/yieldExpression(value to yield)/ 178 /// - The value to await/yieldExpression(value to yield)/
164 /// yieldStarExpression(stream to yield) 179 /// yieldStarExpression(stream to yield)
165 /// - The [helperName] 180 /// - The [bodyName]
floitsch 2015/02/13 15:30:06 The body function ([bodyName]). ? Otherwise it so
sigurdm 2015/02/17 08:43:10 Done.
166 /// - The [controllerName] 181 /// - The [controllerName]
167 /// - A JavaScript function that is executed if the future completed with 182 /// - A JavaScript function that is executed if the future completed with
168 /// an error. That function is responsible for executing the right error 183 /// an error. That function is responsible for executing the right error
169 /// handler and/or finally blocks). 184 /// handler and/or finally blocks).
170 /// 185 ///
171 /// For a return it is called with: 186 /// For a return it is called with:
172 /// 187 ///
173 /// - null 188 /// - null
174 /// - null 189 /// - null
175 /// - The [controllerName] 190 /// - The [controllerName]
176 /// - null. 191 /// - null.
177 final js.Expression streamHelper; 192 final js.Expression streamHelper;
178 193
179 /// Contructor used to initialize the [completerName] variable. 194 /// Contructor used to initialize the [completerName] variable.
180 /// 195 ///
181 /// Specific to async methods. 196 /// Specific to async methods.
182 final js.Expression newCompleter; 197 final js.Expression newCompleter;
183 198
184 /// Contructor used to initialize the [controllerName] variable. 199 /// Contructor used to initialize the [controllerName] variable.
185 /// 200 ///
186 /// Specific to async* methods. 201 /// Specific to async* methods.
187 final js.Expression newController; 202 final js.Expression newController;
188 203
189 /// Used to get the `Stream` out of the [controllerName] variable. 204 /// Used to get the `Stream` out of the [controllerName] variable.
190 /// 205 ///
191 /// Specific to async* methods. 206 /// Specific to async* methods.
192 final js.Expression streamOfController; 207 final js.Expression streamOfController;
193 208
194 /// Contructor creating the Iterable for a sync* method. Called with 209 /// Contructor creating the Iterable for a sync* method. Called with
195 /// [helperName]. 210 /// [bodyName].
196 final js.Expression newIterable; 211 final js.Expression newIterable;
197 212
198 /// A JS Expression that creates a marker showing that iteration is over. 213 /// A JS Expression that creates a marker showing that iteration is over.
199 /// 214 ///
200 /// Called without arguments. 215 /// Called without arguments.
201 final js.Expression endOfIteration; 216 final js.Expression endOfIteration;
202 217
203 /// A JS Expression that creates a marker indicating a 'yield' statement. 218 /// A JS Expression that creates a marker indicating a 'yield' statement.
204 /// 219 ///
205 /// Called with the value to yield. 220 /// Called with the value to yield.
(...skipping 22 matching lines...) Expand all
228 243
229 js.AsyncModifier async; 244 js.AsyncModifier async;
230 245
231 bool get isSync => async == const js.AsyncModifier.sync(); 246 bool get isSync => async == const js.AsyncModifier.sync();
232 bool get isAsync => async == const js.AsyncModifier.async(); 247 bool get isAsync => async == const js.AsyncModifier.async();
233 bool get isSyncStar => async == const js.AsyncModifier.syncStar(); 248 bool get isSyncStar => async == const js.AsyncModifier.syncStar();
234 bool get isAsyncStar => async == const js.AsyncModifier.asyncStar(); 249 bool get isAsyncStar => async == const js.AsyncModifier.asyncStar();
235 250
236 AsyncRewriter(this.diagnosticListener, 251 AsyncRewriter(this.diagnosticListener,
237 spannable, 252 spannable,
238 {this.thenHelper, 253 {this.asyncHelper,
239 this.streamHelper, 254 this.streamHelper,
240 this.streamOfController, 255 this.streamOfController,
241 this.newCompleter, 256 this.newCompleter,
242 this.newController, 257 this.newController,
243 this.endOfIteration, 258 this.endOfIteration,
244 this.newIterable, 259 this.newIterable,
245 this.yieldExpression, 260 this.yieldExpression,
246 this.yieldStarExpression, 261 this.yieldStarExpression,
247 this.safeVariableName}) 262 this.safeVariableName})
248 : _spannable = spannable; 263 : _spannable = spannable;
249 264
250 /// Main entry point. 265 /// Main entry point.
251 /// Rewrites a sync*/async/async* function to an equivalent normal function. 266 /// Rewrites a sync*/async/async* function to an equivalent normal function.
252 /// 267 ///
253 /// [spannable] can be passed to have a location for error messages. 268 /// [spannable] can be passed to have a location for error messages.
254 js.Fun rewrite(js.Fun node, [Spannable spannable]) { 269 js.Fun rewrite(js.Fun node, [Spannable spannable]) {
255 _spannable = spannable; 270 _spannable = spannable;
256 271
257 async = node.asyncModifier; 272 async = node.asyncModifier;
258 assert(!isSync); 273 assert(!isSync);
259 274
260 analysis = new PreTranslationAnalysis(unsupported); 275 analysis = new PreTranslationAnalysis(unsupported);
261 analysis.analyze(node); 276 analysis.analyze(node);
262 277
263 // To avoid name collisions with existing names, the fresh names are 278 // To avoid name collisions with existing names, the fresh names are
264 // generated after the analysis. 279 // generated after the analysis.
265 resultName = freshName("result"); 280 resultName = freshName("result");
281 errorCodeName = freshName("errorCode");
266 completerName = freshName("completer"); 282 completerName = freshName("completer");
267 controllerName = freshName("controller"); 283 controllerName = freshName("controller");
268 helperName = freshName("helper"); 284 bodyName = freshName("body");
269 gotoName = freshName("goto"); 285 gotoName = freshName("goto");
270 handlerName = freshName("handler"); 286 handlerName = freshName("handler");
271 errorName = freshName("error"); 287 errorName = freshName("error");
272 nextName = freshName("next"); 288 nextName = freshName("next");
289 yieldNextName = freshName("yieldNext");
273 returnValueName = freshName("returnValue"); 290 returnValueName = freshName("returnValue");
291 storedErrorName = freshName("storedError");
274 outerLabelName = freshName("outer"); 292 outerLabelName = freshName("outer");
275 selfName = freshName("self"); 293 selfName = freshName("self");
276 294
277 return node.accept(this); 295 return node.accept(this);
278 } 296 }
279 297
280 js.Expression get currentErrorHandler { 298 js.Expression get currentErrorHandler {
281 return errorHandlerLabels.isEmpty 299 return errorHandlerLabels.isEmpty
282 ? new js.LiteralNull() 300 ? js.number(rethrowLabel)
283 : js.number(errorHandlerLabels.last); 301 : js.number(errorHandlerLabels.last);
284 } 302 }
285 303
286 int allocateTempVar() { 304 int allocateTempVar() {
287 assert(tempVarHighWaterMark >= currentTempVarIndex); 305 assert(tempVarHighWaterMark >= currentTempVarIndex);
288 currentTempVarIndex++; 306 currentTempVarIndex++;
289 tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark); 307 tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark);
290 return currentTempVarIndex; 308 return currentTempVarIndex;
291 } 309 }
292 310
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after
535 })); 553 }));
536 var result = fn(visited); 554 var result = fn(visited);
537 currentTempVarIndex = oldTempVarIndex; 555 currentTempVarIndex = oldTempVarIndex;
538 return result; 556 return result;
539 } 557 }
540 558
541 /// Emits the return block that all returns should jump to (after going 559 /// 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 560 /// through all the enclosing finally blocks). The jump to here is made in
543 /// [visitReturn]. 561 /// [visitReturn].
544 /// 562 ///
545 /// Returning from an async method calls the [thenHelper] with the result. 563 /// Returning from an async method calls the [asyncHelper] with the result.
546 /// (the result might have been stored in [returnValueName] by some finally 564 /// (the result might have been stored in [returnValueName] by some finally
547 /// block). 565 /// block).
548 /// 566 ///
549 /// Returning from a sync* function returns an [endOfIteration] marker. 567 /// Returning from a sync* function returns an [endOfIteration] marker.
550 /// 568 ///
551 /// Returning from an async* function calls the [streamHelper] with an 569 /// Returning from an async* function calls the [streamHelper] with an
552 /// [endOfIteration] marker. 570 /// [endOfIteration] marker.
553 void addExit() { 571 void addExit() {
554 if (analysis.hasExplicitReturns || isAsyncStar) { 572 if (analysis.hasExplicitReturns || isAsyncStar) {
555 beginLabel(exitLabel); 573 beginLabel(exitLabel);
556 } else { 574 } else {
557 addStatement(new js.Comment("implicit return")); 575 addStatement(new js.Comment("implicit return"));
558 } 576 }
559 switch (async) { 577 switch (async) {
560 case const js.AsyncModifier.async(): 578 case const js.AsyncModifier.async():
561 String returnValue = 579 String returnValue =
562 analysis.hasExplicitReturns ? returnValueName : "null"; 580 analysis.hasExplicitReturns ? returnValueName : "null";
563 addStatement(js.js.statement( 581 addStatement(js.js.statement(
564 "return #thenHelper($returnValue, null, $completerName, null)", { 582 "return #thenHelper($returnValue, ${error_codes.SUCCESS}, "
floitsch 2015/02/13 15:30:06 Don't use "$" if you don't need to. This is a val
sigurdm 2015/02/17 08:43:10 Done.
565 "thenHelper": thenHelper 583 "$completerName, null)",
566 })); 584 {"thenHelper": asyncHelper}));
567 break; 585 break;
568 case const js.AsyncModifier.syncStar(): 586 case const js.AsyncModifier.syncStar():
569 addStatement(new js.Return(new js.Call(endOfIteration, []))); 587 addStatement(new js.Return(new js.Call(endOfIteration, [])));
570 break; 588 break;
571 case const js.AsyncModifier.asyncStar(): 589 case const js.AsyncModifier.asyncStar():
572 addStatement(js.js.statement( 590 addStatement(js.js.statement(
573 "return #streamHelper(null, null, $controllerName, null)", { 591 "return #streamHelper(null, ${error_codes.SUCCESS}, "
floitsch 2015/02/13 15:30:06 ditto.
sigurdm 2015/02/17 08:43:10 Done.
592 "$controllerName)", {
574 "streamHelper": streamHelper 593 "streamHelper": streamHelper
575 })); 594 }));
576 break; 595 break;
577 default: 596 default:
578 diagnosticListener.internalError( 597 diagnosticListener.internalError(
579 spannable, "Internal error, unexpected asyncmodifier $async"); 598 spannable, "Internal error, unexpected asyncmodifier $async");
580 } 599 }
600 if (isAsync || isAsyncStar) {
601 beginLabel(rethrowLabel);
602 addStatement(js.js.statement(
603 "return #thenHelper($storedErrorName, ${error_codes.ERROR}, "
floitsch 2015/02/13 15:30:06 ditto.
sigurdm 2015/02/17 08:43:09 Done.
604 "${isAsync ? completerName : controllerName})",
605 {"thenHelper": isAsync ? asyncHelper : streamHelper}));
606 }
581 } 607 }
582 608
583 /// The initial call to [thenHelper]/[streamHelper]. 609 /// The initial call to [asyncHelper]/[streamHelper].
584 /// 610 ///
585 /// There is no value to await/yield, so the first argument is `null` and 611 /// There is no value to await/yield, so the first argument is `null` and
586 /// also the errorCallback is `null`. 612 /// also the errorCallback is `null`.
587 /// 613 ///
588 /// Returns the [Future]/[Stream] coming from [completerName]/ 614 /// Returns the [Future]/[Stream] coming from [completerName]/
589 /// [controllerName]. 615 /// [controllerName].
590 js.Statement generateInitializer() { 616 js.Statement generateInitializer() {
591 if (isAsync) { 617 if (isAsync) {
592 return js.js.statement( 618 return js.js.statement(
593 "return #thenHelper(null, $helperName, $completerName, null);", { 619 "return #asyncHelper(null, $bodyName, $completerName, null);", {
594 "thenHelper": thenHelper 620 "asyncHelper": asyncHelper
595 }); 621 });
596 } else if (isAsyncStar) { 622 } else if (isAsyncStar) {
597 return js.js.statement( 623 return js.js.statement(
598 "return #streamOfController($controllerName);", { 624 "return #streamOfController($controllerName);", {
599 "streamOfController": streamOfController 625 "streamOfController": streamOfController
600 }); 626 });
601 } else { 627 } else {
602 throw diagnosticListener.internalError( 628 throw diagnosticListener.internalError(
603 spannable, "Unexpected asyncModifier: $async"); 629 spannable, "Unexpected asyncModifier: $async");
604 } 630 }
605 } 631 }
606 632
607 /// Rewrites an async/sync*/async* function to a normal Javascript function. 633 /// Rewrites an async/sync*/async* function to a normal Javascript function.
608 /// 634 ///
609 /// The control flow is flattened by simulating 'goto' using a switch in a 635 /// 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 636 /// loop and a state variable [gotoName] inside a nested function [bodyName]
611 /// called back by [thenHelper]/[streamHelper]/the [Iterator]. 637 /// that can be called back by [asyncHelper]/[asyncStarHelper]/the [Iterator].
612 /// 638 ///
613 /// Local variables are hoisted outside the helper. 639 /// Local variables are hoisted outside the helper.
614 /// 640 ///
615 /// Awaits in async/async* are translated to code that remembers the current 641 /// 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 642 /// 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 643 /// 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 644 /// and returns a future which is immediately returned by the translated
619 /// await. 645 /// await.
620 /// Yields in async* are translated to a call to the [streamHelper]. They, 646 /// 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 647 /// 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, 648 /// canceled. (Currently we always suspend - this is different from the spec,
623 /// see `streamHelper` in `js_helper.dart`). 649 /// see `streamHelper` in `js_helper.dart`).
624 /// 650 ///
625 /// Yield/yield* in a sync* function is translated to a return of the value, 651 /// 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*). 652 /// 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 653 /// Sync* functions are executed on demand (when the user requests a value) by
628 /// the Iterable that knows how to handle these values. 654 /// the Iterable that knows how to handle these values.
629 /// 655 ///
630 /// Simplified examples (not the exact translation, but intended to show the 656 /// Simplified examples (not the exact translation, but intended to show the
631 /// ideas): 657 /// ideas):
632 /// 658 ///
633 /// function (x, y, z) async { 659 /// function (x, y, z) async {
634 /// var p = await foo(); 660 /// var p = await foo();
635 /// return bar(p); 661 /// return bar(p);
636 /// } 662 /// }
637 /// 663 ///
638 /// Becomes: 664 /// Becomes (without error handling):
639 /// 665 ///
640 /// function(x, y, z) { 666 /// function(x, y, z) {
641 /// var goto = 0, returnValue, completer = new Completer(), p; 667 /// var goto = 0, returnValue, completer = new Completer(), p;
642 /// function helper(result) { 668 /// function helper(result) {
643 /// while (true) { 669 /// while (true) {
644 /// switch (goto) { 670 /// switch (goto) {
645 /// case 0: 671 /// case 0:
646 /// goto = 1 // Remember where to continue when the future succeeds. 672 /// goto = 1 // Remember where to continue when the future succeeds.
647 /// return thenHelper(foo(), helper, completer, null); 673 /// return thenHelper(foo(), helper, completer);
648 /// case 1: 674 /// case 1:
649 /// p = result; 675 /// p = result;
650 /// returnValue = bar(p); 676 /// returnValue = bar(p);
651 /// goto = 2; 677 /// goto = 2;
652 /// break; 678 /// break;
653 /// case 2: 679 /// case 2:
654 /// return thenHelper(returnValue, null, completer, null) 680 /// return thenHelper(returnValue, null, completer)
655 /// } 681 /// }
656 /// } 682 /// }
657 /// return thenHelper(null, helper, completer, null); 683 /// return thenHelper(null, helper, completer);
658 /// } 684 /// }
659 /// } 685 /// }
660 /// 686 ///
661 /// Try/catch is implemented by maintaining [handlerName] to contain the label 687 /// 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 688 /// of the current handler. The switch is nested inside a try/catch that will
663 /// redirect the flow to the current handler. 689 /// redirect the flow to the current handler.
664 /// 690 ///
665 /// A `finally` clause is compiled similar to normal code, with the additional 691 /// 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 692 /// 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` 693 /// clause is done. In the translation, each flow-path that enters a `finally`
(...skipping 14 matching lines...) Expand all
682 /// } 708 /// }
683 /// } 709 /// }
684 /// 710 ///
685 /// Translates into (besides the fact that structures not containing 711 /// Translates into (besides the fact that structures not containing
686 /// await/yield/yield* are left intact): 712 /// await/yield/yield* are left intact):
687 /// 713 ///
688 /// function(x, y, z) { 714 /// function(x, y, z) {
689 /// var goto = 0; 715 /// var goto = 0;
690 /// var returnValue; 716 /// var returnValue;
691 /// var completer = new Completer(); 717 /// var completer = new Completer();
692 /// var handler = null; 718 /// var handler = 8; // Outside try-blocks go to the rethrow label.
floitsch 2015/02/13 15:30:06 Handler is slightly weird, because we also use it
sigurdm 2015/02/17 08:43:10 Acknowledged. I mentioned it in visitTry.
693 /// var p; 719 /// var p;
720 /// var storedError;
694 /// // The result can be either the result of an awaited future, or an 721 /// // The result can be either the result of an awaited future, or an
695 /// // error if the future completed with an error. 722 /// // error if the future completed with an error.
696 /// function helper(result) { 723 /// function helper(errorCode, result) {
724 /// if (errorCode == 1) {
725 /// storedError = result;
726 /// goto = handler;
727 /// }
697 /// while (true) { 728 /// while (true) {
698 /// try { 729 /// try {
699 /// switch (goto) { 730 /// switch (goto) {
700 /// case 0: 731 /// case 0:
701 /// handler = 4; // The outer catch-handler 732 /// handler = 4; // The outer catch-handler
702 /// handler = 1; // The inner (implicit) catch-handler 733 /// handler = 1; // The inner (implicit) catch-handler
703 /// throw "error"; 734 /// throw "error";
704 /// next = [3]; 735 /// next = [3];
705 /// // After the finally (2) continue normally after the try. 736 /// // After the finally (2) continue normally after the try.
706 /// goto = 2; 737 /// goto = 2;
707 /// break; 738 /// break;
708 /// case 1: // (implicit) catch handler for inner try. 739 /// case 1: // (implicit) catch handler for inner try.
709 /// next = [3]; // destination after the finally. 740 /// next = [3]; // destination after the finally.
710 /// // fall-though to the finally handler. 741 /// // fall-though to the finally handler.
711 /// case 2: // finally for inner try 742 /// case 2: // finally for inner try
712 /// handler = 4; // catch-handler for outer try. 743 /// handler = 4; // catch-handler for outer try.
713 /// finalize1(); 744 /// finalize1();
714 /// goto = next.pop(); 745 /// goto = next.pop();
715 /// break; 746 /// break;
716 /// case 3: // exiting inner try. 747 /// case 3: // exiting inner try.
717 /// next = [6]; 748 /// next = [6];
718 /// goto = 5; // finally handler for outer try. 749 /// goto = 5; // finally handler for outer try.
719 /// break; 750 /// break;
720 /// case 4: // catch handler for outer try. 751 /// case 4: // catch handler for outer try.
721 /// e = result; 752 /// handler = 5; // If we throw in this handler, do the finally
floitsch 2015/02/13 15:30:06 ? // If the handler throws, do the finally ... //
floitsch 2015/02/13 15:30:07 ? // If the handler throws, do the finally ... //
sigurdm 2015/02/17 08:43:10 Acknowledged.
sigurdm 2015/02/17 08:43:10 Done.
753 /// next = [8] // And rethrow.
754 /// e = storedError;
722 /// handle(e); 755 /// handle(e);
723 /// // Fall through to finally. 756 /// // Fall through to finally.
724 /// case 5: // finally handler for outer try. 757 /// case 5: // finally handler for outer try.
725 /// handler = null; 758 /// handler = null;
726 /// finalize2(); 759 /// finalize2();
727 /// goto = next.pop(); 760 /// goto = next.pop();
728 /// break; 761 /// break;
729 /// case 6: // Exiting outer try. 762 /// case 6: // Exiting outer try.
730 /// case 7: // return 763 /// case 7: // return
731 /// return thenHelper(returnValue, null, completer, null); 764 /// return thenHelper(returnValue, 0, completer);
765 /// case 8: // Rethrow
766 /// return thenHelper(storedError, 1, completer);
732 /// } 767 /// }
733 /// } catch (error) { 768 /// } catch (error) {
734 /// result = error; 769 /// storedError = error;
735 /// goto = handler; 770 /// goto = handler;
736 /// } 771 /// }
737 /// } 772 /// }
738 /// return thenHelper(null, helper, completer, null); 773 /// return thenHelper(null, helper, completer);
739 /// } 774 /// }
740 /// } 775 /// }
741 /// 776 ///
742 @override 777 @override
743 js.Expression visitFun(js.Fun node) { 778 js.Expression visitFun(js.Fun node) {
744 if (isSync) return node; 779 if (isSync) return node;
745 780
746 beginLabel(newLabel("Function start")); 781 beginLabel(newLabel("Function start"));
747 // AsyncStar needs a returnlabel for its handling of cancelation. See 782 // AsyncStar needs a returnlabel for its handling of cancelation. See
748 // [visitDartYield]. 783 // [visitDartYield].
749 exitLabel = 784 exitLabel =
750 analysis.hasExplicitReturns || isAsyncStar ? newLabel("return") : null; 785 analysis.hasExplicitReturns || isAsyncStar ? newLabel("return") : null;
786 rethrowLabel = newLabel("rethrow");
787 handlerLabels[node] = rethrowLabel;
751 js.Statement body = node.body; 788 js.Statement body = node.body;
752 targetsAndTries.add(node); 789 targetsAndTries.add(node);
753 visitStatement(body); 790 visitStatement(body);
754 targetsAndTries.removeLast(); 791 targetsAndTries.removeLast();
755 addExit(); 792 addExit();
756 793
757 List<js.SwitchClause> clauses = labelledParts.keys.map((label) { 794 List<js.SwitchClause> clauses = labelledParts.keys.map((label) {
758 return new js.Case(js.number(label), new js.Block(labelledParts[label])); 795 return new js.Case(js.number(label), new js.Block(labelledParts[label]));
759 }).toList(); 796 }).toList();
760 js.Statement helperBody = 797 js.Statement helperBody =
761 new js.Switch(new js.VariableUse(gotoName), clauses); 798 new js.Switch(new js.VariableUse(gotoName), clauses);
762 if (hasJumpThoughOuterLabel) { 799 if (hasJumpThoughOuterLabel) {
763 helperBody = js.js.statement("$outerLabelName: #", [helperBody]); 800 helperBody = js.js.statement("$outerLabelName: #", [helperBody]);
764 } 801 }
765 if (hasTryBlocks) { 802 helperBody = js.js.statement("""
766 helperBody = js.js.statement(""" 803 try {
767 try { 804 #body
768 #body 805 } catch ($errorName){
769 } catch ($errorName){ 806 $storedErrorName = $errorName;
770 if ($handlerName === null) 807 $gotoName = $handlerName;
771 throw $errorName; 808 }""", {"body": helperBody});
772 $resultName = $errorName;
773 $gotoName = $handlerName;
774 }""", {"body": helperBody});
775 }
776 List<js.VariableInitialization> inits = <js.VariableInitialization>[]; 809 List<js.VariableInitialization> inits = <js.VariableInitialization>[];
777 810
778 js.VariableInitialization makeInit(String name, js.Expression initValue) { 811 js.VariableInitialization makeInit(String name, js.Expression initValue) {
779 return new js.VariableInitialization( 812 return new js.VariableInitialization(
780 new js.VariableDeclaration(name), initValue); 813 new js.VariableDeclaration(name), initValue);
781 } 814 }
782 815
783 inits.add(makeInit(gotoName, js.number(0))); 816 inits.add(makeInit(gotoName, js.number(0)));
784 if (isAsync) { 817 if (isAsync) {
785 inits.add(makeInit(completerName, new js.New(newCompleter, []))); 818 inits.add(makeInit(completerName, new js.New(newCompleter, [])));
786 } else if (isAsyncStar) { 819 } else if (isAsyncStar) {
787 inits.add(makeInit(controllerName, 820 inits.add(makeInit(controllerName,
788 new js.Call(newController, [new js.VariableUse(helperName)]))); 821 new js.Call(newController, [new js.VariableUse(bodyName)])));
789 } 822 }
790 if (hasTryBlocks) { 823 inits.add(makeInit(handlerName, js.number(rethrowLabel)));
791 inits.add(makeInit(handlerName, new js.LiteralNull())); 824 inits.add(makeInit(storedErrorName, null));
792 }
793 if (hasJumpThroughFinally) { 825 if (hasJumpThroughFinally) {
794 inits.add(makeInit(nextName, null)); 826 inits.add(makeInit(nextName, null));
795 } 827 }
796 if (analysis.hasExplicitReturns && isAsync) { 828 if (analysis.hasExplicitReturns && isAsync) {
797 inits.add(makeInit(returnValueName, null)); 829 inits.add(makeInit(returnValueName, null));
798 } 830 }
831 if (isSyncStar) {
832 inits.add(makeInit(resultName, null));
833 }
799 if (analysis.hasThis && !isSyncStar) { 834 if (analysis.hasThis && !isSyncStar) {
800 // Sync* functions must remember `this` on the level of the outer 835 // Sync* functions must remember `this` on the level of the outer
801 // function. 836 // function.
802 inits.add(makeInit(selfName, new js.This())); 837 inits.add(makeInit(selfName, new js.This()));
803 } 838 }
804 inits.addAll(localVariables.map((js.VariableDeclaration decl) { 839 inits.addAll(localVariables.map((js.VariableDeclaration decl) {
805 return new js.VariableInitialization(decl, null); 840 return new js.VariableInitialization(decl, null);
806 })); 841 }));
807 inits.addAll(new Iterable.generate(tempVarHighWaterMark, 842 inits.addAll(new Iterable.generate(tempVarHighWaterMark,
808 (int i) => makeInit(useTempVar(i + 1).name, null))); 843 (int i) => makeInit(useTempVar(i + 1).name, null)));
809 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits); 844 js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits);
810 if (isSyncStar) { 845 if (isSyncStar) {
floitsch 2015/02/13 15:30:06 Not necessarily this CL, but explain why this is d
sigurdm 2015/02/17 08:43:09 Added todo
811 return js.js(""" 846 return js.js("""
812 function (#params) { 847 function (#params) {
813 if (#needsThis) 848 if (#needsThis)
814 var $selfName = this; 849 var $selfName = this;
815 return new #newIterable(function () { 850 return new #newIterable(function () {
816 #varDecl; 851 #varDecl;
817 return function $helperName($resultName) { 852 return function $bodyName() {
818 while (true) 853 while (true)
819 #helperBody; 854 #helperBody;
820 }; 855 };
821 }); 856 });
822 } 857 }
823 """, { 858 """, {
824 "params": node.params, 859 "params": node.params,
825 "needsThis": analysis.hasThis, 860 "needsThis": analysis.hasThis,
826 "helperBody": helperBody, 861 "helperBody": helperBody,
827 "varDecl": varDecl, 862 "varDecl": varDecl,
828 "newIterable": newIterable 863 "newIterable": newIterable
829 }); 864 });
830 } 865 }
831 return js.js(""" 866 return js.js("""
832 function (#params) { 867 function (#params) {
833 #varDecl; 868 #varDecl;
834 function $helperName($resultName) { 869 function $bodyName($errorCodeName, $resultName) {
870 if (#hasYield)
871 switch ($errorCodeName) {
872 case ${error_codes.STREAM_WAS_CANCELED}:
873 $nextName = $yieldNextName;
874 // fallthrough
floitsch 2015/02/13 15:30:06 Why fall through? Is it to avoid repeating the $go
sigurdm 2015/02/17 08:43:09 Only for the handler. I changed the code, so it do
875 case ${error_codes.ERROR}:
876 $storedErrorName = $resultName;
877 $gotoName = $handlerName;
878 }
879 else
880 if ($errorCodeName == ${error_codes.ERROR}) {
881 $storedErrorName = $resultName;
882 $gotoName = $handlerName;
883 }
835 while (true) 884 while (true)
836 #helperBody; 885 #helperBody;
837 } 886 }
838 #init; 887 #init;
839 }""", { 888 }""", {
840 "params": node.params, 889 "params": node.params,
890 "varDecl": varDecl,
891 "hasYield": analysis.hasYield,
841 "helperBody": helperBody, 892 "helperBody": helperBody,
842 "varDecl": varDecl,
843 "init": generateInitializer() 893 "init": generateInitializer()
844 }); 894 });
845 } 895 }
846 896
847 @override 897 @override
848 js.Expression visitAccess(js.PropertyAccess node) { 898 js.Expression visitAccess(js.PropertyAccess node) {
849 return withExpression2(node.receiver, node.selector, 899 return withExpression2(node.receiver, node.selector,
850 (receiver, selector) => new js.PropertyAccess(receiver, selector)); 900 (receiver, selector) => new js.PropertyAccess(receiver, selector));
851 } 901 }
852 902
(...skipping 28 matching lines...) Expand all
881 ], (evaluated) { 931 ], (evaluated) {
882 return new js.Assignment.compound( 932 return new js.Assignment.compound(
883 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op, 933 new js.PropertyAccess(evaluated[0], evaluated[1]), node.op,
884 evaluated[2]); 934 evaluated[2]);
885 }); 935 });
886 } else { 936 } else {
887 throw "Unexpected assignment left hand side $leftHandSide"; 937 throw "Unexpected assignment left hand side $leftHandSide";
888 } 938 }
889 } 939 }
890 940
891 /// An await is translated to a call to [thenHelper]/[streamHelper]. 941 /// An await is translated to a call to [asyncHelper]/[streamHelper].
892 /// 942 ///
893 /// See the comments of [visitFun] for an example. 943 /// See the comments of [visitFun] for an example.
894 @override 944 @override
895 js.Expression visitAwait(js.Await node) { 945 js.Expression visitAwait(js.Await node) {
896 assert(isAsync || isAsyncStar); 946 assert(isAsync || isAsyncStar);
897 int afterAwait = newLabel("returning from await."); 947 int afterAwait = newLabel("returning from await.");
898 withExpression(node.expression, (js.Expression value) { 948 withExpression(node.expression, (js.Expression value) {
899 addStatement(setGotoVariable(afterAwait)); 949 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(""" 950 addStatement(js.js.statement("""
909 return #thenHelper(#value, 951 return #thenHelper(#value,
910 $helperName, 952 $bodyName,
911 ${isAsync ? completerName : controllerName}, 953 ${isAsync ? completerName : controllerName});
912 #errorCallback);
913 """, { 954 """, {
914 "thenHelper": isAsync ? thenHelper : streamHelper, 955 "thenHelper": isAsync ? asyncHelper : streamHelper,
floitsch 2015/02/13 15:30:07 rename "thenHelper" to "asyncHelper" ?
sigurdm 2015/02/17 08:43:10 Done.
915 "value": value, 956 "value": value,
916 "errorCallback": errorCallback
917 })); 957 }));
918 }, store: false); 958 }, store: false);
919 beginLabel(afterAwait); 959 beginLabel(afterAwait);
920 return new js.VariableUse(resultName); 960 return new js.VariableUse(resultName);
921 } 961 }
922 962
923 /// Checks if [node] is the variable named [resultName]. 963 /// Checks if [node] is the variable named [resultName].
924 /// 964 ///
925 /// [resultName] is used to hold the result of a transformed computation 965 /// [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 966 /// for example the result of awaiting, or the result of a conditional or
(...skipping 589 matching lines...) Expand 10 before | Expand all | Expand 10 after
1516 return new js.VariableUse(selfName); 1556 return new js.VariableUse(selfName);
1517 } 1557 }
1518 1558
1519 @override 1559 @override
1520 void visitThrow(js.Throw node) { 1560 void visitThrow(js.Throw node) {
1521 withExpression(node.expression, (js.Expression expression) { 1561 withExpression(node.expression, (js.Expression expression) {
1522 addStatement(new js.Throw(expression)); 1562 addStatement(new js.Throw(expression));
1523 }, store: false); 1563 }, store: false);
1524 } 1564 }
1525 1565
1526 setErrorHandler() { 1566 setErrorHandler([int errorHandler]) {
1527 addExpressionStatement(new js.Assignment( 1567 addExpressionStatement(new js.Assignment(
1528 new js.VariableUse(handlerName), currentErrorHandler)); 1568 new js.VariableUse(handlerName),
1569 errorHandler == null ? currentErrorHandler : js.number(errorHandler)));
1529 } 1570 }
1530 1571
1531 @override 1572 List<int> _finalliesAndEnclosingHandler() {
1573 List<int> result = new List<int>();
1574 for (int i = targetsAndTries.length - 1; i >= 0; i--) {
1575 js.Node node = targetsAndTries[i];
1576 int handlerLabel = handlerLabels[node];
1577 if (handlerLabel != null) {
1578 result.add(handlerLabel);
1579 break;
1580 }
1581 if (node is js.Try) {
1582 assert(node.finallyPart != null);
1583 result.add(finallyLabels[node]);
1584 }
1585 }
1586 return result.reversed.toList();
1587 }
1588
1589 /// See the comments of [visitFun] for more explanation.
1532 void visitTry(js.Try node) { 1590 void visitTry(js.Try node) {
1533 if (!shouldTransform(node)) { 1591 if (!shouldTransform(node)) {
1534 js.Block body = translateInBlock(node.body); 1592 js.Block body = translateInBlock(node.body);
1535 js.Catch catchPart = (node.catchPart == null) 1593 js.Catch catchPart = (node.catchPart == null)
1536 ? null 1594 ? null
1537 : new js.Catch(node.catchPart.declaration, 1595 : new js.Catch(node.catchPart.declaration,
1538 translateInBlock(node.catchPart.body)); 1596 translateInBlock(node.catchPart.body));
1539 js.Block finallyPart = (node.finallyPart == null) 1597 js.Block finallyPart = (node.finallyPart == null)
1540 ? null 1598 ? null
1541 : translateInBlock(node.finallyPart); 1599 : translateInBlock(node.finallyPart);
1542 addStatement(new js.Try(body, catchPart, finallyPart)); 1600 addStatement(new js.Try(body, catchPart, finallyPart));
1543 return; 1601 return;
1544 } 1602 }
1603
1545 hasTryBlocks = true; 1604 hasTryBlocks = true;
1546 int handlerLabel = newLabel("catch"); 1605 int handlerLabel = newLabel("catch");
1547 int finallyLabel = newLabel("finally"); 1606 int finallyLabel = newLabel("finally");
1548 int afterFinallyLabel = newLabel("after finally"); 1607 int afterFinallyLabel = newLabel("after finally");
1549 errorHandlerLabels.add(handlerLabel); 1608 errorHandlerLabels.add(handlerLabel);
1550 // Set the error handler here. It must be cleared on every path out; 1609 // Set the error handler here. It must be cleared on every path out;
1551 // normal and error exit. 1610 // normal and error exit.
1552 setErrorHandler(); 1611 setErrorHandler();
1553 if (node.finallyPart != null) { 1612 if (node.finallyPart != null) {
1554 finallyLabels[node] = finallyLabel; 1613 finallyLabels[node] = finallyLabel;
1555 targetsAndTries.add(node); 1614 targetsAndTries.add(node);
1556 } 1615 }
1557 visitStatement(node.body); 1616 visitStatement(node.body);
1558 errorHandlerLabels.removeLast(); 1617 errorHandlerLabels.removeLast();
1559 addStatement( 1618 addStatement(
1560 js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)])); 1619 js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)]));
1561 if (node.finallyPart == null) { 1620 if (node.finallyPart == null) {
1562 setErrorHandler(); 1621 setErrorHandler();
1563 addGoto(afterFinallyLabel); 1622 addGoto(afterFinallyLabel);
1564 } else { 1623 } else {
1565 // The handler is set as the first thing in the finally block. 1624 // The handler is set as the first thing in the finally block.
1566 addGoto(finallyLabel); 1625 addGoto(finallyLabel);
1567 } 1626 }
1568 beginLabel(handlerLabel); 1627 beginLabel(handlerLabel);
1569 if (node.catchPart != null) { 1628 if (node.catchPart != null) {
1570 setErrorHandler(); 1629 if (node.finallyPart == null) {
1630 setErrorHandler();
1631 } else {
1632 // If an error is thrown in the catch-part, and there is a finally
1633 // block, goto that.
1634 // And from that finally block, go to all enclosing finally blocks until
floitsch 2015/02/13 15:30:06 If the catch-part throws visit the finally block f
sigurdm 2015/02/17 08:43:09 Done.
1635 // the first enclosing catch-handler, and go to those.
1636 List<int> finallies = _finalliesAndEnclosingHandler();
1637 setErrorHandler(finallies.removeLast());
1638 addStatement(js.js.statement("$nextName = #;",
1639 [new js.ArrayInitializer(finallies.map(js.number).toList())]));
1640 }
1571 // The catch declaration name can shadow outer variables, so a fresh name 1641 // The catch declaration name can shadow outer variables, so a fresh name
1572 // is needed to avoid collisions. See Ecma 262, 3rd edition, 1642 // is needed to avoid collisions. See Ecma 262, 3rd edition,
1573 // section 12.14. 1643 // section 12.14.
1574 String errorRename = freshName(node.catchPart.declaration.name); 1644 String errorRename = freshName(node.catchPart.declaration.name);
1575 localVariables.add(new js.VariableDeclaration(errorRename)); 1645 localVariables.add(new js.VariableDeclaration(errorRename));
1576 variableRenamings 1646 variableRenamings
1577 .add(new Pair(node.catchPart.declaration.name, errorRename)); 1647 .add(new Pair(node.catchPart.declaration.name, errorRename));
1578 addExpressionStatement(new js.Assignment( 1648 addExpressionStatement(new js.Assignment(
1579 new js.VariableUse(errorRename), new js.VariableUse(resultName))); 1649 new js.VariableUse(errorRename),
1650 new js.VariableUse(storedErrorName)));
1580 visitStatement(node.catchPart.body); 1651 visitStatement(node.catchPart.body);
1581 variableRenamings.removeLast(); 1652 variableRenamings.removeLast();
1582 } 1653 }
1654
1583 if (node.finallyPart != null) { 1655 if (node.finallyPart != null) {
1584 targetsAndTries.removeLast(); 1656 targetsAndTries.removeLast();
1585 setErrorHandler(); 1657 setErrorHandler();
1586 // This belongs to the catch-part, but is only needed if there is a 1658 // This belongs to the catch-part, but is only needed if there is a
1587 // `finally`. Therefore it is in this branch. 1659 // `finally`. Therefore it is in this branch.
1588 // This is needed even if there is no explicit catch-branch, because 1660 // 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. 1661 // if an exception is raised the finally part has to be run.
1590 addStatement( 1662 addStatement(
1591 js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)])); 1663 js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)]));
1592 beginLabel(finallyLabel); 1664 beginLabel(finallyLabel);
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
1676 new js.Return(new js.Call(yieldStarExpression, [expression]))); 1748 new js.Return(new js.Call(yieldStarExpression, [expression])));
1677 } else { 1749 } else {
1678 addStatement(new js.Return(expression)); 1750 addStatement(new js.Return(expression));
1679 } 1751 }
1680 } 1752 }
1681 1753
1682 /// Translates a yield/yield* in an async* function. 1754 /// Translates a yield/yield* in an async* function.
1683 /// 1755 ///
1684 /// yield/yield* in an async* function is translated much like the `await` is 1756 /// yield/yield* in an async* function is translated much like the `await` is
1685 /// translated in [visitAwait], only the object is wrapped in a 1757 /// translated in [visitAwait], only the object is wrapped in a
1686 /// [yieldExpression]/[yieldStarExpression] to let [streamHelper] distinguish. 1758 /// [yieldExpression]/[yieldStarExpression] to let [asyncStarHelper]
1687 /// 1759 /// distinguish.
floitsch 2015/02/13 15:30:06 distinguish them.
sigurdm 2015/02/17 08:43:09 Done.
1688 /// Because there is no Future that can fail (as there is in await) null is
1689 /// passed as the errorCallback.
1690 void addAsyncYield(js.DartYield node, js.Expression expression) { 1760 void addAsyncYield(js.DartYield node, js.Expression expression) {
1691 assert(isAsyncStar); 1761 assert(isAsyncStar);
1692 // Find all the finally blocks that should be performed if the stream is 1762 // Find all the finally blocks that should be performed if the stream is
1693 // canceled during the yield. 1763 // canceled during the yield.
1694 // At the bottom of the stack is the return label. 1764 // At the bottom of the stack is the return label.
1695 List<int> enclosingFinallyLabels = <int>[exitLabel]; 1765 List<int> enclosingFinallyLabels = <int>[exitLabel];
1696 enclosingFinallyLabels.addAll(targetsAndTries 1766 enclosingFinallyLabels.addAll(targetsAndTries
1697 .where((js.Node node) => node is js.Try) 1767 .where((js.Node node) => node is js.Try)
1698 .map((js.Try node) => finallyLabels[node])); 1768 .map((js.Try node) => finallyLabels[node]));
1699 int destinationOnCancel = enclosingFinallyLabels.removeLast(); 1769 setErrorHandler(enclosingFinallyLabels.removeLast());
sigurdm 2015/02/17 08:43:09 I realized that I forget to reset the error-handle
1700 js.ArrayInitializer finallyListInitializer = new js.ArrayInitializer( 1770 addStatement(js.js.statement("$yieldNextName = #",
1701 enclosingFinallyLabels.map(js.number).toList()); 1771 [new js.ArrayInitializer(enclosingFinallyLabels.map(js.number)
1772 .toList())]));
1702 addStatement(js.js.statement(""" 1773 addStatement(js.js.statement("""
1703 return #streamHelper(#yieldExpression(#expression), 1774 return #streamHelper(#yieldExpression(#expression),
1704 $helperName, $controllerName, function () { 1775 $bodyName, $controllerName);""", {
1705 if (#notEmptyFinallyList)
1706 $nextName = #finallyList;
1707 $gotoName = #destinationOnCancel;
1708 $helperName();
1709 });""", {
1710 "streamHelper": streamHelper, 1776 "streamHelper": streamHelper,
1711 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression, 1777 "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression,
1712 "expression": expression, 1778 "expression": expression,
1713 "notEmptyFinallyList": enclosingFinallyLabels.isNotEmpty,
1714 "finallyList": finallyListInitializer,
1715 "destinationOnCancel": js.number(destinationOnCancel)
1716 })); 1779 }));
1717 } 1780 }
1718 1781
1719 @override 1782 @override
1720 void visitDartYield(js.DartYield node) { 1783 void visitDartYield(js.DartYield node) {
1721 assert(isSyncStar || isAsyncStar); 1784 assert(isSyncStar || isAsyncStar);
1722 int label = newLabel("after yield"); 1785 int label = newLabel("after yield");
1723 // Don't do a break here for the goto, but instead a return in either 1786 // Don't do a break here for the goto, but instead a return in either
1724 // addSynYield or addAsyncYield. 1787 // addSynYield or addAsyncYield.
1725 withExpression(node.expression, (js.Expression expression) { 1788 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>(); 1809 Map<js.Node, js.Node> targets = new Map<js.Node, js.Node>();
1747 List<js.Node> loopsAndSwitches = new List<js.Node>(); 1810 List<js.Node> loopsAndSwitches = new List<js.Node>();
1748 List<js.LabeledStatement> labelledStatements = 1811 List<js.LabeledStatement> labelledStatements =
1749 new List<js.LabeledStatement>(); 1812 new List<js.LabeledStatement>();
1750 Set<String> usedNames = new Set<String>(); 1813 Set<String> usedNames = new Set<String>();
1751 1814
1752 bool hasExplicitReturns = false; 1815 bool hasExplicitReturns = false;
1753 1816
1754 bool hasThis = false; 1817 bool hasThis = false;
1755 1818
1819 bool hasYield = false;
1820
1756 // The function currently being analyzed. 1821 // The function currently being analyzed.
1757 js.Fun currentFunction; 1822 js.Fun currentFunction;
1758 1823
1759 // For error messages. 1824 // For error messages.
1760 final Function unsupported; 1825 final Function unsupported;
1761 1826
1762 PreTranslationAnalysis(void this.unsupported(js.Node node)); 1827 PreTranslationAnalysis(void this.unsupported(js.Node node));
1763 1828
1764 bool visit(js.Node node) { 1829 bool visit(js.Node node) {
1765 bool containsAwait = node.accept(this); 1830 bool containsAwait = node.accept(this);
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after
2136 bool visitWhile(js.While node) { 2201 bool visitWhile(js.While node) {
2137 loopsAndSwitches.add(node); 2202 loopsAndSwitches.add(node);
2138 bool condition = visit(node.condition); 2203 bool condition = visit(node.condition);
2139 bool body = visit(node.body); 2204 bool body = visit(node.body);
2140 loopsAndSwitches.removeLast(); 2205 loopsAndSwitches.removeLast();
2141 return condition || body; 2206 return condition || body;
2142 } 2207 }
2143 2208
2144 @override 2209 @override
2145 bool visitDartYield(js.DartYield node) { 2210 bool visitDartYield(js.DartYield node) {
2211 hasYield = true;
2146 visit(node.expression); 2212 visit(node.expression);
2147 return true; 2213 return true;
2148 } 2214 }
2149 } 2215 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/js_backend/backend.dart » ('j') | sdk/lib/_internal/compiler/js_lib/js_helper.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698