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