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

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

Issue 839323003: Implementation of async-await transformation on js ast. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments. Fix setting of the handler in finallies... 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library rewrite_async;
6
7 // TODO(sigurdm): Avoid using variables in templates. It could blow up memory
8 // use.
9
10 import "dart:math" show max;
11 import 'dart:collection';
12
13 import "js.dart" show
14 ArrayHole,
15 ArrayInitializer,
16 Assignment,
17 AsyncModifier,
18 Await,
19 Binary,
20 Blob,
21 Block,
22 Break,
23 Call,
24 Case,
25 Catch,
26 Comment,
27 Conditional,
28 Continue,
29 DartYield,
30 Default,
31 Do,
32 EmptyStatement,
33 Expression,
34 ExpressionStatement,
35 For,
36 ForIn,
37 Fun,
38 FunctionDeclaration,
39 If,
40 InterpolatedExpression,
41 InterpolatedLiteral,
42 InterpolatedParameter,
43 InterpolatedSelector,
44 InterpolatedStatement,
45 LabeledStatement,
46 Literal,
47 LiteralBool,
48 LiteralExpression,
49 LiteralNull,
50 LiteralNumber,
51 LiteralStatement,
52 LiteralString,
53 Loop,
54 NamedFunction,
55 New,
56 Node,
57 NodeVisitor,
58 ObjectInitializer,
59 Parameter,
60 Postfix,
61 Prefix,
62 Program,
63 Property,
64 PropertyAccess,
65 RegExpLiteral,
66 Return,
67 Statement,
68 Switch,
69 SwitchClause,
70 This,
71 Throw,
72 Try,
73 VariableDeclaration,
74 VariableDeclarationList,
75 VariableInitialization,
76 VariableUse,
77 While,
78 js,
79 number;
80
81 import '../util/util.dart';
82 import '../dart2jslib.dart' show DiagnosticListener;
83
84 import "../helpers/helpers.dart";
85
86 /// Rewrites a js Fun with async/sync*/async* functions and await and yield
87 /// (with dart-like semantics) to an equivalent function without these.
88 /// await-for is not handled and must be rewritten before. (Currently handled
89 /// in ssa/builder.dart).
90 ///
91 /// When generating the input to this, special care must be taken that
92 /// parameters to sync* functions that are mutated in the body must be boxed.
93 /// (Currently handled in closure.dart).
94 ///
95 /// Look at [visitFun], [visitDartYield] and [visitAwait] for more explanation.
96 class AsyncRewriter extends NodeVisitor {
97
98 // Local variables are hoisted to the top of the function, so they are
99 // collected here.
100 List<VariableDeclaration> localVariables = new List<VariableDeclaration>();
101
102 Map<Node, int> continueLabels = new Map<Node, int>();
103 Map<Node, int> breakLabels = new Map<Node, int>();
104 Map<Node, int> finallyLabels = new Map<Node, int>();
105 int returnLabel;
106
107 // A stack of all enclosing jump targets (including the function for
108 // representing the target of a return, and all enclosing try-blocks that have
109 // finally part, this way ensuring all the finally blocks between a jump and
110 // its target are run before the jump.
111 List<Node> targetsAndTries = new List<Node>();
112
113 List<int> continueStack = new List<int>();
114 List<int> breakStack = new List<int>();
115 List<int> returnStack = new List<int>();
116
117 List<Pair<String, String>> variableRenamings =
118 new List<Pair<String, String>>();
119
120 PreTranslationAnalysis analysis;
121
122 List<int> errorHandlerLabels = new List<int>();
123
124 final Function safeVariableName;
125
126 // All the <x>Name variables are names of Javascript variables used in the
127 // transformed code.
128
129 /// Contains the result of an awaited expression, or a conditional or
130 /// lazy boolean operator.
131 ///
132 /// It is a parameter to the [helperName] function.
floitsch 2015/02/05 20:17:22 Doesn't really help in understanding what it's for
sigurdm 2015/02/06 14:26:32 Tried to give an example.
floitsch 2015/02/06 14:58:43 Never mind. I was again confused by the similarity
sigurdm 2015/02/06 15:21:20 Acknowledged.
133 String resultName;
floitsch 2015/02/05 20:17:22 Future CL: consider: js.Expression result = unca
sigurdm 2015/02/06 14:26:33 I started doing something like this, but one probl
134
135 /// The name of the inner function that is scheduled to do each await/yield,
136 /// and called to do a new iteation for sync*.
floitsch 2015/02/05 20:17:21 iteration
sigurdm 2015/02/06 14:26:32 Done.
137 String helperName;
138
139 /// The Completer that will finish an async function.
floitsch 2015/02/05 20:17:21 What if we are in a sync* or async* ?
sigurdm 2015/02/06 14:26:33 Done.
140 String completerName;
141
142 /// The StreamController that controls an async* function.
floitsch 2015/02/05 20:17:21 What if we are in a sync* or async function?
sigurdm 2015/02/06 14:26:33 Done.
143 String controllerName;
144
145
146 /// Used to simulate a goto.
147 ///
148 /// To "goto" a label, the label is assigned to this
149 /// variable, and break out of the switch to take another iteration in the
150 /// while loop. See [addGoto]
151 String gotoName;
152
153 /// The label of the current error handler.
154 String handlerName;
155
156 /// Current caught error.
157 String errorName;
158
159 /// A stack of labels of finally blocks to visit, and the label to go to after
160 /// the last.
161 String nextName;
162
163 /// The current returned value (a finally block may overwrite it).
164 String returnValueName;
165
166 /// The label of the outer loop.
167 ///
168 /// Used if there are untransformed loops containing break or continues to
169 /// targets outside the loop.
170 String outerLabelName;
171
172 /// If javascript `this` is used, it is accessed via this variable, in the
173 /// [helperName] function.
174 String selfName;
175
176 // These expressions are hooks for communicating with the runtime.
177
178 /// The function called by an async function to simulate an await or return.
179 ///
180 /// For an await it is called with:
181 ///
182 /// - The value to await
183 /// - The [helperName]
184 /// - The [completerName]
185 /// - A Javascript function that will take the program to the right error
floitsch 2015/02/05 20:17:22 that takes... It's not just taking the program to
sigurdm 2015/02/06 14:26:32 Done.
186 /// handler in case the future completes with an error.
187 ///
188 /// For a return it is called with:
189 ///
190 /// - The value to complete the completer with.
191 /// - null
192 /// - The [completerName]
193 /// - null.
194 final Expression thenHelper;
195
196 /// The function called by an async* function to simulate an await, yield or
197 /// yield*.
198 ///
199 /// For an await/yield/yield* it is called with:
200 ///
201 /// - The value to await/yieldExpression(value to yield)/
202 /// yieldStarExpression(stream to yield)
203 /// - The [helperName]
204 /// - The [completerName]
floitsch 2015/02/05 20:17:20 controllerName ?
sigurdm 2015/02/06 14:26:33 Done.
205 /// - A Javascript function that will take the program to the right error
floitsch 2015/02/05 20:17:21 ditto.
sigurdm 2015/02/06 14:26:32 Done.
206 /// handler in case the future completes with an error.
207 ///
208 /// For a return it is called with:
209 ///
210 /// - null
211 /// - null
212 /// - The [controllerName]
213 /// - null.
214 final Expression streamHelper;
215
216 /// How to initialize the [completerName] variable.
floitsch 2015/02/05 20:17:22 It's not a "how". The constructor that is used to
sigurdm 2015/02/06 14:26:32 Done.
217 ///
218 /// Specific to async methods.
219 final Expression newCompleter;
220
221 /// How to initialize the [controllerName] variable.
floitsch 2015/02/05 20:17:21 ditto.
sigurdm 2015/02/06 14:26:32 Done.
222 ///
223 /// Specific to async* methods.
224 final Expression newController;
floitsch 2015/02/05 20:17:21 It's inconsistent that "newCompleter" and newItera
sigurdm 2015/02/06 14:26:33 true. Made them all constructors.
225
226 /// Creates an Iterable for a sync* method. Called with [helperName]
floitsch 2015/02/05 20:17:22 Missing trailing ".".
sigurdm 2015/02/06 14:26:33 Done.
227 final Expression newIterable;
228
229 /// Creates a marker showing that iteration is over.
floitsch 2015/02/05 20:17:21 A JS Expression that creates ...
sigurdm 2015/02/06 14:26:32 Done.
230 ///
231 /// Called without arguments.
232 final Expression endOfIteration;
233
234 /// Creates a marker indicating a 'yield' statement.
floitsch 2015/02/05 20:17:21 ditto.
sigurdm 2015/02/06 14:26:33 Done.
235 ///
236 /// Called with the value to yield.
237 final Expression yieldExpression;
238
239 /// Creates a marker indication a 'yield*' statement.
floitsch 2015/02/05 20:17:21 ditto.
sigurdm 2015/02/06 14:26:34 Done.
240 ///
241 /// Called with the stream to yield from.
242 final Expression yieldStarExpression;
243
244 final DiagnosticListener diagnosticListener;
245 // For error reporting only.
246 Spannable get spannable {
247 return (_spannable == null) ? NO_LOCATION_SPANNABLE : _spannable;
248 }
249
250 Spannable _spannable;
251
252 int _currentLabel = 0;
253
254 // The highest temporary variable index currently in use.
255 int currentTempVarIndex = 0;
256 // The highest temporary variable index ever in use in this function.
257 int tempVarHighWaterMark = 0;
258 Map<int, Expression> tempVarNames = new Map<int, Expression>();
259
260 AsyncModifier async;
261
262 bool get isSync => async == const AsyncModifier.sync();
263 bool get isAsync => async == const AsyncModifier.async();
264 bool get isSyncStar => async == const AsyncModifier.syncStar();
265 bool get isAsyncStar => async == const AsyncModifier.asyncStar();
266
267 AsyncRewriter(this.diagnosticListener,
268 spannable,
269 {this.thenHelper,
270 this.streamHelper,
271 this.newCompleter,
272 this.newController,
273 this.endOfIteration,
274 this.newIterable,
275 this.yieldExpression,
276 this.yieldStarExpression,
277 this.safeVariableName})
278 : _spannable = spannable;
279
280 /// Main entry point.
281 /// Rewrites a sync*/async/async* function to an equivalent normal function.
282 ///
283 /// [spannable] can be passed to have a location for error messages.
284 Fun rewrite(Fun node, [Spannable spannable]) {
285 _spannable = spannable;
286
287 async = node.asyncModifier;
288 assert(!isSync);
289
290 analysis = new PreTranslationAnalysis(unsupported);
291 analysis.analyze(node);
292
293 // To avoid name collisions with existing names, the fresh names are
294 // generated after the analysis.
295 resultName = freshName("result");
296 completerName = freshName("completer");
297 controllerName = freshName("controller");
298 helperName = freshName("helper");
299 gotoName = freshName("goto");
300 handlerName = freshName("handler");
301 errorName = freshName("error");
302 nextName = freshName("next");
303 returnValueName = freshName("returnValue");
304 outerLabelName = freshName("outer");
305 selfName = freshName("self");
306
307 return node.accept(this);
308 }
309
310 Expression get currentErrorHandler {
311 return errorHandlerLabels.isEmpty ?
312 new LiteralNull() :
313 number(errorHandlerLabels.last);
314 }
315
316 int allocateTempVar() {
317 assert(tempVarHighWaterMark >= currentTempVarIndex);
318 currentTempVarIndex++;
319 tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark);
320 return currentTempVarIndex;
321 }
322
323 VariableUse useTempVar(int i) {
324 return tempVarNames.putIfAbsent(i, () =>
325 new VariableUse(freshName("temp$i")));
326 }
327
328 /// Generates a variable name with [safeVariableName] based on [originalName]
329 /// with a suffix to guarantee it does not collide with already used names.
330 String freshName(String originalName) {
331 String safeName = safeVariableName(originalName);
332 String result = safeName;
333 int counter = 1;
334 while (analysis.usedNames.contains(result)) {
335 result = "$safeName$counter";
336 ++counter;
337 }
338 analysis.usedNames.add(result);
339 return result;
340 }
341
342 /// All the pieces are collected in this map, to create a switch with a case
343 /// for each label.
344 ///
345 /// The order is important, therefore the type is explicitly LinkedHashMap.
346 LinkedHashMap<int, List<Statement>> labelledParts =
347 new LinkedHashMap<int, List<Statement>>();
348
349 /// Description of each label for readability of the non-minified output.
350 Map<int, String> labelComments = new Map<int, String>();
351
352 /// True if the function has any try blocks containing await.
353 bool hasTryBlocks = false;
354
355 /// True if any return, break or continue passes through a finally.
356 bool hasJumpThroughFinally = false;
357
358 /// True if the traversion currently is inside a loop or switch for which
359 /// [shouldTransform] is false.
360 bool insideUntranslatedBreakable = false;
361
362 /// True if a label is used to break to an outer switch-statement.
363 bool hasJumpThoughOuterLabel = false;
364
365 /// Buffer for collecting translated statements belonging to the same switch
366 /// case.
367 List<Statement> currentStatementBuffer;
368
369 // Labels will become cases in the big switch expression, and `goto label`
370 // is expressed by assigning to the switch key [gotoName] and breaking out of
371 // the switch.
372
373 int newLabel([String comment]) {
374 int result = _currentLabel;
375 _currentLabel++;
376 if (comment != null) {
377 labelComments[result] = comment;
378 }
379 return result;
380 }
381
382 /// Begins outputting statements to a new buffer with label [label].
383 ///
384 /// Each buffer ends up as its own case part in the big state-switch.
385 void beginLabel(int label) {
386 assert(!labelledParts.containsKey(label));
387 currentStatementBuffer = new List<Statement>();
388 labelledParts[label] = currentStatementBuffer;
389 addStatement(new Comment(labelComments[label]));
390 }
391
392 /// Returns a statement assigning to the variable named [gotoName].
393 /// This should be followed by a break for the goto to be executed. Use
394 /// [gotoWithBreak] or [addGoto] for this.
395 Statement setGotoVariable(int label) {
396 return new ExpressionStatement(
397 new Assignment(new VariableUse(gotoName), number(label)));
398 }
399
400 /// Return a block that has a goto to [label] including the break.
floitsch 2015/02/05 20:17:22 Returns
sigurdm 2015/02/06 14:26:33 Done.
401 ///
402 /// Also inserts a comment describing the label if available.
403 Block gotoAndBreak(int label) {
404 List<Statement> statements = new List<Statement>();
405 if (labelComments.containsKey(label)) {
406 statements.add(new Comment("goto ${labelComments[label]}"));
407 }
408 statements.add(setGotoVariable(label));
409 if (insideUntranslatedBreakable) {
410 hasJumpThoughOuterLabel = true;
411 statements.add(new Break(outerLabelName));
412 } else {
413 statements.add(new Break(null));
414 }
415 return new Block(statements);
416 }
417
418 /// Add a goto to [label] including the break.
floitsch 2015/02/05 20:17:22 Adds
sigurdm 2015/02/06 14:26:33 Done.
419 ///
420 /// Also inserts a comment describing the label if available.
421 void addGoto(int label) {
422 if (labelComments.containsKey(label)) {
423 addStatement(new Comment("goto ${labelComments[label]}"));
424 }
425 addStatement(setGotoVariable(label));
426
427 addBreak();
428 }
429
430 void addStatement(Statement node) {
431 currentStatementBuffer.add(node);
432 }
433
434 void addExpressionStatement(Expression node) {
435 addStatement(new ExpressionStatement(node));
436 }
437
438 /// True if there is an await or yield in [node] or some subexpression.
439 bool shouldTransform(Node node) {
440 return analysis.hasAwaitOrYield.contains(node);
441 }
442
443 void unsupported(Node node) {
444 throw new UnsupportedError(
445 "Node $node cannot be transformed by the await-sync transformer");
446 }
447
448 void unreachable(Node node) {
449 diagnosticListener.internalError(spannable,
450 "Internal error, trying to visit $node");
451 }
452
453 visitStatement(Statement node) {
454 node.accept(this);
455 }
456
457 /// Visits [node] to ensure its sideeffects are performed, but throwing away
458 /// the result.
459 ///
460 /// If the return value of visiting [node] is an expression guaranteed to have
461 /// no side effect, it is dropped.
462 void visitExpressionIgnoreResult(Expression node) {
463 Expression result = node.accept(this);
464 if (!(result is Literal || result is VariableUse)) {
465 addExpressionStatement(result);
466 }
467 }
468
469 Expression visitExpression(Expression node) {
470 return node.accept(this);
471 }
472
473 /// Emits an assignment of [result] to a temporary variable unless it can
474 /// be guaranteed that evaluating [result] has no side-effect, and has
475 /// constant value.
476 ///
477 /// Returns the temporary variable or the result itself.
478 ///
479 /// For example:
480 ///
481 /// - _storeIfNecessary(someLiteral) returns someLiteral.
482 /// - _storeIfNecessary(someVariable)
483 /// inserts: var tempX = someVariable
484 /// returns: tempX
485 /// where tempX is a fresh temporary variable.
486 Expression _storeIfNecessary(Expression result) {
487 // Note that RegExes, ArrayInitializer and ObjectInitializer are not
488 // [Literal]s.
489 if (result is Literal) return result;
490 Expression tempVar = useTempVar(allocateTempVar());
491 addExpressionStatement(new Assignment(tempVar, result));
492 return tempVar;
493 }
494
495 withExpression(Expression node, fn(Expression result), {bool store}) {
496 int oldTempVarIndex = currentTempVarIndex;
497 Expression visited = visitExpression(node);
498 if (store) {
499 visited = _storeIfNecessary(visited);
500 }
501 var result = fn(visited);
502 currentTempVarIndex = oldTempVarIndex;
503 return result;
504 }
505
506 /// Calls [fn] with the value of evaluating [node1] and [node2].
507 ///
508 /// If `shouldTransform(node2)` the first expression is stored in a temporary
509 /// variable.
510 ///
511 /// This is because node1 must be evaluated before visiting node2,
floitsch 2015/02/05 20:17:21 I'm not sure I fully understand the comment. My be
sigurdm 2015/02/06 14:26:33 Better. Thanks.
512 /// because the evaluation of an await or yield cannot be expressed as
513 /// an expression, visiting node2 it will output statements that
514 /// might have an influence on the value of node1.
515 withExpression2(Expression node1, Expression node2, fn(
516 Expression result1,
517 Expression result2)) {
518 int oldTempVarIndex = currentTempVarIndex;
519 Expression r1 = visitExpression(node1);
520 if (shouldTransform(node2)) {
521 r1 = _storeIfNecessary(r1);
522 }
523 Expression r2 = visitExpression(node2);
524 var result = fn(r1, r2);
525 currentTempVarIndex = oldTempVarIndex;
526 return result;
527 }
528
529 /// Calls [fn] with the value of evaluating all [nodes].
530 ///
531 /// All results before the last node where `shouldTransform(node)` are stored
532 /// in temporary variables.
533 ///
534 /// See more explanation on [withExpression2].
535 ///
536 /// If any of the nodes are null, they are ignored, and a null is passed to
537 /// [fn] in that place.
538 withExpressions(List<Expression> nodes, fn(List<Expression> results)) {
539 int oldTempVarIndex = currentTempVarIndex;
540 // Find last occurence of a 'transform' expression in [nodes].
541 // All expressions before that must be stored in temp-vars.
542 int lastTransformIndex = 0;
543 for (int i = nodes.length - 1; i >= 0; --i) {
544 if (nodes[i] == null) continue;
545 if (shouldTransform(nodes[i])) {
546 lastTransformIndex = i;
547 break;
548 }
549 }
550 List<Node> visited = nodes.take(lastTransformIndex).map((Node node) {
551 return node == null
552 ? null
553 : _storeIfNecessary(visitExpression(node));
554 }).toList();
555 visited.addAll(nodes.skip(lastTransformIndex).map((Node node) {
556 return node == null
557 ? null
558 : visitExpression(node);
559 }));
560 var result = fn(visited);
561 currentTempVarIndex = oldTempVarIndex;
562 return result;
563 }
564
565 /// Emits the return block that all returns should jump to (after going
566 /// through all the enclosing finally blocks) the jump to here is made in
floitsch 2015/02/05 20:17:22 blocks). The jump ...
sigurdm 2015/02/06 14:26:32 Done.
567 /// [visitReturn].
568 ///
569 /// Returning from an async method calls the [thenHelper] with the result.
570 /// (the result might have been stored in [returnValueName] by some finally
571 /// block).
572 ///
573 /// Returning from a sync* function returns an [endOfIteration] marker.
574 ///
575 /// Returning from an async* function calls the [streamHelper] with an
576 /// [endOfIteration] marker.
577 void addReturn() {
floitsch 2015/02/05 20:17:22 addFooter ? addExit ? addFunctionExit ? (`addRetu
sigurdm 2015/02/06 14:26:32 I go with addExit.
578 if (analysis.hasExplicitReturns || isAsyncStar) {
579 beginLabel(returnLabel);
580 } else {
581 addStatement(new Comment("implicit return"));
582 }
583 switch (async) {
584 case const AsyncModifier.async():
585 String returnValue = analysis.hasExplicitReturns
586 ? returnValueName
587 : "null";
588 addStatement(js.statement(
589 "return #thenHelper($returnValue, null, $completerName, null)",
590 {"thenHelper": thenHelper}));
591 break;
592 case const AsyncModifier.syncStar():
593 addStatement(new Return(new Call(endOfIteration, [])));
594 break;
595 case const AsyncModifier.asyncStar():
596 addStatement(js.statement(
597 "return #streamHelper(null, null, $controllerName, null)",
598 {"streamHelper": streamHelper}));
599 break;
600 default:
601 diagnosticListener.internalError(spannable,
602 "Internal error, unexpected asyncmodifier $async");
603 }
604 }
605
606 /// The initial call to [thenHelper]/[streamHelper].
607 ///
608 /// There is no value to await/yield, so the first argument is `null` and
609 /// also the errorCallback is `null`.
610 ///
611 /// Returns the [Future]/[Stream] coming from [completerName]/
612 /// [controllerName].
613 Statement generateInitializer() {
614 if (isAsync) {
615 return js.statement(
616 "return #thenHelper(null, $helperName, $completerName, null);",
617 {"thenHelper": thenHelper});
618 } else if (isAsyncStar) {
619 return js.statement(
620 "return #streamHelper(null, $helperName, $controllerName, null);",
621 {"streamHelper": streamHelper});
622 } else {
623 throw diagnosticListener.internalError(spannable,
624 "Unexpected asyncModifier: $async");
625 }
626 }
627
628 /// Rewrites an async/sync*/async* function to a normal Javascript function.
629 ///
630 /// The control flow is flattened by simulating 'goto' using a switch in a
631 /// loop and a state variable [gotoName] inside a helper-function that can be
632 /// called back by [thenHelper]/[streamHelper]/the [Iterator].
633 ///
634 /// Local variables are hoisted outside the helper.
635 ///
636 /// Awaits and yields in async/async* are simulated by calling
floitsch 2015/02/05 20:17:21 are translated to a call to ... Similarly remove
floitsch 2015/02/05 20:17:21 Maybe: Awaits in async/async* are translated to co
sigurdm 2015/02/06 14:26:33 Done.
sigurdm 2015/02/06 14:26:33 Better, thanks.
637 /// [thenHelper]/[streamHelper].
638 ///
639 /// Yield/yield* in a sync* function is simulated by returning the value.
floitsch 2015/02/05 20:17:21 Yield/yield* in a sync* function is translated to
sigurdm 2015/02/06 14:26:32 Done.
640 ///
641 /// Simplified examples (not the exact translation, but intended to show the
642 /// ideas):
643 ///
644 /// function (x, y, z) async {
645 /// var p = await foo();
646 /// return bar(p);
647 /// }
648 ///
649 /// Becomes:
650 ///
651 /// function(x, y, z) {
652 /// var goto = 0, returnValue, completer = new Completer(), p;
653 /// function helper(result) {
654 /// while (true) {
655 /// switch (goto) {
656 /// case 0:
657 /// goto = 1
floitsch 2015/02/05 20:17:21 Add comment, that "goto = 1" remembers the locatio
sigurdm 2015/02/06 14:26:33 Done.
658 /// return thenHelper(foo(), helper, completer, null);
659 /// case 1:
660 /// p = result;
661 /// returnValue = bar(p);
662 /// goto = 2;
663 /// break;
664 /// case 2:
665 /// return thenHelper(returnValue, null, completer, null)
666 /// }
667 /// }
668 /// return thenHelper(null, helper, completer, null);
669 /// }
670 /// }
671 ///
672 /// Try/catch is implemented by maintaining [handlerName] to contain the label
673 /// of the current handler. The switch is nested inside a try/catch that will
674 /// redirect the flow to the current handler.
675 ///
676 /// Finally is implemented by letting the translation of each flow-path that
floitsch 2015/02/05 20:17:21 A `finally` clause is compiled similar to normal c
floitsch 2015/02/05 20:17:22 `Finally`
sigurdm 2015/02/06 14:26:32 Acknowledged.
sigurdm 2015/02/06 14:26:32 Done.
677 /// will enter a finally set up the variable [nextName] with a stack of
678 /// finally blocks to exit via.
679 ///
680 /// function (x, y, z) async {
681 /// try {
682 /// try {
683 /// throw "error";
684 /// } finally {
685 /// finalize1();
686 /// }
687 /// } catch (e) {
688 /// handle(e);
689 /// } finally {
690 /// finalize2();
691 /// }
692 /// }
693 ///
694 /// Translates into (besides the fact that structures not containing
695 /// await/yield/yield* are left intact):
696 ///
697 /// function(x, y, z) {
698 /// var goto = 0;
699 /// var returnValue;
700 /// var completer = new Completer();
701 /// var handler = null;
702 /// var p;
703 /// function helper(result) {
floitsch 2015/02/05 20:17:23 comment that "result" could be either the value of
sigurdm 2015/02/06 14:26:34 Done.
704 /// while (true) {
705 /// try {
706 /// switch (goto) {
707 /// case 0:
708 /// handler = 4; // The outer catch-handler
709 /// handler = 1; // The inner (implicit) catch-handler
710 /// throw "error";
711 /// next = [3];
floitsch 2015/02/05 20:17:20 // After the finally (2) continue normally after t
sigurdm 2015/02/06 14:26:33 Done.
712 /// goto = 2;
713 /// break;
714 /// case 1: // catch handler for inner try (implicit).
floitsch 2015/02/05 20:17:20 (implicit) catch handler for the inner try.
sigurdm 2015/02/06 14:26:33 Done.
715 /// next = [3]; // destination after the finally.
floitsch 2015/02/05 20:17:22 why is this not "next = [4]" ? If we enter this ex
sigurdm 2015/02/06 14:26:33 Good point, this is a bug indeed.
716 /// // fall-though to the finally handler.
717 /// case 2: // finally for inner try
718 /// handler = 4; // catch-handler for outer try.
719 /// finalize1();
720 /// goto = next.pop();
721 /// break;
722 /// case 3: // exiting inner try.
723 /// next = [6];
724 /// goto = 5; // finally handler for outer try.
floitsch 2015/02/05 20:17:22 Why is there no break?
sigurdm 2015/02/06 14:26:32 It is a mistake.
725 /// case 4: // catch handler for outer try.
726 /// e = result;
727 /// handle(e);
728 /// goto = 4;
floitsch 2015/02/05 20:17:21 why is this here? It looks unnecessary anyway. Eve
sigurdm 2015/02/06 14:26:32 Another mistake
729 /// case 5: // finally handler for outer try.
730 /// handler = null;
731 /// finalize2();
732 /// goto = next.pop();
733 /// break;
734 /// case 6: // Exiting outer try.
735 /// case 7: // return
736 /// return thenHelper(returnValue, null, completer, null);
737 /// }
738 /// } catch (error) {
739 /// result = error;
740 /// goto = handler;
741 /// }
742 /// }
743 /// return thenHelper(null, helper, completer, null);
744 /// }
745 /// }
746 ///
747 @override
748 Expression visitFun(Fun node) {
749 if (isSync) return node;
750
751 beginLabel(newLabel("Function start"));
752 // AsyncStar needs a returnlabel for its handling of cancelation. See
753 // [visitDartYield].
754 returnLabel = analysis.hasExplicitReturns || isAsyncStar
755 ? newLabel("Return")
756 : null;
757 Statement body = node.body;
758 targetsAndTries.add(node);
759 visitStatement(body);
760 targetsAndTries.removeLast();
761 addReturn();
762
763 List<SwitchClause> clauses = labelledParts.keys.map((label) {
764 return new Case(
765 number(label),
766 new Block(labelledParts[label]));
767 }).toList();
768 Statement helperBody = new Switch(new VariableUse(gotoName), clauses);
769 if (hasJumpThoughOuterLabel) {
770 helperBody = js.statement("$outerLabelName: #", [helperBody]);
771 }
772 if (hasTryBlocks) {
773 helperBody = js.statement("""
774 try {
775 #body
776 } catch ($errorName){
777 if ($handlerName === null)
778 throw $errorName;
779 $resultName = $errorName;
780 $gotoName = $handlerName;
781 }""", {"body": helperBody});
782 }
783 List<VariableInitialization> inits = <VariableInitialization>[];
784
785 VariableInitialization makeInit(String name, Expression initValue) {
786 return new VariableInitialization(new VariableDeclaration(name),
787 initValue);
788 }
789
790 inits.add(makeInit(gotoName, number(0)));
791 if (isAsync) {
792 inits.add(makeInit(completerName, new New(newCompleter, [])));
793 } else if (isAsyncStar) {
794 inits.add(makeInit(controllerName,
795 new Call(newController,
796 [new VariableUse(helperName)])));
797 }
798 if (hasTryBlocks) {
799 inits.add(makeInit(handlerName, new LiteralNull()));
800 }
801 if (hasJumpThroughFinally) {
802 inits.add(makeInit(nextName, null));
803 }
804 if (analysis.hasExplicitReturns && isAsync) {
805 inits.add(makeInit(returnValueName, null));
806 }
807 if (analysis.hasThis) {
808 inits.add(makeInit(selfName, new This()));
809 }
810 inits.addAll(localVariables.map(
811 (VariableDeclaration decl) => new VariableInitialization(decl, null)));
812 inits.addAll(new Iterable.generate(tempVarHighWaterMark,
813 (int i) => makeInit(useTempVar(i + 1).name, null)));
814 VariableDeclarationList varDecl = new VariableDeclarationList(inits);
815 if (isSyncStar) {
816 return js("""
817 function (#params) {
818 return new #newIterable(function () {
819 #varDecl;
820 return function $helperName($resultName) {
821 while (true)
822 #helperBody;
823 };
824 });
825 }
826 """, {"params": node.params,
827 "helperBody": helperBody,
828 "varDecl": varDecl,
829 "newIterable": newIterable});
830 }
831 return js("""
832 function (#params) {
833 #varDecl;
834 function $helperName($resultName) {
835 while (true)
836 #helperBody;
837 }
838 #init;
839 }""", {"params": node.params,
840 "helperBody": helperBody,
841 "varDecl": varDecl,
842 "init": generateInitializer()});
843 }
844
845 @override
846 Expression visitAccess(PropertyAccess node) {
847 return withExpression2(
848 node.receiver,
849 node.selector,
850 (receiver, selector) => new PropertyAccess(receiver, selector));
851 }
852
853 @override
854 Expression visitArrayHole(ArrayHole node) {
855 return node;
856 }
857
858 @override
859 Expression visitArrayInitializer(ArrayInitializer node) {
860 return withExpressions(node.elements, (elements) {
861 return new ArrayInitializer(elements);
862 });
863 }
864
865 @override
866 Expression visitAssignment(Assignment node) {
867 if (!shouldTransform(node)) {
868 return new Assignment.compound(visitExpression(node.leftHandSide),
869 node.op,
870 visitExpression(node.value));
871 }
872 Expression leftHandSide = node.leftHandSide;
873 if (leftHandSide is VariableUse) {
874 return withExpression(node.value, (Expression value) {
875 return new Assignment(leftHandSide, value);
876 }, store: false);
877 } else if (leftHandSide is PropertyAccess) {
878 return withExpressions(
879 [leftHandSide.receiver, leftHandSide.selector, node.value],
880 (evaluated) {
881 return new Assignment.compound(
882 new PropertyAccess(evaluated[0], evaluated[1]),
883 node.op,
884 evaluated[2]);
885 });
886 } else {
887 throw "Unexpected assignment left hand side $leftHandSide";
888 }
889 }
890
891 /// An await is translated to a call to [thenHelper]/[streamHelper].
892 ///
893 /// See the comments of [visitFun] for an example.
894 @override
895 Expression visitAwait(Await node) {
896 assert(isAsync || isAsyncStar);
897 int afterAwait = newLabel("returning from await.");
898 withExpression(node.expression, (Expression value) {
899 addStatement(setGotoVariable(afterAwait));
900 Expression errorCallback = errorHandlerLabels.isEmpty
901 ? new LiteralNull()
902 : js("""
903 function($errorName) {
904 $gotoName = #currentHandler;
905 $helperName($errorName);
906 }""", {"currentHandler": currentErrorHandler});
907
908 addStatement(js.statement("""
909 return #thenHelper(#value,
910 $helperName,
911 ${isAsync ? completerName : controllerName},
912 #errorCallback);
913 """, {"thenHelper": isAsync ? thenHelper : streamHelper,
914 "value": value,
915 "errorCallback": errorCallback}));
916 }, store: false);
917 beginLabel(afterAwait);
918 return new VariableUse(resultName);
919 }
920
921 /// Checks if [node] is the variable named [resultName].
922 ///
923 /// [resultName] is used to hold the result of a transformed computation
924 /// for example the result of awaiting, or the result of a conditional or
925 /// short-circuiting expression.
926 /// If the subexpression of some transformed node already is transformed and
927 /// visiting it returns [resultName], it is not redundantly assigned to itself
928 /// again.
929 bool isResult(Expression node) {
930 return node is VariableUse && node.name == resultName;
931 }
932
933 @override
934 Expression visitBinary(Binary node) {
935 if (shouldTransform(node.right) &&
936 (node.op == "||" || node.op == "&&")) {
937 int thenLabel = newLabel("then");
938 int joinLabel = newLabel("join");
939 withExpression(node.left, (Expression left) {
940 Statement assignLeft = isResult(left)
941 ? new Block.empty()
942 : new ExpressionStatement(
943 new Assignment(new VariableUse(resultName), left));
944 if (node.op == "||") {
945 addStatement(new If(left, gotoAndBreak(thenLabel), assignLeft));
946 } else {
947 assert(node.op == "&&");
948 addStatement(new If(left, assignLeft, gotoAndBreak(thenLabel)));
949 }
950 }, store: true);
951 addGoto(joinLabel);
952 beginLabel(thenLabel);
953 withExpression(node.right, (Expression value) {
954 if (!isResult(value)) {
955 addExpressionStatement(
956 new Assignment(new VariableUse(resultName), value));
957 }
958 }, store: false);
959 beginLabel(joinLabel);
960 return new VariableUse(resultName);
961 }
962
963 return withExpression2(
964 node.left,
965 node.right,
966 (left, right) => new Binary(node.op, left, right));
967 }
968
969 @override
970 Expression visitBlob(Blob node) {
971 return node;
972 }
973
974 @override
975 void visitBlock(Block node) {
976 for (Statement statement in node.statements) {
977 visitStatement(statement);
978 }
979 }
980
981 @override
982 void visitBreak(Break node) {
983 Node target = analysis.targets[node];
984 if (!shouldTransform(target)) {
985 addStatement(node);
986 return;
987 }
988 translateJump(target, breakLabels[target]);
989 }
990
991 @override
992 Expression visitCall(Call node) {
993 bool storeTarget = node.arguments.any(shouldTransform);
994 return withExpression(node.target, (target) {
995 return withExpressions(node.arguments, (List<Expression> arguments) {
996 return new Call(target, arguments);
997 });
998 }, store: storeTarget);
999 }
1000
1001 @override
1002 void visitCase(Case node) {
1003 return unreachable(node);
1004 }
1005
1006 @override
1007 void visitCatch(Catch node) {
1008 return unreachable(node);
1009 }
1010
1011 @override
1012 void visitComment(Comment node) {
1013 addStatement(node);
1014 }
1015
1016 @override
1017 Expression visitConditional(Conditional node) {
1018 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
1019 return withExpression(node.condition, (Expression condition) {
1020 return new Conditional(condition, node.then, node.otherwise);
1021 });
1022 }
1023 int thenLabel = newLabel("then");
1024 int joinLabel = newLabel("join");
1025 int elseLabel = newLabel("else");
1026 withExpression(node.condition, (Expression condition) {
1027 addExpressionStatement(
1028 new Assignment(
1029 new VariableUse(gotoName),
1030 new Conditional(
1031 condition,
1032 number(thenLabel),
1033 number(elseLabel))));
1034 }, store: false);
1035 addBreak();
1036 beginLabel(thenLabel);
1037 withExpression(node.then, (Expression value) {
1038 if (!isResult(value)) {
1039 addExpressionStatement(
1040 new Assignment(new VariableUse(resultName), value));
1041 }
1042 }, store: false);
1043 addGoto(joinLabel);
1044 beginLabel(elseLabel);
1045 withExpression(node.otherwise, (Expression value) {
1046 if (!isResult(value)) {
1047 addExpressionStatement(
1048 new Assignment(new VariableUse(resultName), value));
1049 }
1050 }, store: false);
1051 beginLabel(joinLabel);
1052 return new VariableUse(resultName);
1053 }
1054
1055 @override
1056 void visitContinue(Continue node) {
1057 Node target = analysis.targets[node];
1058 if (!shouldTransform(target)) {
1059 addStatement(node);
1060 return;
1061 }
1062 translateJump(target, continueLabels[target]);
1063 }
1064
1065 /// Emits a break statement that exits the big switch statement.
1066 void addBreak() {
1067 if (insideUntranslatedBreakable) {
1068 hasJumpThoughOuterLabel = true;
1069 addStatement(new Break(outerLabelName));
1070 } else {
1071 addStatement(new Break(null));
1072 }
1073 }
1074
1075 /// Common code for handling break, continue, return.
1076 ///
1077 /// It is necessary to run all nesting finally-handlers between the jump and
1078 /// the target. For that [nextName] is used as a stack of places to go.
floitsch 2015/02/05 20:17:21 Maybe reference visitFun.
sigurdm 2015/02/06 14:26:32 Done.
1079 void translateJump(Node target, int targetLabel) {
1080 // Compute a stack of all the 'finally' nodes that must be visited before
1081 // the jump.
1082 // The bottom of the stack is the label where the jump goes to.
1083 List<int> jumpStack = new List<int>();
1084 for (Node node in targetsAndTries.reversed) {
1085 if (node is Try) {
1086 assert(node.finallyPart != null);
1087 jumpStack.add(finallyLabels[node]);
1088 } else if (node == target) {
1089 jumpStack.add(targetLabel);
1090 break;
1091 }
1092 // Ignore other nodes.
1093 }
1094 jumpStack = jumpStack.reversed.toList();
1095 // As the program jumps directly to the top of the stack, it is taken off
1096 // now.
1097 int firstTarget = jumpStack.removeLast();
1098 if (jumpStack.isNotEmpty) {
1099 hasJumpThroughFinally = true;
1100 Expression jsJumpStack = new ArrayInitializer(
1101 jumpStack.map((int label) => number(label))
1102 .toList());
1103 addStatement(js.statement("$nextName = #", [jsJumpStack]));
1104 }
1105 addGoto(firstTarget);
1106 }
1107
1108 @override
1109 void visitDefault(Default node) => unreachable(node);
1110
1111 @override
1112 void visitDo(Do node) {
1113 if (!shouldTransform(node)) {
1114 bool oldInsideUntranslatedBreakable = insideUntranslatedBreakable;
1115 insideUntranslatedBreakable = true;
1116 withExpression(node.condition, (Expression condition) {
1117 addStatement(new Do(translateInBlock(node.body), condition));
1118 }, store: false);
1119 insideUntranslatedBreakable = oldInsideUntranslatedBreakable;
1120 return;
1121 }
1122 int startLabel = newLabel("do body");
1123
1124 int continueLabel = newLabel("do condition");
1125 continueLabels[node] = continueLabel;
1126
1127 int afterLabel = newLabel("after do");
1128 breakLabels[node] = afterLabel;
1129
1130 beginLabel(startLabel);
1131
1132 targetsAndTries.add(node);
1133 visitStatement(node.body);
1134 targetsAndTries.removeLast();
1135
1136 beginLabel(continueLabel);
1137 withExpression(node.condition, (Expression condition) {
1138 addStatement(new If.noElse(condition,
1139 gotoAndBreak(startLabel)));
1140 }, store: false);
1141 beginLabel(afterLabel);
1142 }
1143
1144 @override
1145 void visitEmptyStatement(EmptyStatement node) {
1146 addStatement(node);
1147 }
1148
1149 void visitExpressionInStatementContext(Expression node) {
1150 if (node is VariableDeclarationList) {
1151 // Treat VariableDeclarationList as a statement.
1152 visitVariableDeclarationList(node);
1153 } else {
1154 visitExpressionIgnoreResult(node);
1155 }
1156 }
1157
1158 @override
1159 void visitExpressionStatement(ExpressionStatement node) {
1160 visitExpressionInStatementContext(node.expression);
1161 }
1162
1163 @override
1164 void visitFor(For node) {
1165 if (!shouldTransform(node) ) {
1166 bool oldInsideUntranslated = insideUntranslatedBreakable;
1167 insideUntranslatedBreakable = true;
1168 // Note that node.init, node.condition, node.update all can be null, but
1169 // withExpressions handles that.
1170 withExpressions([node.init, node.condition, node.update],
1171 (List<Expression> transformed) {
1172 addStatement(new For(transformed[0],
1173 transformed[1],
1174 transformed[2],
1175 translateInBlock(node.body)));
1176 });
1177 insideUntranslatedBreakable = oldInsideUntranslated;
1178 return;
1179 }
1180
1181 if (node.init != null) {
1182 visitExpressionInStatementContext(node.init);
1183 }
1184 int startLabel = newLabel("for condition");
1185 // If there is no update, continuing the loop is the same as going to the
1186 // start.
1187 int continueLabel = (node.update == null)
1188 ? startLabel
1189 : newLabel("for update");
1190 continueLabels[node] = continueLabel;
1191 int afterLabel = newLabel("after for");
1192 breakLabels[node] = afterLabel;
1193 beginLabel(startLabel);
1194 Expression condition = node.condition;
1195 if (condition == null ||
1196 (condition is LiteralBool && condition.value == true)) {
1197 // No condition is the same as true. So the check is not needed.
floitsch 2015/02/05 20:17:20 I'm not sure I understand the comment. Remove?
sigurdm 2015/02/06 14:26:33 Done.
1198 addStatement(new Comment("trivial condition"));
1199 } else {
1200 withExpression(condition, (Expression condition) {
1201 addStatement(new If.noElse(new Prefix("!", condition),
1202 gotoAndBreak(afterLabel)));
1203 }, store: false);
1204 }
1205 targetsAndTries.add(node);
1206 visitStatement(node.body);
1207 targetsAndTries.removeLast();
1208 if (node.update != null) {
1209 beginLabel(continueLabel);
1210 visitExpressionIgnoreResult(node.update);
1211 }
1212 addGoto(startLabel);
1213 beginLabel(afterLabel);
1214 }
1215
1216 @override
1217 void visitForIn(ForIn node) {
1218 // The dart output currently never uses for-in loops.
1219 throw "Javascript for-in not implemented yet in the await transformation";
1220 }
1221
1222 @override
1223 void visitFunctionDeclaration(FunctionDeclaration node) {
1224 unsupported(node);
1225 }
1226
1227 // Only used for code where `!shouldTransform(node)`.
1228 Block translateInBlock(Statement node) {
1229 assert(!shouldTransform(node));
1230 List<Statement> oldBuffer = currentStatementBuffer;
1231 currentStatementBuffer = new List();
1232 List<Statement> resultBuffer = currentStatementBuffer;
1233 visitStatement(node);
1234 currentStatementBuffer = oldBuffer;
1235 return new Block(resultBuffer);
1236 }
1237
1238 @override
1239 void visitIf(If node) {
1240 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
1241 withExpression(node.condition, (Expression condition) {
1242 addStatement(
1243 new If(
1244 condition,
1245 translateInBlock(node.then),
1246 translateInBlock(node.otherwise)));
1247 }, store: false);
1248 return;
1249 }
1250 int thenLabel = newLabel("then");
1251 int joinLabel = newLabel("join");
1252 int elseLabel =
1253 node.otherwise is EmptyStatement ? joinLabel : newLabel("else");
1254
1255 withExpression(node.condition, (Expression condition) {
1256 addExpressionStatement(
1257 new Assignment(
1258 new VariableUse(gotoName),
1259 new Conditional(
1260 condition,
1261 number(thenLabel),
1262 number(elseLabel))));
1263 }, store: false);
1264 addBreak();
1265 beginLabel(thenLabel);
1266 visitStatement(node.then);
1267 if (node.otherwise is! EmptyStatement) {
1268 addGoto(joinLabel);
1269 beginLabel(elseLabel);
1270 visitStatement(node.otherwise);
1271 }
1272 beginLabel(joinLabel);
1273 }
1274
1275 @override
1276 visitInterpolatedExpression(InterpolatedExpression node) => unsupported(node);
1277
1278 @override
1279 visitInterpolatedLiteral(InterpolatedLiteral node) => unsupported(node);
1280
1281 @override
1282 visitInterpolatedParameter(InterpolatedParameter node) => unsupported(node);
1283
1284 @override
1285 visitInterpolatedSelector(InterpolatedSelector node) => unsupported(node);
1286
1287 @override
1288 visitInterpolatedStatement(InterpolatedStatement node) => unsupported(node);
1289
1290 @override
1291 void visitLabeledStatement(LabeledStatement node) {
1292 if (!shouldTransform(node)) {
1293 addStatement(
1294 new LabeledStatement(node.label, translateInBlock(node.body)));
1295 return;
1296 }
1297 int breakLabel = newLabel("break ${node.label}");
1298 int continueLabel = newLabel("continue ${node.label}");
1299 breakLabels[node] = breakLabel;
1300 continueLabels[node] = continueLabel;
1301
1302 beginLabel(continueLabel);
1303 targetsAndTries.add(node);
1304 visitStatement(node.body);
1305 targetsAndTries.removeLast();
1306 beginLabel(breakLabel);
1307 }
1308
1309 @override
1310 Expression visitLiteralBool(LiteralBool node) => node;
1311
1312 @override
1313 visitLiteralExpression(LiteralExpression node) => unsupported(node);
1314
1315 @override
1316 Expression visitLiteralNull(LiteralNull node) => node;
1317
1318 @override
1319 Expression visitLiteralNumber(LiteralNumber node) => node;
1320
1321 @override
1322 visitLiteralStatement(LiteralStatement node) => unsupported(node);
1323
1324 @override
1325 Expression visitLiteralString(LiteralString node) => node;
1326
1327 @override
1328 visitNamedFunction(NamedFunction node) {
1329 unsupported(node);
1330 }
1331
1332 @override
1333 Expression visitNew(New node) {
1334 bool storeTarget = node.arguments.any(shouldTransform);
1335 return withExpression(node.target, (target) {
1336 return withExpressions(node.arguments, (List<Expression> arguments) {
1337 return new New(target, arguments);
1338 });
1339 }, store: storeTarget);
1340 }
1341
1342 @override
1343 Expression visitObjectInitializer(ObjectInitializer node) {
1344 return withExpressions(
1345 node.properties.map((Property property) => property.value).toList(),
1346 (List<Node> values) {
1347 List<Property> properties = new List.generate(values.length, (int i) {
1348 return new Property(node.properties[i].name, values[i]);
1349 });
1350 return new ObjectInitializer(properties);
1351 });
1352 }
1353
1354 @override
1355 visitParameter(Parameter node) => unreachable(node);
1356
1357 @override
1358 Expression visitPostfix(Postfix node) {
1359 if (node.op == "++" || node.op == "--") {
1360 Expression argument = node.argument;
1361 if (argument is VariableUse) {
1362 return new Postfix(node.op, argument);
1363 } else if (argument is PropertyAccess) {
1364 return withExpression2(argument.receiver, argument.selector,
1365 (receiver, selector) {
1366 return new Postfix(node.op, new PropertyAccess(receiver, selector));
1367 });
1368 } else {
1369 throw "Unexpected postfix ${node.op} "
1370 "operator argument ${node.argument}";
1371 }
1372 }
1373 return withExpression(
1374 node.argument,
1375 (Expression argument) => new Postfix(node.op, argument),
1376 store: false);
1377 }
1378
1379 @override
1380 Expression visitPrefix(Prefix node) {
1381 if (node.op == "++" || node.op == "--") {
1382 Expression argument = node.argument;
1383 if (argument is VariableUse) {
1384 return new Prefix(node.op, argument);
1385 } else if (argument is PropertyAccess) {
1386 return withExpression2(argument.receiver, argument.selector,
1387 (receiver, selector) {
1388 return new Prefix(node.op, new PropertyAccess(receiver, selector));
1389 });
1390 } else {
1391 throw "Unexpected prefix ${node.op} operator "
1392 "argument ${node.argument}";
1393 }
1394 }
1395 return withExpression(
1396 node.argument,
1397 (Expression argument) => new Prefix(node.op, argument),
1398 store: false);
1399 }
1400
1401 @override
1402 visitProgram(Program node) => unsupported(node);
1403
1404 @override
1405 Property visitProperty(Property node) {
1406 return withExpression(
1407 node.value,
1408 (Expression value) => new Property(node.name, value),
1409 store: false);
1410 }
1411
1412 @override
1413 Expression visitRegExpLiteral(RegExpLiteral node) => node;
1414
1415 @override
1416 void visitReturn(Return node) {
1417 assert(node.value == null || !isSyncStar && !isAsyncStar);
1418 Node target = analysis.targets[node];
1419 if (node.value != null) {
1420 withExpression(node.value, (Expression value) {
1421 addStatement(js.statement("$returnValueName = #", [value]));
1422 }, store: false);
1423 }
1424 translateJump(target, returnLabel);
1425 }
1426
1427 @override
1428 void visitSwitch(Switch node) {
1429 if (!node.cases.any(shouldTransform)) {
1430 // If only the key has an await, translation can be simplified.
1431 bool oldInsideUntranslated = insideUntranslatedBreakable;
1432 insideUntranslatedBreakable = true;
1433 withExpression(node.key, (Expression key) {
1434 List<SwitchClause> cases = node.cases.map((SwitchClause clause) {
1435 if (clause is Case) {
1436 return new Case(clause.expression, translateInBlock(clause.body));
1437 } else if (clause is Default) {
1438 return new Default(translateInBlock(clause.body));
1439 }
1440 }).toList();
1441 addStatement(new Switch(key, cases));
1442 }, store: false);
1443 insideUntranslatedBreakable = oldInsideUntranslated;
1444 return;
1445 }
1446 int before = newLabel("switch");
1447 int after = newLabel("after switch");
1448 breakLabels[node] = after;
1449
1450 beginLabel(before);
1451 List<int> labels = new List<int>(node.cases.length);
1452
1453 if (!node.cases.every(
1454 (SwitchClause x) => !(x is Case && shouldTransform(x.expression)))) {
1455 int defaultIndex = null; // Null means no default was found.
1456 // If there is an await in one of the keys, a chain of ifs has to be used.
1457
1458 withExpression(node.key, (Expression key) {
1459 int i = 0;
1460 for (SwitchClause clause in node.cases) {
1461 if (clause is Default) {
1462 // The goto for the default case is added after all non-default
1463 // clauses have been handled.
1464 defaultIndex = i;
1465 labels[i] = newLabel("default");
1466 continue;
1467 } else if (clause is Case) {
1468 labels[i] = newLabel("case");
1469 withExpression(clause.expression, (expression) {
1470 addStatement(
1471 new If.noElse(new Binary("===", key, expression),
1472 gotoAndBreak(labels[i])));
1473 }, store: false);
1474 }
1475 i++;
1476 }
1477 }, store: true);
1478
1479 if (defaultIndex == null) {
1480 addGoto(after);
1481 } else {
1482 addGoto(labels[defaultIndex]);
1483 }
1484
1485 } else {
1486 bool hasDefault = false;
1487 int i = 0;
1488 List<SwitchClause> clauses = new List<SwitchClause>();
1489 for (SwitchClause clause in node.cases) {
1490 if (clause is Case) {
1491 labels[i] = newLabel("case");
1492 clauses.add(new Case(clause.expression, gotoAndBreak(labels[i])));
1493 } else if (i is Default) {
1494 labels[i] = newLabel("default");
1495 clauses.add(new Default(gotoAndBreak(labels[i])));
1496 hasDefault = true;
1497 } else {
1498 diagnosticListener.internalError(spannable,
1499 "Unknown clause type $clause");
1500 }
1501 i++;
1502 }
1503 withExpression(node.key, (Expression key) {
1504 addStatement(new Switch(key, clauses));
1505 }, store: false);
1506 if (!hasDefault) {
1507 addGoto(after);
1508 }
1509 }
1510
1511 targetsAndTries.add(node);
1512 for (int i = 0; i < labels.length; i++) {
1513 beginLabel(labels[i]);
1514 visitStatement(node.cases[i].body);
1515 }
1516 beginLabel(after);
1517 targetsAndTries.removeLast();
1518 }
1519
1520 @override
1521 Expression visitThis(This node) {
1522 return new VariableUse(selfName);
1523 }
1524
1525 @override
1526 void visitThrow(Throw node) {
1527 withExpression(node.expression, (Expression expression) {
1528 addStatement(new Throw(expression));
1529 }, store: false);
1530 }
1531
1532 setHandler() {
1533 addExpressionStatement(
1534 new Assignment(new VariableUse(handlerName), currentErrorHandler));
1535 }
1536
1537 @override
1538 void visitTry(Try node) {
floitsch 2015/02/05 20:17:21 Reference visitFun.
sigurdm 2015/02/06 14:26:32 Done.
1539 if (!shouldTransform(node)) {
1540 Block body = translateInBlock(node.body);
1541 Catch catchPart = (node.catchPart == null)
1542 ? null
1543 : new Catch(node.catchPart.declaration,
1544 translateInBlock(node.catchPart.body));
1545 Block finallyPart = (node.finallyPart == null)
1546 ? null
1547 : translateInBlock(node.finallyPart);
1548 addStatement(new Try(body, catchPart, finallyPart));
1549 return;
1550 }
1551 hasTryBlocks = true;
1552 int handlerLabel = newLabel("catch");
1553 int finallyLabel = newLabel("finally");
1554 int afterFinallyLabel = newLabel("after finally");
1555 errorHandlerLabels.add(handlerLabel);
1556 // Set the error handler here. It must be cleared on every path out;
1557 // normal and error exit.
1558 setHandler();
floitsch 2015/02/05 20:17:22 maybe rename to setErrorHandler
sigurdm 2015/02/06 14:26:33 Done.
1559 if (node.finallyPart != null) {
1560 finallyLabels[node] = finallyLabel;
1561 targetsAndTries.add(node);
1562 }
1563 visitStatement(node.body);
1564 errorHandlerLabels.removeLast();
1565 addStatement(js.statement("$nextName = [#];",
1566 [number(afterFinallyLabel)]));
1567 if (node.finallyPart != null) {
1568 // The handler is set as the first thing in the finally block.
1569 addGoto(finallyLabel);
1570 } else {
1571 setHandler();
1572 addGoto(afterFinallyLabel);
1573 }
1574 beginLabel(handlerLabel);
1575 if (node.catchPart != null) {
1576 setHandler();
1577 // The catch declaration name can shadow outer variables, so a fresh name
1578 // is needed to avoid collisions.
floitsch 2015/02/05 20:17:22 collisions. See Ecma 262, 3rd edition, section 12.
sigurdm 2015/02/06 14:26:33 Done.
1579 String errorRename = freshName(node.catchPart.declaration.name);
1580 localVariables.add(new VariableDeclaration(errorRename));
1581 variableRenamings.add(
1582 new Pair(node.catchPart.declaration.name, errorRename));
1583 addExpressionStatement(
1584 new Assignment(new VariableUse(errorRename),
1585 new VariableUse(resultName)));
1586 visitStatement(node.catchPart.body);
1587 variableRenamings.removeLast();
1588 }
1589 if (node.finallyPart != null) {
1590 setHandler();
1591 targetsAndTries.removeLast();
1592 // This belongs to the catch-part, but is only needed if there is a
1593 // finally therefore it is in this branch.
1594 // This is needed it even if there is no explicit catch-branch, because
1595 // if an exception is raised the finally part has to be run.
floitsch 2015/02/05 20:17:21 I don't get this. If the try-body executes withou
sigurdm 2015/02/06 14:26:34 I agree - this is a bug. Thanks for spotting.
1596 addStatement(js.statement("$nextName = [#];",
1597 [number(afterFinallyLabel)]));
1598 beginLabel(finallyLabel);
1599 setHandler();
1600 visitStatement(node.finallyPart);
1601 addStatement(new Comment("// goto the next finally handler"));
1602 addStatement(js.statement("$gotoName = $nextName.pop();"));
1603 addBreak();
1604 }
1605 beginLabel(afterFinallyLabel);
1606 }
1607
1608 @override
1609 visitVariableDeclaration(VariableDeclaration node) {
1610 unreachable(node);
1611 }
1612
1613 @override
1614 void visitVariableDeclarationList(VariableDeclarationList node) {
1615 // Declaration of local variables is hoisted outside the helper but the
1616 // initialization is done here.
1617 for (VariableInitialization initialization in node.declarations) {
1618 VariableDeclaration declaration = initialization.declaration;
1619 localVariables.add(declaration);
1620 if (initialization.value != null) {
1621 withExpression(initialization.value, (Expression value) {
1622 addStatement(
1623 new ExpressionStatement(new Assignment(declaration, value)));
1624 }, store: false);
1625 }
1626 }
1627 }
1628
1629 @override
1630 void visitVariableInitialization(VariableInitialization node) {
1631 unreachable(node);
1632 }
1633
1634 @override
1635 Expression visitVariableUse(VariableUse node) {
1636 Pair<String, String> renaming = variableRenamings.lastWhere(
1637 (Pair renaming) => renaming.a == node.name,
1638 orElse: () => null);
1639 if (renaming == null) return node;
1640 return new VariableUse(renaming.b);
1641 }
1642
1643 @override
1644 void visitWhile(While node) {
1645 if (!shouldTransform(node)) {
1646 bool oldInsideUntranslated = insideUntranslatedBreakable;
1647 insideUntranslatedBreakable = true;
1648 withExpression(node.condition, (Expression condition) {
1649 addStatement(new While(condition, translateInBlock(node.body)));
1650 }, store: false);
1651 insideUntranslatedBreakable = oldInsideUntranslated;
1652 return;
1653 }
1654 int continueLabel = newLabel("while condition");
1655 continueLabels[node] = continueLabel;
1656 beginLabel(continueLabel);
1657
1658 int afterLabel = newLabel("after while");
1659 breakLabels[node] = afterLabel;
1660 Expression condition = node.condition;
1661 // If the condition is `true`, a test is not needed.
1662 if (!(condition is LiteralBool && condition.value == true)) {
1663 withExpression(node.condition, (Expression condition) {
1664 addStatement(new If.noElse(new Prefix("!", condition),
1665 gotoAndBreak(afterLabel)));
1666 }, store: false);
1667 }
1668 targetsAndTries.add(node);
1669 visitStatement(node.body);
1670 targetsAndTries.removeLast();
1671 addGoto(continueLabel);
1672 beginLabel(afterLabel);
1673 }
1674
1675 /// Translates a yield/yield* in an sync* function.
floitsch 2015/02/05 20:17:21 in a sync*
sigurdm 2015/02/06 14:26:34 Done.
1676 ///
1677 /// `yield` in a sync* function just returns [value].
1678 /// `yield*` wraps [value] in a [yieldStarExpression] and returns it.
1679 void addSyncYield(DartYield node, Expression expression) {
1680 assert(isSyncStar);
1681 if (node.hasStar) {
1682 addStatement(new Return(new Call(yieldStarExpression, [expression])));
1683 } else {
1684 addStatement(new Return(expression));
1685 }
1686 }
1687
1688 /// Translates a yield/yield* in an async* function.
1689 ///
1690 /// yield/yield* in an async* function is translated much like the `await` is
1691 /// translated in [visitAwait], only the object is wrapped in a
1692 /// [yieldExpression]/[yieldStarExpression] to let [streamHelper] distinguish.
1693 ///
1694 /// Because there is no Future that can fail (as there is in await) null is
1695 /// passed as the errorCallback.
1696 void addAsyncYield(DartYield node, Expression expression) {
1697 assert(isAsyncStar);
1698 // Find all the finally blocks that should be performed if the stream is
1699 // canceled during the yield.
1700 // At the bottom of the stack is the return label.
1701 List<int> enclosingFinallyLabels = <int>[returnLabel];
floitsch 2015/02/05 20:17:22 I think "exitLabel" would read easier. (Here in th
sigurdm 2015/02/06 14:26:33 Done.
1702 enclosingFinallyLabels.addAll(targetsAndTries
1703 .where((Node node) => node is Try)
1704 .map((Try node) => finallyLabels[node]));
1705 int destinationOnCancel = enclosingFinallyLabels.removeLast();
1706 ArrayInitializer finallyListInitializer =
1707 new ArrayInitializer(enclosingFinallyLabels
1708 .map((int label) => number(label))
floitsch 2015/02/05 20:17:22 .map(number).toList() and yes: we should have a p
sigurdm 2015/02/06 14:26:32 Done.
1709 .toList());
1710 addStatement(js.statement("""
1711 return #streamHelper(#yieldExpression(#expression),
1712 $helperName, $controllerName, function () {
1713 if (#notEmptyFinallyList)
1714 $nextName = #finallyList;
1715 $gotoName = #destinationOnCancel;
1716 $helperName();
1717 });""",
1718 {"streamHelper": streamHelper,
1719 "yieldExpression": node.hasStar
1720 ? yieldStarExpression
1721 : yieldExpression,
1722 "expression": expression,
1723 "notEmptyFinallyList": enclosingFinallyLabels.isNotEmpty,
1724 "finallyList": finallyListInitializer,
1725 "destinationOnCancel": number(destinationOnCancel)}));
1726 }
1727
1728 @override
1729 void visitDartYield(DartYield node) {
1730 assert(isSyncStar || isAsyncStar);
1731 int label = newLabel("after yield");
1732 // Don't do a break here for the goto, but instead a return in either
1733 // addSynYield or addAsyncYield.
1734 withExpression(node.expression, (Expression expression) {
1735 addStatement(setGotoVariable(label));
1736 if (isSyncStar) {
1737 addSyncYield(node, expression);
1738 } else {
1739 addAsyncYield(node, expression);
1740 }
1741 }, store: false);
1742 beginLabel(label);
1743 }
1744 }
1745
1746 /// Finds out
1747 ///
1748 /// - which expressions have yield or await nested in them.
1749 /// - targets of jumps
1750 /// - a set of used names.
1751 /// - if any [This]-expressions are used.
1752 class PreTranslationAnalysis extends NodeVisitor<bool> {
1753 Set<Node> hasAwaitOrYield = new Set<Node>();
1754
1755 Map<Node, Node> targets = new Map<Node, Node>();
1756 List<Node> loopsAndSwitches = new List<Node>();
1757 List<LabeledStatement> labelledStatements = new List<LabeledStatement>();
1758 Set<String> usedNames = new Set<String>();
1759
1760 bool hasExplicitReturns = false;
1761
1762 bool hasThis = false;
1763
1764 // The function currently being analyzed.
1765 Fun currentFunction;
1766
1767 // For error messages.
1768 final Function unsupported;
1769
1770 PreTranslationAnalysis(void this.unsupported(Node node));
1771
1772 bool visit(Node node) {
1773 bool containsAwait = node.accept(this);
1774 if (containsAwait) {
1775 hasAwaitOrYield.add(node);
1776 }
1777 return containsAwait;
1778 }
1779
1780 analyze(Fun node) {
1781 currentFunction = node;
1782 node.params.forEach(visit);
1783 visit(node.body);
1784 }
1785
1786 @override
1787 bool visitAccess(PropertyAccess node) {
1788 bool receiver = visit(node.receiver);
1789 bool selector = visit(node.selector);
1790 return receiver || selector;
1791 }
1792
1793 @override
1794 bool visitArrayHole(ArrayHole node) {
1795 return false;
1796 }
1797
1798 @override
1799 bool visitArrayInitializer(ArrayInitializer node) {
1800 bool containsAwait = false;
1801 for (Expression element in node.elements) {
1802 if (visit(element)) containsAwait = true;
1803 }
1804 return containsAwait;
1805 }
1806
1807 @override
1808 bool visitAssignment(Assignment node) {
1809 bool leftHandSide = visit(node.leftHandSide);
1810 bool value = (node.value == null) ? false : visit(node.value);
1811 return leftHandSide || value;
1812 }
1813
1814 @override
1815 bool visitAwait(Await node) {
1816 visit(node.expression);
1817 return true;
1818 }
1819
1820 @override
1821 bool visitBinary(Binary node) {
1822 bool left = visit(node.left);
1823 bool right = visit(node.right);
1824 return left || right;
1825 }
1826
1827 @override
1828 bool visitBlob(Blob node) {
1829 return false;
1830 }
1831
1832 @override
1833 bool visitBlock(Block node) {
1834 bool containsAwait = false;
1835 for (Statement statement in node.statements) {
1836 if (visit(statement)) containsAwait = true;
1837 }
1838 return containsAwait;
1839 }
1840
1841 @override
1842 bool visitBreak(Break node) {
1843 if (node.targetLabel != null) {
1844 targets[node] = labelledStatements.lastWhere(
1845 (LabeledStatement statement) => statement.label == node.targetLabel);
1846 } else {
1847 targets[node] = loopsAndSwitches.last;
1848 }
1849 return false;
1850 }
1851
1852 @override
1853 bool visitCall(Call node) {
1854 bool containsAwait = visit(node.target);
1855 for (Expression argument in node.arguments) {
1856 if (visit(argument)) containsAwait = true;
1857 }
1858 return containsAwait;
1859 }
1860
1861 @override
1862 bool visitCase(Case node) {
1863 bool expression = visit(node.expression);
1864 bool body = visit(node.body);
1865 return expression || body;
1866 }
1867
1868 @override
1869 bool visitCatch(Catch node) {
1870 bool declaration = visit(node.declaration);
1871 bool body = visit(node.body);
1872 return declaration || body;
1873 }
1874
1875 @override
1876 bool visitComment(Comment node) {
1877 return false;
1878 }
1879
1880 @override
1881 bool visitConditional(Conditional node) {
1882 bool condition = visit(node.condition);
1883 bool then = visit(node.then);
1884 bool otherwise = visit(node.otherwise);
1885 return condition || then || otherwise;
1886 }
1887
1888 @override
1889 bool visitContinue(Continue node) {
1890 if (node.targetLabel != null) {
1891 targets[node] = labelledStatements.lastWhere(
1892 (LabeledStatement stm) => stm.label == node.targetLabel);
1893 } else {
1894 targets[node] =
1895 loopsAndSwitches.lastWhere((Node node) => node is! Switch);
1896 }
1897 assert(() {
1898 Node target = targets[node];
1899 return target is Loop ||
1900 (target is LabeledStatement && target.body is Loop);
1901 });
1902 return false;
1903 }
1904
1905 @override
1906 bool visitDefault(Default node) {
1907 return visit(node.body);
1908 }
1909
1910 @override
1911 bool visitDo(Do node) {
1912 loopsAndSwitches.add(node);
1913 bool body = visit(node.body);
1914 bool condition = visit(node.condition);
1915 loopsAndSwitches.removeLast();
1916 return body || condition;
1917 }
1918
1919 @override
1920 bool visitEmptyStatement(EmptyStatement node) {
1921 return false;
1922 }
1923
1924 @override
1925 bool visitExpressionStatement(ExpressionStatement node) {
1926 return visit(node.expression);
1927 }
1928
1929 @override
1930 bool visitFor(For node) {
1931 bool init = (node.init == null) ? false : visit(node.init);
1932 bool condition = (node.condition == null) ? false : visit(node.condition);
1933 bool update = (node.update == null) ? false : visit(node.update);
1934 loopsAndSwitches.add(node);
1935 bool body = visit(node.body);
1936 loopsAndSwitches.removeLast();
1937 return init || condition || update || body;
1938 }
1939
1940 @override
1941 bool visitForIn(ForIn node) {
1942 bool object = visit(node.object);
1943 loopsAndSwitches.add(node);
1944 bool body = visit(node.body);
1945 loopsAndSwitches.removeLast();
1946 return object || body;
1947 }
1948
1949 @override
1950 bool visitFun(Fun node) {
1951 return false;
1952 }
1953
1954 @override
1955 bool visitFunctionDeclaration(FunctionDeclaration node) {
1956 return false;
1957 }
1958
1959 @override
1960 bool visitIf(If node) {
1961 bool condition = visit(node.condition);
1962 bool then = visit(node.then);
1963 bool otherwise = visit(node.otherwise);
1964 return condition || then || otherwise;
1965 }
1966
1967 @override
1968 bool visitInterpolatedExpression(InterpolatedExpression node) {
1969 return unsupported(node);
1970 }
1971
1972 @override
1973 bool visitInterpolatedLiteral(InterpolatedLiteral node) {
1974 return unsupported(node);
1975 }
1976
1977 @override
1978 bool visitInterpolatedParameter(InterpolatedParameter node) {
1979 return unsupported(node);
1980 }
1981
1982 @override
1983 bool visitInterpolatedSelector(InterpolatedSelector node) {
1984 return unsupported(node);
1985 }
1986
1987 @override
1988 bool visitInterpolatedStatement(InterpolatedStatement node) {
1989 return unsupported(node);
1990 }
1991
1992 @override
1993 bool visitLabeledStatement(LabeledStatement node) {
1994 usedNames.add(node.label);
1995 labelledStatements.add(node);
1996 bool containsAwait = visit(node.body);
1997 labelledStatements.removeLast();
1998 return containsAwait;
1999 }
2000
2001 @override
2002 bool visitLiteralBool(LiteralBool node) {
2003 return false;
2004 }
2005
2006 @override
2007 bool visitLiteralExpression(LiteralExpression node) {
2008 return unsupported(node);
2009 }
2010
2011 @override
2012 bool visitLiteralNull(LiteralNull node) {
2013 return false;
2014 }
2015
2016 @override
2017 bool visitLiteralNumber(LiteralNumber node) {
2018 return false;
2019 }
2020
2021 @override
2022 bool visitLiteralStatement(LiteralStatement node) {
2023 return unsupported(node);
2024 }
2025
2026 @override
2027 bool visitLiteralString(LiteralString node) {
2028 return false;
2029 }
2030
2031 @override
2032 bool visitNamedFunction(NamedFunction node) {
2033 return false;
2034 }
2035
2036 @override
2037 bool visitNew(New node) {
2038 return visitCall(node);
2039 }
2040
2041 @override
2042 bool visitObjectInitializer(ObjectInitializer node) {
2043 bool containsAwait = false;
2044 for (Property property in node.properties) {
2045 if (visit(property)) containsAwait = true;
2046 }
2047 return containsAwait;
2048 }
2049
2050 @override
2051 bool visitParameter(Parameter node) {
2052 usedNames.add(node.name);
2053 return false;
2054 }
2055
2056 @override
2057 bool visitPostfix(Postfix node) {
2058 return visit(node.argument);
2059 }
2060
2061 @override
2062 bool visitPrefix(Prefix node) {
2063 return visit(node.argument);
2064 }
2065
2066 @override
2067 bool visitProgram(Program node) {
2068 throw "Unexpected";
2069 }
2070
2071 @override
2072 bool visitProperty(Property node) {
2073 return visit(node.value);
2074 }
2075
2076 @override
2077 bool visitRegExpLiteral(RegExpLiteral node) {
2078 return false;
2079 }
2080
2081 @override
2082 bool visitReturn(Return node) {
2083 hasExplicitReturns = true;
2084 targets[node] = currentFunction;
2085 if (node.value == null) return false;
2086 return visit(node.value);
2087 }
2088
2089 @override
2090 bool visitSwitch(Switch node) {
2091 loopsAndSwitches.add(node);
2092 bool result = visit(node.key);
2093 for (SwitchClause clause in node.cases) {
2094 if (visit(clause)) result = true;
2095 }
2096 loopsAndSwitches.removeLast();
2097 return result;
2098 }
2099
2100 @override
2101 bool visitThis(This node) {
2102 hasThis = true;
2103 return false;
2104 }
2105
2106 @override
2107 bool visitThrow(Throw node) {
2108 return visit(node.expression);
2109 }
2110
2111 @override
2112 bool visitTry(Try node) {
2113 bool body = visit(node.body);
2114 bool catchPart = (node.catchPart == null) ? false : visit(node.catchPart);
2115 bool finallyPart = (node.finallyPart == null)
2116 ? false
2117 : visit(node.finallyPart);
2118 return body || catchPart || finallyPart;
2119 }
2120
2121 @override
2122 bool visitVariableDeclaration(VariableDeclaration node) {
2123 usedNames.add(node.name);
2124 return false;
2125 }
2126
2127 @override
2128 bool visitVariableDeclarationList(VariableDeclarationList node) {
2129 bool result = false;
2130 for (VariableInitialization init in node.declarations) {
2131 if (visit(init)) result = true;
2132 }
2133 return result;
2134 }
2135
2136 @override
2137 bool visitVariableInitialization(VariableInitialization node) {
2138 return visitAssignment(node);
2139 }
2140
2141 @override
2142 bool visitVariableUse(VariableUse node) {
2143 usedNames.add(node.name);
2144 return false;
2145 }
2146
2147 @override
2148 bool visitWhile(While node) {
2149 loopsAndSwitches.add(node);
2150 bool condition = visit(node.condition);
2151 bool body = visit(node.body);
2152 loopsAndSwitches.removeLast();
2153 return condition || body;
2154 }
2155
2156 @override
2157 bool visitDartYield(DartYield node) {
2158 visit(node.expression);
2159 return true;
2160 }
2161 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698