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

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: Go back to generating return of wrapped errors when possible. 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";
14 import '../util/util.dart';
15 import '../dart2jslib.dart' show DiagnosticListener;
16
17 import "../helpers/helpers.dart";
18
19 /// Rewrites a js Fun with async/sync*/async* functions and await and yield
20 /// (with dart-like semantics) to an equivalent function without these.
21 /// await-for is not handled and must be rewritten before. (Currently handled
22 /// in ssa/builder.dart).
23 /// When generating the input to this, special care must be taken that
24 /// parameters to sync* functions that are mutated in the body must be boxed.
25 /// (Currently handled in closure.dart).
26
27 class AsyncRewriter extends NodeVisitor {
28
29 // Local variables are hoisted to the top of the function, so we collect them
30 // here.
31 List<VariableDeclaration> localVariables = new List<VariableDeclaration>();
32
33 Map<Node, int> continueLabels = new Map<Node, int>();
34 Map<Node, int> breakLabels = new Map<Node, int>();
35 Map<Node, int> finallyLabels = new Map<Node, int>();
36 int returnLabel;
37
38 // A stack of all enclosing jump targets (including the function for
39 // representing the target of a return. And all enclosing try-blocks that have
floitsch 2015/02/04 12:31:28 return, and all
sigurdm 2015/02/05 14:06:04 Done.
40 // finally part, so we can ensure all the finally blocks between a jump and
41 // its target are run before the jump.
42 List<Node> targetsAndTries = new List<Node>();
43
44 List<int> continueStack = new List<int>();
45 List<int> breakStack = new List<int>();
46 List<int> returnStack = new List<int>();
47
48 List<Pair<String, String>> variableRenamings =
49 new List<Pair<String, String>>();
50
51 PreTranslationAnalysis analysis;
52
53 List<int> errorHandlerLabels = new List<int>();
54
55 final Function safeVariableName;
56
57 String resultName;
58 String helperName;
59 String controllerName;
60 String gotoName;
61 String handlerName;
62 String errorName;
63 String nextName;
64 String returnValueName;
65 String outerLabelName;
66 String selfName;
67
68 final Expression thenHelper;
69 final Expression newController;
70 final Expression endOfIteration;
71 final Expression newIterable;
72 final Expression yieldExpression;
73 final Expression yieldStarExpression;
74
75 final DiagnosticListener diagnosticListener;
76 // For error reporting only.
77 Spannable get spannable {
78 return (_spannable == null) ? NO_LOCATION_SPANNABLE : _spannable;
79 }
80
81 Spannable _spannable;
82
83 int _currentLabel = 0;
84
85 // The highest temporary variable index currently in use.
86 int currentTempVarIndex = 0;
87 // The highest temporary variable index ever in use in this function.
88 int tempVarHighWaterMark = 0;
89 Map<int, Expression> tempVarNames = new Map<int, Expression>();
90
91 AsyncModifier async;
92
93 bool get isSync => async == const AsyncModifier.sync();
94 bool get isAsync => async == const AsyncModifier.async();
95 bool get isSyncStar => async == const AsyncModifier.syncStar();
96 bool get isAsyncStar => async == const AsyncModifier.asyncStar();
97
98 AsyncRewriter(this.diagnosticListener,
99 spannable,
100 {this.thenHelper,
101 this.newController,
102 this.endOfIteration,
103 this.newIterable,
104 this.yieldExpression,
105 this.yieldStarExpression,
106 this.safeVariableName})
107 : _spannable = spannable;
108
109 /// Main entry point.
110 /// Rewrites a sync*/async/async* function to an equivalent normal function.
111 ///
112 /// [spannable] can be passed to have a location for error messages.
113 Fun rewrite(Fun node, [Spannable spannable]) {
114 _spannable = spannable;
115
116 async = node.asyncModifier;
117 assert(!isSync);
118
119 analysis = new PreTranslationAnalysis(unsupported);
120 analysis.analyze(node);
121
122 // To avoid name collisions with existing names, the fresh names are
123 // generated after the analysis.
124 resultName = freshName("result");
125 controllerName = freshName("completer");
126 helperName = freshName("helper");
127 gotoName = freshName("goto");
128 handlerName = freshName("handler");
129 errorName = freshName("error");
130 nextName = freshName("next");
131 returnValueName = freshName("returnValue");
132 outerLabelName = freshName("outer");
133 selfName = freshName("self");
134
135 return node.accept(this);
136 }
137
138 Expression get currentErrorHandler {
139 return errorHandlerLabels.isEmpty ?
140 new LiteralNull() :
141 new LiteralNumber("${errorHandlerLabels.last}");
142 }
143
144 int allocateTempVar() {
145 assert(tempVarHighWaterMark >= currentTempVarIndex);
146 currentTempVarIndex++;
147 tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark);
148 return currentTempVarIndex;
149 }
150
151 VariableUse useTempVar(int i) {
152 return tempVarNames.putIfAbsent(i, () =>
153 new VariableUse(freshName("temp$i")));
154 }
155
156 /// Generates a variable name with [safeVariableName] based on [originalName]
157 /// with a suffix to guarantee it does not collide with already used names.
158 String freshName(String originalName) {
159 String safeName = safeVariableName(originalName);
160 String result = safeName;
161 int counter = 1;
162 while (analysis.usedNames.contains(result)) {
163 result = "$safeName$counter";
164 ++counter;
165 }
166 analysis.usedNames.add(result);
167 return result;
168 }
169
170 /// We collect all the pieces in this map, and output a switch with a case
171 /// for each label.
172 ///
173 /// The order is important, therefore the type is explicitly LinkedHashMap.
174 LinkedHashMap<int, List<Statement>> labelledParts =
175 new LinkedHashMap<int, List<Statement>>();
176
177 /// Description of each label for readability of the non-minified output.
178 Map<int, String> labelComments = new Map<int, String>();
179
180 /// True if the function has any try blocks containing await.
181 bool hasTryBlocks = false;
182
183 /// True if any return, break or continue passes through a finally.
184 bool hasJumpThroughFinally = false;
185
186 /// True if the traversion currently is inside a loop or switch for which
187 /// [shouldTransform] is false.
188 bool insideUntranslatedBreakable = false;
189
190 /// True if we use a label to break to the outer switch-statement.
191 bool hasJumpThoughOuterLabel = false;
192
193 /// Buffer for collecting translated statements belonging to the same switch
194 /// case.
195 List<Statement> currentStatementBuffer;
196
197 // Labels will become cases in the big switch expression, and `goto label`
198 // is expressed by assigning to the switch key [gotoName] and breaking out of
199 // the switch.
200
201 int newLabel([String comment]) {
202 int result = _currentLabel;
203 _currentLabel++;
204 if (comment != null) {
205 labelComments[result] = comment;
206 }
207 return result;
208 }
209
210 /// Begins outputting statements to a new buffer with label [label].
211 /// Each buffer will end up as its own case part in the big state-switch.
floitsch 2015/02/04 12:31:28 New line before. No need for future tense.
sigurdm 2015/02/05 14:06:03 Done.
212 void beginLabel(int label) {
213 assert(!labelledParts.containsKey(label));
214 currentStatementBuffer = new List<Statement>();
215 labelledParts[label] = currentStatementBuffer;
216 addStatement(new Comment(labelComments[label]));
217 }
218
219 /// Returns a statement assigning to the variable named [gotoName].
220 /// This should be followed by a break for the goto to be executed. Use
221 /// [gotoWithBreak] or [addGoto] for this.
222 Statement setGotoVariable(int label) {
223 return new ExpressionStatement(
224 new Assignment(new VariableUse(gotoName), new LiteralNumber("$label")));
225 }
226
227 /// Return a block that has a goto to [label] including the break.
228 ///
229 /// Will also insert a comment describing the label if available.
floitsch 2015/02/04 12:31:28 No need for future tense.
sigurdm 2015/02/05 14:06:04 Done.
230 Block gotoAndBreak(int label) {
231 List<Statement> statements = new List<Statement>();
232 if (labelComments.containsKey(label)) {
233 statements.add(new Comment("goto ${labelComments[label]}"));
234 }
235 statements.add(setGotoVariable(label));
236 statements.add(breakStatement());
237 return new Block(statements);
238 }
239
240 /// Add a goto to [label] including the break.
241 ///
242 /// Will also insert a comment describing the label if available.
floitsch 2015/02/04 12:31:28 No need for future tense.
sigurdm 2015/02/05 14:06:03 Done.
243 void addGoto(int label) {
244 if (labelComments.containsKey(label)) {
245 addStatement(new Comment("goto ${labelComments[label]}"));
246 }
247 addStatement(setGotoVariable(label));
248
249 addBreak();
250 }
251
252 void addStatement(Statement node) {
253 currentStatementBuffer.add(node);
254 }
255
256 void addExpressionStatement(Expression node) {
257 addStatement(new ExpressionStatement(node));
258 }
259
260 /// True if there is an await or yield in [node] or some subexpression.
261 bool shouldTransform(Node node) {
262 return analysis.hasAwaitOrYield.contains(node);
263 }
264
265 void unsupported(Node node) {
266 throw new UnsupportedError(
267 "Node $node cannot be transformed by the await-sync transformer");
268 }
269
270 void unreachable(Node node) {
271 diagnosticListener.internalError(spannable,
272 "Internal error, trying to visit $node");
273 }
274
275 visitStatement(Statement node) {
276 node.accept(this);
277 }
278
279 /// Visits [node] to ensure its sideeffects are performed, but throwing away
280 /// the result.
281 ///
282 /// If the return value of visiting [node] is an expression we can guarantee
283 /// has no side effect, it is dropped.
284 void visitExpressionIgnoreResult(Expression node) {
285 Expression result = node.accept(this);
286 if (!(result is Literal || result is VariableUse)) {
287 addExpressionStatement(result);
288 }
289 }
290
291 Expression visitExpression(Expression node) {
292 return node.accept(this);
293 }
294
295 /// Emits an assignment of [result] to a temporary variable unless it can
296 /// be guaranteed that evaluating [result] has no side-effect, and has
297 /// constant value.
298 /// Returns the temporary variable or the result itself.
floitsch 2015/02/04 12:31:28 New line before.
sigurdm 2015/02/05 14:06:04 Done.
299 ///
300 /// For example:
301 ///
302 /// - _storeIfNecessary(someLiteral) returns someLiteral.
303 /// - _storeIfNecessary(someVariable)
304 /// inserts: var tempX = someVariable
305 /// returns: tempX
306 /// where tempX is a fresh temporary variable.
307 Expression _storeIfNecessary(Expression result) {
308 // Note that RegExes, ArrayInitializer and ObjectInitializer are not
309 // [Literal]s.
310 if (result is Literal) return result;
311 Expression tempVar = useTempVar(allocateTempVar());
312 addExpressionStatement(new Assignment(tempVar, result));
313 return tempVar;
314 }
315
316 withExpression(Expression node, fn(Expression result), {bool store}) {
317 int oldTempVarIndex = currentTempVarIndex;
318 Expression visited = visitExpression(node);
319 if (store) {
320 visited = _storeIfNecessary(visited);
321 }
322 var result = fn(visited);
323 currentTempVarIndex = oldTempVarIndex;
324 return result;
325 }
326
327 /// Calls [fn] with the value of evaluating [node1] and [node2].
328 ///
329 /// If `shouldTransform(node2)` we store the first expression in a temporary
330 /// variable.
331 ///
332 /// This is because we need to ensure evaluating node1 before visiting node2,
333 /// because the evaluation of an await or yield cannot be expressed as
334 /// an expression, visiting node2 it will output statements that
335 /// might have an influence on the value of node1.
336 withExpression2(Expression node1, Expression node2, fn(Expression result1,
337 Expression result2)) {
338 int oldTempVarIndex = currentTempVarIndex;
339 Expression r1 = visitExpression(node1);
340 if (shouldTransform(node2)) {
341 r1 = _storeIfNecessary(r1);
342 }
343 Expression r2 = visitExpression(node2);
344 var result = fn(r1, r2);
345 currentTempVarIndex = oldTempVarIndex;
346 return result;
347 }
348
349 /// Calls [fn] with the value of evaluating all [nodes].
350 ///
351 /// All results before the last node where `shouldTransform(node)` we store in
floitsch 2015/02/04 12:31:28 are stored
sigurdm 2015/02/05 14:06:03 Done.
352 /// temporary variables.
353 ///
354 /// See more explanation on [withExpression2].
355 ///
356 /// If any of the nodes are null, they are ignored, and a null is passed to
357 /// [fn] in that place.
358 withExpressions(List<Expression> nodes, fn(List<Expression> results)) {
359 int oldTempVarIndex = currentTempVarIndex;
360 // Find last occurence of a 'transform' expression in [nodes].
361 // All expressions before that must be stored in temp-vars.
362 int lastTransformIndex = 0;
363 for (int i = nodes.length - 1; i >= 0; --i) {
364 if (nodes[i] == null) continue;
365 if (shouldTransform(nodes[i])) {
366 lastTransformIndex = i;
367 break;
368 }
369 }
370 List<Node> visited = nodes.take(lastTransformIndex).map((Node node) {
371 return node == null
372 ? null
373 : _storeIfNecessary(visitExpression(node));
374 }).toList();
375 visited.addAll(nodes.skip(lastTransformIndex).map((Node node) {
376 return node == null
377 ? null
378 : visitExpression(node);
379 }));
380 var result = fn(visited);
381 currentTempVarIndex = oldTempVarIndex;
382 return result;
383 }
384
385 /// Emits the return block that all returns should jump to.
386 ///
387 /// Returning from an async method calls the helper with the result.
388 /// (the result might been stored in [returnValueName] by some finally block).
389 ///
390 /// Returning from a sync* will return a [endOfIteration] marker.
391 void addReturn() {
392 if (analysis.hasExplicitReturns || isAsyncStar) {
393 beginLabel(returnLabel);
394 } else {
395 addStatement(new Comment("implicit return"));
396 }
397 switch (async) {
398 case const AsyncModifier.async():
399 String returnValue = analysis.hasExplicitReturns
400 ? returnValueName
401 : "null";
402 addStatement(js.statement(
403 "return #thenHelper($returnValue, null, $controllerName, null)",
404 {"thenHelper": thenHelper}));
405 break;
406 case const AsyncModifier.syncStar():
407 addStatement(new Return(new Call(endOfIteration, [])));
408 break;
409 case const AsyncModifier.asyncStar():
410 addStatement(js.statement(
411 "return #thenHelper(null, null, $controllerName, null)",
412 {"thenHelper": thenHelper}));
413 break;
414 default:
415 throw "Internal error, unexpected asyncmodifier";
416 }
417 }
418
419 /// Initializing a controller. (This is a Completer for async,
420 /// a StreamController for async* and nothing for a sync*).
421 /// The controller is be passed to each call to [thenHelper].
floitsch 2015/02/04 12:31:28 -be-
sigurdm 2015/02/05 14:06:03 Done.
422 Statement generateInitializer() {
423 switch (async) {
424 case const AsyncModifier.async():
425 return js.statement(
426 "return #thenHelper(null, $helperName, $controllerName, null);",
427 {"thenHelper": thenHelper});
428 case const AsyncModifier.syncStar():
429 throw "Not handled here";
430 case const AsyncModifier.asyncStar():
431 return js.statement(
432 "return #thenHelper(null, $helperName, $controllerName, null);",
433 {"thenHelper": thenHelper}); default:
434 throw "Internal error, unexpected asyncmodifier";
435 }
436 }
437
438 @override
439 visitFun(Fun node) {
440 if (isSync) return node;
441
442 beginLabel(newLabel("Function start"));
443 // AsyncStar needs a returnlabel for its handling of cancelation. See
444 // [visitYield].
445 returnLabel = analysis.hasExplicitReturns || isAsyncStar
446 ? newLabel("Return")
447 : null;
448 Statement body = node.body;
449 targetsAndTries.add(node);
450 visitStatement(body);
451 targetsAndTries.removeLast();
452 addReturn();
453
454 List<SwitchClause> clauses = labelledParts.keys.map((label) {
455 return new Case(
456 new LiteralNumber("$label"),
457 new Block(labelledParts[label]));
458 }).toList();
459 Statement helperBody = new Switch(new VariableUse(gotoName), clauses);
460 if (hasJumpThoughOuterLabel) {
461 helperBody = js.statement("$outerLabelName: #", [helperBody]);
462 }
463 if (hasTryBlocks) {
464 helperBody = js.statement("""
465 try {
466 #body
467 } catch ($errorName){
468 if ($handlerName === null)
469 throw $errorName;
470 $resultName = $errorName;
471 $gotoName = $handlerName;
472 }""", {"body": helperBody});
473 }
474 List<VariableInitialization> inits = <VariableInitialization>[];
475
476 VariableInitialization makeInit(String name, Expression initValue) {
477 return new VariableInitialization(new VariableDeclaration(name),
478 initValue);
479 }
480
481 inits.add(makeInit(gotoName, new LiteralNumber("0")));
482 if (isAsync || isAsyncStar) {
483 inits.add(makeInit(controllerName, newController));
484 }
485 if (hasTryBlocks) {
486 inits.add(makeInit(handlerName, new LiteralNull()));
487 }
488 if (hasJumpThroughFinally) {
489 inits.add(makeInit(nextName, null));
490 }
491 if (analysis.hasExplicitReturns && isAsync) {
492 inits.add(makeInit(returnValueName, null));
493 }
494 if (analysis.hasThis) {
495 inits.add(makeInit(selfName, new This()));
496 }
497 inits.addAll(localVariables.map(
498 (VariableDeclaration decl) => new VariableInitialization(decl, null)));
499 inits.addAll(new Iterable.generate(tempVarHighWaterMark,
500 (int i) => makeInit(useTempVar(i + 1).name, null)));
501 VariableDeclarationList varDecl = new VariableDeclarationList(inits);
502 if (isSyncStar) {
503 return js("""
504 function (#params) {
505 return new #newIterable(function () {
506 #varDecl;
507 return function $helperName($resultName) {
508 while (true)
509 #helperBody;
510 };
511 });
512 }
513 """, {"params": node.params,
514 "helperBody": helperBody,
515 "varDecl": varDecl,
516 "newIterable": newIterable});
517 }
518 return js("""
519 function (#params) {
520 #varDecl;
521 function $helperName($resultName) {
522 while (true)
523 #helperBody;
524 }
525 #init;
526 }""", {"params": node.params,
527 "helperBody": helperBody,
528 "varDecl": varDecl,
529 "init": generateInitializer()});
530 }
531
532 @override
533 visitAccess(PropertyAccess node) {
534 return withExpression2(
535 node.receiver,
536 node.selector,
537 (receiver, selector) => new PropertyAccess(receiver, selector));
538 }
539
540 @override
541 visitArrayHole(ArrayHole node) {
542 return node;
543 }
544
545 @override
546 visitArrayInitializer(ArrayInitializer node) {
547 return withExpressions(node.elements, (elements) {
548 return new ArrayInitializer(elements);
549 });
550 }
551
552 @override
553 visitAssignment(Assignment node) {
554 if (!shouldTransform(node)) {
555 return new Assignment.compound(visitExpression(node.leftHandSide),
556 node.op,
557 visitExpression(node.value));
558 }
559 Expression leftHandSide = node.leftHandSide;
560 if (leftHandSide is VariableUse) {
561 return withExpression(node.value, (Expression value) {
562 return new Assignment(leftHandSide, value);
563 }, store: false);
564 } else if (leftHandSide is PropertyAccess) {
565 return withExpressions(
566 [leftHandSide.receiver, leftHandSide.selector, node.value],
567 (evaluated) {
568 return new Assignment.compound(
569 new PropertyAccess(evaluated[0], evaluated[1]),
570 node.op,
571 evaluated[2]);
572 });
573 } else {
574 throw "Unexpected assignment left hand side $leftHandSide";
575 }
576 }
577
578 @override
579 Expression visitAwait(Await node) {
580 int afterWait = newLabel("returning from await.");
581 withExpression(node.expression, (Expression expression) {
582 addStatement(setGotoVariable(afterWait));
583 if (errorHandlerLabels.isEmpty) {
584 addStatement(js.statement("""
585 return #thenHelper(#expression, $helperName, $controllerName, null);
586 """, {"thenHelper": thenHelper, "expression": expression}));
587 } else {
588 addStatement(js.statement("""
589 return #thenHelper(#expression, $helperName, $controllerName,
590 function($errorName) {
591 $gotoName = #currentHandler;
592 $helperName($errorName);
593 });""", {"thenHelper": thenHelper,
594 "expression": expression,
595 "currentHandler": currentErrorHandler}));
596 }
597 }, store: false);
598 beginLabel(afterWait);
599 return new VariableUse(resultName);
600 }
601
602 /// Checks if [node] is the variable named [resultName].
603 ///
604 /// [resultName] is used to hold the result of a transformed computation
605 /// for example the result of awaiting, or the result of a conditional or
606 /// short-circuiting expression.
607 /// If the subexpression of some transformed node already is transformed and
608 /// visiting it returns [resultName], we can avoid reduntdantly assigning it
floitsch 2015/02/04 12:31:28 redundantly
sigurdm 2015/02/05 14:06:03 Done.
609 /// to itself again.
610 bool isResult(Expression node) {
611 return node is VariableUse && node.name == resultName;
612 }
613
614 @override
615 Expression visitBinary(Binary node) {
616 if (shouldTransform(node.right) &&
617 (node.op == "||" || node.op == "&&")) {
618 int thenLabel = newLabel("then");
619 int joinLabel = newLabel("join");
620 withExpression(node.left, (Expression left) {
621 Statement assignLeft = isResult(left)
622 ? new Block.empty()
623 : new ExpressionStatement(
624 new Assignment(new VariableUse(resultName), left));
625 if (node.op == "||") {
626 addStatement(new If(left, gotoAndBreak(thenLabel), assignLeft));
627 } else {
628 assert(node.op == "&&");
629 addStatement(new If(left, assignLeft, gotoAndBreak(thenLabel)));
630 }
631 }, store: true);
632 addGoto(joinLabel);
633 beginLabel(thenLabel);
634 withExpression(node.right, (Expression value) {
635 if (!isResult(value)) {
636 addExpressionStatement(
637 new Assignment(new VariableUse(resultName), value));
638 }
639 }, store: false);
640 beginLabel(joinLabel);
641 return new VariableUse(resultName);
642 }
643
644 return withExpression2(
645 node.left,
646 node.right,
647 (left, right) => new Binary(node.op, left, right));
648 }
649
650 @override
651 visitBlob(Blob node) {
652 return node;
653 }
654
655 @override
656 void visitBlock(Block node) {
657 for (Statement statement in node.statements) {
658 visitStatement(statement);
659 }
660 }
661
662 @override
663 void visitBreak(Break node) {
664 Node target = analysis.targets[node];
665 if (!shouldTransform(target)) {
666 addStatement(node);
667 return;
668 }
669 translateJump(target, breakLabels[target]);
670 }
671
672 @override
673 Expression visitCall(Call node) {
674 bool storeTarget = node.arguments.any(shouldTransform);
675 return withExpression(node.target, (target) {
676 return withExpressions(node.arguments, (List<Expression> arguments) {
677 return new Call(target, arguments);
678 });
679 }, store: storeTarget);
680 }
681
682 @override
683 visitCase(Case node) {
684 return unreachable(node);
685 }
686
687 @override
688 visitCatch(Catch node) {
689 return unreachable(node);
690 }
691
692 @override
693 visitComment(Comment node) {
694 addStatement(node);
695 }
696
697 @override
698 Expression visitConditional(Conditional node) {
699 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
700 return withExpression(node.condition, (Expression condition) {
701 return new Conditional(condition, node.then, node.otherwise);
702 });
703 }
704 int thenLabel = newLabel("then");
705 int joinLabel = newLabel("join");
706 int elseLabel = newLabel("else");
707 withExpression(node.condition, (Expression condition) {
708 addExpressionStatement(
709 new Assignment(
710 new VariableUse(gotoName),
711 new Conditional(
712 condition,
713 new LiteralNumber("$thenLabel"),
714 new LiteralNumber("$elseLabel"))));
715 }, store: false);
716 addBreak();
717 beginLabel(thenLabel);
718 withExpression(node.then, (Expression value) {
719 if (!isResult(value)) {
720 addExpressionStatement(
721 new Assignment(new VariableUse(resultName), value));
722 }
723 }, store: false);
724 addGoto(joinLabel);
725 beginLabel(elseLabel);
726 withExpression(node.otherwise, (Expression value) {
727 if (!isResult(value)) {
728 addExpressionStatement(
729 new Assignment(new VariableUse(resultName), value));
730 }
731 }, store: false);
732 beginLabel(joinLabel);
733 return new VariableUse(resultName);
734 }
735
736 @override
737 visitContinue(Continue node) {
738 Node target = analysis.targets[node];
739 if (!shouldTransform(target)) {
740 addStatement(node);
741 return;
742 }
743 translateJump(target, continueLabels[target]);
744 }
745
746 /// Returns a break statement that exits the big switch statement.
747 Statement breakStatement() {
floitsch 2015/02/04 12:31:28 Inline into addBreak. The only other use can be re
floitsch 2015/02/04 12:31:28 has a getter name.
sigurdm 2015/02/05 14:06:03 Acknowledged.
sigurdm 2015/02/05 14:06:03 Done.
748 if (insideUntranslatedBreakable) {
749 hasJumpThoughOuterLabel = true;
750 return new Break(outerLabelName);
751 } else {
752 return new Break(null);
753 }
754 }
755
756 /// Convenience wrapper around [breakStatement].
757 void addBreak() {
758 addStatement(breakStatement());
759 }
760
761 /// Common code for handling break, continue, return.
762 ///
763 /// It is necessary to run all nesting finally-handlers between the jump and
764 /// the target. For that we use [nextName] as a stack of places to go.
765 void translateJump(Node target, int targetLabel) {
766 // Compute a stack of all the 'finally' nodes we must visit before the jump.
767 // The bottom of the stack is the label where the jump goes to.
768 List<int> jumpStack = new List<int>();
769 for (Node node in targetsAndTries.reversed) {
770 if (node is Try) {
771 assert(node.finallyPart != null);
772 jumpStack.add(finallyLabels[node]);
773 } else if (node == target) {
774 jumpStack.add(targetLabel);
775 break;
776 }
777 // Ignore other nodes.
778 }
779 jumpStack = jumpStack.reversed.toList();
780 // We jump directly to the top of the stack, so take it off now.
781 int firstTarget = jumpStack.removeLast();
782 if (jumpStack.isNotEmpty) {
783 hasJumpThroughFinally = true;
784 Expression jsJumpStack = new ArrayInitializer(
785 jumpStack.map((int label) => new LiteralNumber("$label"))
786 .toList());
787 addStatement(js.statement("$nextName = #", [jsJumpStack]));
788 }
789 addGoto(firstTarget);
790 }
791
792 @override
793 visitDefault(Default node) => unreachable(node);
794
795 @override
796 visitDo(Do node) {
797 if (!shouldTransform(node)) {
798 bool oldInsideUntranslatedBreakable = insideUntranslatedBreakable;
799 insideUntranslatedBreakable = true;
800 withExpression(node.condition, (Expression condition) {
801 addStatement(new Do(translateInBlock(node.body), condition));
802 }, store: false);
803 insideUntranslatedBreakable = oldInsideUntranslatedBreakable;
804 return;
805 }
806 int startLabel = newLabel("do body");
807
808 int continueLabel = newLabel("do condition");
809 continueLabels[node] = continueLabel;
810
811 int afterLabel = newLabel("after do");
812 breakLabels[node] = afterLabel;
813
814 beginLabel(startLabel);
815
816 targetsAndTries.add(node);
817 visitStatement(node.body);
818 targetsAndTries.removeLast();
819
820 beginLabel(continueLabel);
821 withExpression(node.condition, (Expression condition) {
822 addStatement(new If.noElse(condition,
823 gotoAndBreak(startLabel)));
824 }, store: false);
825 beginLabel(afterLabel);
826 }
827
828 @override
829 visitEmptyStatement(EmptyStatement node) => node;
830
831 visitExpressionInStatementContext(Expression node) {
832 if (node is VariableDeclarationList) {
833 // Treat VariableDeclarationList as a statement.
834 visitVariableDeclarationList(node);
835 } else {
836 visitExpressionIgnoreResult(node);
837 }
838 }
839
840 @override
841 visitExpressionStatement(ExpressionStatement node) {
842 visitExpressionInStatementContext(node.expression);
843 }
844
845 @override
846 visitFor(For node) {
847 if (!shouldTransform(node) ) {
848 bool oldInsideUntranslated = insideUntranslatedBreakable;
849 insideUntranslatedBreakable = true;
850 // Note that node.init, node.condition, node.update all can be null, but
851 // withExpressions handles that.
852 withExpressions([node.init, node.condition, node.update],
853 (List<Expression> transformed) {
854 addStatement(new For(transformed[0],
855 transformed[1],
856 transformed[2],
857 translateInBlock(node.body)));
858 });
859 insideUntranslatedBreakable = oldInsideUntranslated;
860 return;
861 }
862
863 if (node.init != null) {
864 visitExpressionInStatementContext(node.init);
865 }
866 int startLabel = newLabel("for condition");
867 // If there is no update, continuing the loop is the same as going to the
868 // start.
869 int continueLabel = (node.update == null)
870 ? startLabel
871 : newLabel("for update");
872 continueLabels[node] = continueLabel;
873 int afterLabel = newLabel("after for");
874 breakLabels[node] = afterLabel;
875 beginLabel(startLabel);
876 Expression condition = node.condition;
877 if (condition != null &&
878 !(condition is LiteralBool && condition.value == true)) {
879 // No condition is the same as true. We don't have to check.
880 withExpression(node.condition, (Expression condition) {
881 addStatement(new If.noElse(new Prefix("!", condition),
882 gotoAndBreak(afterLabel)));
883 }, store: false);
884 }
885 targetsAndTries.add(node);
886 visitStatement(node.body);
887 targetsAndTries.removeLast();
888 if (node.update != null) {
889 beginLabel(continueLabel);
890 visitExpressionIgnoreResult(node.update);
891 }
892 addGoto(startLabel);
893 beginLabel(afterLabel);
894 }
895
896 @override
897 visitForIn(ForIn node) {
898 // The dart output currently never uses for-in loops.
899 throw "Javascript for-in not implemented yet in the await transformation";
900 }
901
902 @override
903 visitFunctionDeclaration(FunctionDeclaration node) {
904 return node;
905 }
906
907 // Only used for code where `!shouldTransform(node)`.
908 Block translateInBlock(Statement node) {
909 assert(!shouldTransform(node));
910 List<Statement> oldBuffer = currentStatementBuffer;
911 currentStatementBuffer = new List();
912 List<Statement> resultBuffer = currentStatementBuffer;
913 visitStatement(node);
914 currentStatementBuffer = oldBuffer;
915 return new Block(resultBuffer);
916 }
917
918 @override
919 visitIf(If node) {
920 if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
921 withExpression(node.condition, (Expression condition) {
922 addStatement(
923 new If(
924 condition,
925 translateInBlock(node.then),
926 translateInBlock(node.otherwise)));
927 }, store: false);
928 return;
929 }
930 int thenLabel = newLabel("then");
931 int joinLabel = newLabel("join");
932 int elseLabel =
933 node.otherwise is EmptyStatement ? joinLabel : newLabel("else");
934
935 withExpression(node.condition, (Expression condition) {
936 addExpressionStatement(
937 new Assignment(
938 new VariableUse(gotoName),
939 new Conditional(
940 condition,
941 new LiteralNumber("$thenLabel"),
942 new LiteralNumber("$elseLabel"))));
943 }, store: false);
944 addBreak();
945 beginLabel(thenLabel);
946 visitStatement(node.then);
947 if (node.otherwise is! EmptyStatement) {
948 addGoto(joinLabel);
949 beginLabel(elseLabel);
950 visitStatement(node.otherwise);
951 }
952 beginLabel(joinLabel);
953 }
954
955 @override
956 visitInterpolatedExpression(InterpolatedExpression node) => unsupported(node);
957
958 @override
959 visitInterpolatedLiteral(InterpolatedLiteral node) => unsupported(node);
960
961 @override
962 visitInterpolatedParameter(InterpolatedParameter node) => unsupported(node);
963
964 @override
965 visitInterpolatedSelector(InterpolatedSelector node) => unsupported(node);
966
967 @override
968 visitInterpolatedStatement(InterpolatedStatement node) => unsupported(node);
969
970 @override
971 visitLabeledStatement(LabeledStatement node) {
972 if (!shouldTransform(node)) {
973 addStatement(
974 new LabeledStatement(node.label, translateInBlock(node.body)));
975 return;
976 }
977 int breakLabel = newLabel("break ${node.label}");
978 int continueLabel = newLabel("continue ${node.label}");
979 breakLabels[node] = breakLabel;
980 continueLabels[node] = continueLabel;
981
982 beginLabel(continueLabel);
983 targetsAndTries.add(node);
984 visitStatement(node.body);
985 targetsAndTries.removeLast();
986 beginLabel(breakLabel);
987 }
988
989 @override
990 visitLiteralBool(LiteralBool node) => node;
991
992 @override
993 visitLiteralExpression(LiteralExpression node) => unsupported(node);
994
995 @override
996 visitLiteralNull(LiteralNull node) => node;
997
998 @override
999 visitLiteralNumber(LiteralNumber node) => node;
1000
1001 @override
1002 visitLiteralStatement(LiteralStatement node) => unsupported(node);
1003
1004 @override
1005 visitLiteralString(LiteralString node) => node;
1006
1007 @override
1008 visitNamedFunction(NamedFunction node) {
1009 throw new UnimplementedError("Internal error: "
1010 "Named functions are not handled yet by the javascript async "
1011 "transform");
1012 }
1013
1014 @override
1015 visitNew(New node) {
1016 bool storeTarget = node.arguments.any(shouldTransform);
1017 return withExpression(node.target, (target) {
1018 return withExpressions(node.arguments, (List<Expression> arguments) {
1019 return new New(target, arguments);
1020 });
1021 }, store: storeTarget);
1022 }
1023
1024 @override
1025 visitObjectInitializer(ObjectInitializer node) {
1026 return withExpressions(
1027 node.properties.map((Property property) => property.value).toList(),
1028 (List<Node> values) {
1029 List<Property> properties = new List.generate(values.length, (int i) {
1030 return new Property(node.properties[i].name, values[i]);
1031 });
1032 return new ObjectInitializer(properties);
1033 });
1034 }
1035
1036 @override
1037 visitParameter(Parameter node) => unreachable(node);
1038
1039 @override
1040 visitPostfix(Postfix node) {
1041 if (node.op == "++" || node.op == "--") {
1042 Expression argument = node.argument;
1043 if (argument is VariableUse) {
1044 return new Postfix(node.op, argument);
1045 } else if (argument is PropertyAccess) {
1046 return withExpression2(argument.receiver, argument.selector,
1047 (receiver, selector) {
1048 return new Postfix(node.op, new PropertyAccess(receiver, selector));
1049 });
1050 } else {
1051 throw "Unexpected postfix ${node.op} "
1052 "operator argument ${node.argument}";
1053 }
1054 }
1055 return withExpression(
1056 node.argument,
1057 (Expression argument) => new Postfix(node.op, argument),
1058 store: false);
1059 }
1060
1061 @override
1062 visitPrefix(Prefix node) {
1063 if (node.op == "++" || node.op == "--") {
1064 Expression argument = node.argument;
1065 if (argument is VariableUse) {
1066 return new Prefix(node.op, argument);
1067 } else if (argument is PropertyAccess) {
1068 return withExpression2(argument.receiver, argument.selector,
1069 (receiver, selector) {
1070 return new Prefix(node.op, new PropertyAccess(receiver, selector));
1071 });
1072 } else {
1073 throw "Unexpected prefix ${node.op} operator "
1074 "argument ${node.argument}";
1075 }
1076 }
1077 return withExpression(
1078 node.argument,
1079 (Expression argument) => new Prefix(node.op, argument),
1080 store: false);
1081 }
1082
1083 @override
1084 visitProgram(Program node) => throw "Unsupported";
1085
1086 @override
1087 visitProperty(Property node) {
1088 return withExpression(
1089 node.value,
1090 (Expression value) => new Property(node.name, value),
1091 store: false);
1092 }
1093
1094 @override
1095 visitRegExpLiteral(RegExpLiteral node) => node;
1096
1097 @override
1098 visitReturn(Return node) {
1099 assert(node.value == null || !isSyncStar && !isAsyncStar);
1100 Node target = analysis.targets[node];
1101 if (node.value != null) {
1102 withExpression(node.value, (Expression value) {
1103 addStatement(js.statement("$returnValueName = #", [value]));
1104 }, store: false);
1105 }
1106 translateJump(target, returnLabel);
1107 }
1108
1109 @override
1110 visitSwitch(Switch node) {
1111 if (!node.cases.any(shouldTransform)) {
1112 // If only the key has an await, translation can be simplified.
1113 bool oldInsideUntranslated = insideUntranslatedBreakable;
1114 insideUntranslatedBreakable = true;
1115 withExpression(node.key, (Expression key) {
1116 List<SwitchClause> cases = node.cases.map((SwitchClause clause) {
1117 if (clause is Case) {
1118 return new Case(clause.expression, translateInBlock(clause.body));
1119 } else if (clause is Default) {
1120 return new Default(translateInBlock(clause.body));
1121 }
1122 }).toList();
1123 addStatement(new Switch(key, cases));
1124 }, store: false);
1125 insideUntranslatedBreakable = oldInsideUntranslated;
1126 return;
1127 }
1128 int before = newLabel("switch");
1129 int after = newLabel("after switch");
1130 breakLabels[node] = after;
1131
1132 beginLabel(before);
1133 List<int> labels = new List<int>(node.cases.length);
1134
1135 if (!node.cases.every(
1136 (SwitchClause x) => !(x is Case && shouldTransform(x.expression)))) {
1137 int defaultIndex = null; // Null means no default was found.
1138 // If there is an await in one of the keys, we have to use a chain of ifs.
1139
1140 withExpression(node.key, (Expression key) {
1141 int i = 0;
1142 for (SwitchClause clause in node.cases) {
1143 if (clause is Default) {
1144 // The goto for the default case is added after all non-default
1145 // clauses have been handled.
1146 defaultIndex = i;
1147 labels[i] = newLabel("default");
1148 continue;
1149 } else if (clause is Case) {
1150 labels[i] = newLabel("case");
1151 withExpression(clause.expression, (expression) {
1152 addStatement(
1153 new If.noElse(new Binary("===", key, expression),
1154 gotoAndBreak(labels[i])));
1155 }, store: false);
1156 }
1157 i++;
1158 }
1159 }, store: true);
1160
1161 if (defaultIndex == null) {
1162 addGoto(after);
1163 } else {
1164 addGoto(labels[defaultIndex]);
1165 }
1166
1167 } else {
1168 bool hasDefault = false;
1169 int i = 0;
1170 List<SwitchClause> clauses = new List<SwitchClause>();
1171 for (SwitchClause clause in node.cases) {
1172 if (clause is Case) {
1173 labels[i] = newLabel("case");
1174 clauses.add(new Case(clause.expression, gotoAndBreak(labels[i])));
1175 } else if (i is Default) {
1176 labels[i] = newLabel("default");
1177 clauses.add(new Default(gotoAndBreak(labels[i])));
1178 hasDefault = true;
1179 } else {
1180 diagnosticListener.internalError(spannable,
1181 "Unknown clause type $clause");
1182 }
1183 i++;
1184 }
1185 withExpression(node.key, (Expression key) {
1186 addStatement(new Switch(key, clauses));
1187 }, store: false);
1188 if (!hasDefault) {
1189 addGoto(after);
1190 }
1191 }
1192
1193 targetsAndTries.add(node);
1194 for (int i = 0; i < labels.length; i++) {
1195 beginLabel(labels[i]);
1196 visitStatement(node.cases[i].body);
1197 }
1198 beginLabel(after);
1199 targetsAndTries.removeLast();
1200 }
1201
1202 @override
1203 visitThis(This node) {
1204 return new VariableUse(selfName);
1205 }
1206
1207 @override
1208 visitThrow(Throw node) {
1209 withExpression(node.expression, (Expression expression) {
1210 addStatement(new Throw(expression));
1211 }, store: false);
1212 }
1213
1214 setHandler() {
1215 addExpressionStatement(
1216 new Assignment(new VariableUse(handlerName), currentErrorHandler));
1217 }
1218
1219 @override
1220 void visitTry(Try node) {
1221 if (!shouldTransform(node)) {
1222 Block body = translateInBlock(node.body);
1223 Catch catchPart = (node.catchPart == null)
1224 ? null
1225 : new Catch(node.catchPart.declaration,
1226 translateInBlock(node.catchPart.body));
1227 Block finallyPart = (node.finallyPart == null)
1228 ? null
1229 : translateInBlock(node.finallyPart);
1230 addStatement(new Try(body, catchPart, finallyPart));
1231 return;
1232 }
1233 hasTryBlocks = true;
1234 int handlerLabel = newLabel("catch");
1235 int finallyLabel = newLabel("finally");
1236 int afterFinallyLabel = newLabel("after finally");
1237 errorHandlerLabels.add(handlerLabel);
1238 // We set the error handler here. It must be cleared on every path out.
floitsch 2015/02/04 12:31:28 out; normal and error exit.
sigurdm 2015/02/05 14:06:03 Done.
1239 // Normal and error exit.
1240 setHandler();
1241 if (node.finallyPart != null) {
1242 finallyLabels[node] = finallyLabel;
1243 targetsAndTries.add(node);
1244 }
1245 visitStatement(node.body);
1246 errorHandlerLabels.removeLast();
1247 setHandler();
1248 addStatement(js.statement("$nextName = [#];",
1249 [new LiteralNumber("$afterFinallyLabel")]));
1250 if (node.finallyPart != null) {
1251 addGoto(finallyLabel);
1252 } else {
1253 addGoto(afterFinallyLabel);
1254 }
1255 beginLabel(handlerLabel);
1256 if (node.catchPart != null) {
1257 setHandler();
1258 // The catch declaration name can shadow outer variables, so we need
1259 // a fresh name to avoid collisions.
1260 String errorRename = freshName(node.catchPart.declaration.name);
1261 localVariables.add(new VariableDeclaration(errorRename));
1262 variableRenamings.add(
1263 new Pair(node.catchPart.declaration.name, errorRename));
1264 addExpressionStatement(
1265 new Assignment(new VariableUse(errorRename),
1266 new VariableUse(resultName)));
1267 visitStatement(node.catchPart.body);
1268 variableRenamings.removeLast();
1269 }
1270 if (node.finallyPart != null) {
1271 targetsAndTries.removeLast();
1272 // This belongs to the catch-part, but is only needed if there is a
1273 // finally therefore it is in this branch.
1274 addStatement(js.statement("$nextName = [#];",
1275 [new LiteralNumber("$afterFinallyLabel")]));
1276 beginLabel(finallyLabel);
1277 visitStatement(node.finallyPart);
1278 addStatement(new Comment("// goto the next finally handler"));
1279 addStatement(js.statement("$gotoName = $nextName.pop();"));
1280 addBreak();
1281 }
1282 beginLabel(afterFinallyLabel);
1283 }
1284
1285 @override
1286 visitVariableDeclaration(VariableDeclaration node) {
1287 throw "TODO";
1288 }
1289
1290 @override
1291 void visitVariableDeclarationList(VariableDeclarationList node) {
1292 // Declaration of local variables is hoisted outside the helper but the
1293 // initialization is done here.
1294 for (VariableInitialization initialization in node.declarations) {
1295 VariableDeclaration declaration = initialization.declaration;
1296 localVariables.add(declaration);
1297 if (initialization.value != null) {
1298 withExpression(initialization.value, (Expression value) {
1299 addStatement(
1300 new ExpressionStatement(new Assignment(declaration, value)));
1301 }, store: false);
1302 }
1303 }
1304 }
1305
1306 @override
1307 visitVariableInitialization(VariableInitialization node) {
1308 // These should be handled by visitVariableDeclarationList.
1309 throw "Unexpected variableInitialization";
1310 }
1311
1312 @override
1313 visitVariableUse(VariableUse node) {
1314 String renaming = variableRenamings.lastWhere(
1315 (Pair renaming) => renaming.a == node.name,
1316 orElse: () => new Pair(null, null)).b;
floitsch 2015/02/04 12:31:28 The ".b" disappears now. It must be on its own lin
sigurdm 2015/02/05 14:06:03 Done.
1317 if (renaming == null) return node;
1318 return new VariableUse(renaming);
1319 }
1320
1321 @override
1322 visitWhile(While node) {
1323 if (!shouldTransform(node)) {
1324 bool oldInsideUntranslated = insideUntranslatedBreakable;
1325 insideUntranslatedBreakable = true;
1326 withExpression(node.condition, (Expression condition) {
1327 addStatement(new While(condition, translateInBlock(node.body)));
1328 }, store: false);
1329 insideUntranslatedBreakable = oldInsideUntranslated;
1330 return;
1331 }
1332 int continueLabel = newLabel("while condition");
1333 continueLabels[node] = continueLabel;
1334 beginLabel(continueLabel);
1335
1336 int afterLabel = newLabel("after while");
1337 breakLabels[node] = afterLabel;
1338 Expression condition = node.condition;
1339 // If the condition is `true`, we don't need a test.
1340 if (!(condition is LiteralBool && condition.value == true)) {
1341 withExpression(node.condition, (Expression condition) {
1342 addStatement(new If.noElse(new Prefix("!", condition),
1343 gotoAndBreak(afterLabel)));
1344 }, store: false);
1345 }
1346 targetsAndTries.add(node);
1347 visitStatement(node.body);
1348 targetsAndTries.removeLast();
1349 addGoto(continueLabel);
1350 beginLabel(afterLabel);
1351 }
1352
1353 @override
1354 visitDartYield(DartYield node) {
1355 assert(isSyncStar || isAsyncStar);
1356 if (isSyncStar) {
floitsch 2015/02/04 12:31:28 move the isSyncStar check into the withExpression:
sigurdm 2015/02/05 14:06:04 Done.
1357 int label = newLabel("after yield");
1358 withExpression(node.expression, (Expression expression) {
1359 // We don't do a break here for the goto, but instead a return below.
1360 addStatement(setGotoVariable(label));
1361 if (node.hasStar) {
1362 addStatement(new Return(new Call(yieldStarExpression, [expression])));
1363 } else {
1364 addStatement(new Return(expression));
1365 }
1366 beginLabel(label);
1367 }, store: false);
1368 } else {
1369 int label = newLabel("after yield");
1370 withExpression(node.expression, (Expression expression) {
1371 // We don't do a break here for the goto, but instead a return below.
1372 addStatement(setGotoVariable(label));
1373 // Find all the finally blocks that should be performed if the stream is
1374 // canceled during the yield.
1375 // At the bottom of the stack is the return label.
1376 List<int> enclosingFinallyLabels = <int>[returnLabel];
1377 enclosingFinallyLabels.addAll(targetsAndTries
1378 .where((Node node) => node is Try)
1379 .map((Try node) => finallyLabels[node]));
1380 int destinationOnCancel = enclosingFinallyLabels.removeLast();
1381 addStatement(js.statement("""
1382 return #thenHelper(#yieldExpression(#expression),
floitsch 2015/02/04 12:31:28 thenHelper really isn't the best name anymore... (
sigurdm 2015/02/05 14:06:03 True - will add todo to rename
1383 $helperName, $controllerName, function () {
1384 if (#notEmptyFinallyList) {
1385 $nextName = #finallyList;
1386 }
1387 $gotoName = #destinationOnCancel;
1388 $helperName();
1389 });""",
1390 {"thenHelper": thenHelper,
1391 "yieldExpression": node.hasStar
1392 ? yieldStarExpression
1393 : yieldExpression,
1394 "expression": expression,
1395 "notEmptyFinallyList": enclosingFinallyLabels.isNotEmpty,
1396 "finallyList": new ArrayInitializer(enclosingFinallyLabels.map(
floitsch 2015/02/04 12:31:28 Generate the array-initializer before. put ".map"
sigurdm 2015/02/05 14:06:03 Done.
1397 (int label) => new LiteralNumber("$label")).toList()),
1398 "destinationOnCancel": new LiteralNumber("$destinationOnCancel")
1399 }));
1400 beginLabel(label);
1401 }, store: false);
1402 }
1403 }
1404 }
1405
1406 /// Finds out
1407 ///
1408 /// - which expressions have yield or await nested in them.
1409 /// - targets of jumps
1410 /// - a set of used names.
1411 /// - if any [This]-expressions are used.
1412 class PreTranslationAnalysis extends NodeVisitor<bool> {
1413 Set<Node> hasAwaitOrYield = new Set<Node>();
1414
1415 Map<Node, Node> targets = new Map<Node, Node>();
1416 List<Node> loopsAndSwitches = new List<Node>();
1417 List<LabeledStatement> labelledStatements = new List<LabeledStatement>();
1418 Set<String> usedNames = new Set<String>();
1419
1420 bool hasExplicitReturns = false;
1421
1422 bool hasThis = false;
1423
1424 // The function currently being analyzed.
1425 Fun currentFunction;
1426
1427 // For error messages.
1428 final Function unsupported;
1429
1430 PreTranslationAnalysis(this.unsupported);
floitsch 2015/02/04 12:31:28 Type unupported here: PreTranslationAnalysis(void
sigurdm 2015/02/05 14:06:03 Done.
1431
1432 bool visit(Node node) {
1433 bool containsAwait = node.accept(this);
1434 if (containsAwait) {
1435 hasAwaitOrYield.add(node);
1436 }
1437 return containsAwait;
1438 }
1439
1440 analyze(Fun node) {
1441 currentFunction = node;
1442 node.params.forEach(visit);
1443 visit(node.body);
1444 }
1445
1446 @override
1447 bool visitAccess(PropertyAccess node) {
1448 bool receiver = visit(node.receiver);
1449 bool selector = visit(node.selector);
1450 return receiver || selector;
1451 }
1452
1453 @override
1454 bool visitArrayHole(ArrayHole node) {
1455 return false;
1456 }
1457
1458 @override
1459 bool visitArrayInitializer(ArrayInitializer node) {
1460 bool containsAwait = false;
1461 for (Expression element in node.elements) {
1462 if (visit(element)) containsAwait = true;
1463 }
1464 return containsAwait;
1465 }
1466
1467 @override
1468 bool visitAssignment(Assignment node) {
1469 bool leftHandSide = visit(node.leftHandSide);
1470 bool value = (node.value == null) ? false : visit(node.value);
1471 return leftHandSide || value;
1472 }
1473
1474 @override
1475 bool visitAwait(Await node) {
1476 visit(node.expression);
1477 return true;
1478 }
1479
1480 @override
1481 bool visitBinary(Binary node) {
1482 bool left = visit(node.left);
1483 bool right = visit(node.right);
1484 return left || right;
1485 }
1486
1487 @override
1488 bool visitBlob(Blob node) {
1489 return false;
1490 }
1491
1492 @override
1493 bool visitBlock(Block node) {
1494 bool containsAwait = false;
1495 for (Statement statement in node.statements) {
1496 if (visit(statement)) containsAwait = true;
1497 }
1498 return containsAwait;
1499 }
1500
1501 @override
1502 bool visitBreak(Break node) {
1503 if (node.targetLabel != null) {
1504 targets[node] = labelledStatements.lastWhere(
1505 (LabeledStatement statement) => statement.label == node.targetLabel);
1506 } else {
1507 targets[node] = loopsAndSwitches.last;
1508 }
1509 return false;
1510 }
1511
1512 @override
1513 bool visitCall(Call node) {
1514 bool containsAwait = visit(node.target);
1515 for (Expression argument in node.arguments) {
1516 if (visit(argument)) containsAwait = true;
1517 }
1518 return containsAwait;
1519 }
1520
1521 @override
1522 bool visitCase(Case node) {
1523 bool expression = visit(node.expression);
1524 bool body = visit(node.body);
1525 return expression || body;
1526 }
1527
1528 @override
1529 bool visitCatch(Catch node) {
1530 bool declaration = visit(node.declaration);
1531 bool body = visit(node.body);
1532 return declaration || body;
1533 }
1534
1535 @override
1536 bool visitComment(Comment node) {
1537 return false;
1538 }
1539
1540 @override
1541 bool visitConditional(Conditional node) {
1542 bool condition = visit(node.condition);
1543 bool then = visit(node.then);
1544 bool otherwise = visit(node.otherwise);
1545 return condition || then || otherwise;
1546 }
1547
1548 @override
1549 bool visitContinue(Continue node) {
1550 if (node.targetLabel != null) {
1551 targets[node] = labelledStatements.lastWhere(
1552 (LabeledStatement stm) => stm.label == node.targetLabel);
1553 } else {
1554 targets[node] =
1555 loopsAndSwitches.lastWhere((Node node) => node is! Switch);
1556 }
1557 assert(targets[node] is Loop || targets[node] is LabeledStatement);
floitsch 2015/02/04 12:31:28 If the target is a labeledstatement, then its cont
sigurdm 2015/02/05 14:06:03 Aha - makes more sense. Done
1558 return false;
1559 }
1560
1561 @override
1562 bool visitDefault(Default node) {
1563 return visit(node.body);
1564 }
1565
1566 @override
1567 bool visitDo(Do node) {
1568 loopsAndSwitches.add(node);
1569 bool body = visit(node.body);
1570 bool condition = visit(node.condition);
1571 loopsAndSwitches.removeLast();
1572 return body || condition;
1573 }
1574
1575 @override
1576 bool visitEmptyStatement(EmptyStatement node) {
1577 return false;
1578 }
1579
1580 @override
1581 bool visitExpressionStatement(ExpressionStatement node) {
1582 return visit(node.expression);
1583 }
1584
1585 @override
1586 bool visitFor(For node) {
1587 bool init = (node.init == null) ? false : visit(node.init);
1588 bool condition = (node.condition == null) ? false : visit(node.condition);
1589 bool update = (node.update == null) ? false : visit(node.update);
1590 loopsAndSwitches.add(node);
1591 bool body = visit(node.body);
1592 loopsAndSwitches.removeLast();
1593 return init || condition || update || body;
1594 }
1595
1596 @override
1597 bool visitForIn(ForIn node) {
1598 bool object = visit(node.object);
1599 loopsAndSwitches.add(node);
1600 bool body = visit(node.body);
1601 loopsAndSwitches.removeLast();
1602 return object || body;
1603 }
1604
1605 @override
1606 bool visitFun(Fun node) {
1607 return false;
1608 }
1609
1610 @override
1611 bool visitFunctionDeclaration(FunctionDeclaration node) {
1612 return false;
1613 }
1614
1615 @override
1616 bool visitIf(If node) {
1617 bool condition = visit(node.condition);
1618 bool then = visit(node.then);
1619 bool otherwise = visit(node.otherwise);
1620 return condition || then || otherwise;
1621 }
1622
1623 @override
1624 bool visitInterpolatedExpression(InterpolatedExpression node) {
1625 return unsupported(node);
1626 }
1627
1628 @override
1629 bool visitInterpolatedLiteral(InterpolatedLiteral node) {
1630 return unsupported(node);
1631 }
1632
1633 @override
1634 bool visitInterpolatedParameter(InterpolatedParameter node) {
1635 return unsupported(node);
1636 }
1637
1638 @override
1639 bool visitInterpolatedSelector(InterpolatedSelector node) {
1640 return unsupported(node);
1641 }
1642
1643 @override
1644 bool visitInterpolatedStatement(InterpolatedStatement node) {
1645 return unsupported(node);
1646 }
1647
1648 @override
1649 bool visitLabeledStatement(LabeledStatement node) {
1650 usedNames.add(node.label);
1651 labelledStatements.add(node);
1652 bool containsAwait = visit(node.body);
1653 labelledStatements.removeLast();
1654 return containsAwait;
1655 }
1656
1657 @override
1658 bool visitLiteralBool(LiteralBool node) {
1659 return false;
1660 }
1661
1662 @override
1663 bool visitLiteralExpression(LiteralExpression node) {
1664 return unsupported(node);
1665 }
1666
1667 @override
1668 bool visitLiteralNull(LiteralNull node) {
1669 return false;
1670 }
1671
1672 @override
1673 bool visitLiteralNumber(LiteralNumber node) {
1674 return false;
1675 }
1676
1677 @override
1678 bool visitLiteralStatement(LiteralStatement node) {
1679 return unsupported(node);
1680 }
1681
1682 @override
1683 bool visitLiteralString(LiteralString node) {
1684 return false;
1685 }
1686
1687 @override
1688 bool visitNamedFunction(NamedFunction node) {
1689 return false;
1690 }
1691
1692 @override
1693 bool visitNew(New node) {
1694 return visitCall(node);
1695 }
1696
1697 @override
1698 bool visitObjectInitializer(ObjectInitializer node) {
1699 bool containsAwait = false;
1700 for (Property property in node.properties) {
1701 if (visit(property)) containsAwait = true;
1702 }
1703 return containsAwait;
1704 }
1705
1706 @override
1707 bool visitParameter(Parameter node) {
1708 usedNames.add(node.name);
1709 return false;
1710 }
1711
1712 @override
1713 bool visitPostfix(Postfix node) {
1714 return visit(node.argument);
1715 }
1716
1717 @override
1718 bool visitPrefix(Prefix node) {
1719 return visit(node.argument);
1720 }
1721
1722 @override
1723 bool visitProgram(Program node) {
1724 throw "Unexpected";
1725 }
1726
1727 @override
1728 bool visitProperty(Property node) {
1729 return visit(node.value);
1730 }
1731
1732 @override
1733 bool visitRegExpLiteral(RegExpLiteral node) {
1734 return false;
1735 }
1736
1737 @override
1738 bool visitReturn(Return node) {
1739 hasExplicitReturns = true;
1740 targets[node] = currentFunction;
1741 if (node.value == null) return false;
1742 return visit(node.value);
1743 }
1744
1745 @override
1746 bool visitSwitch(Switch node) {
1747 loopsAndSwitches.add(node);
1748 bool result = visit(node.key);
1749 for (SwitchClause clause in node.cases) {
1750 if (visit(clause)) result = true;
1751 }
1752 loopsAndSwitches.removeLast();
1753 return result;
1754 }
1755
1756 @override
1757 bool visitThis(This node) {
1758 hasThis = true;
1759 return false;
1760 }
1761
1762 @override
1763 bool visitThrow(Throw node) {
1764 return visit(node.expression);
1765 }
1766
1767 @override
1768 bool visitTry(Try node) {
1769 bool body = visit(node.body);
1770 bool catchPart = (node.catchPart == null) ? false : visit(node.catchPart);
1771 bool finallyPart = (node.finallyPart == null)
1772 ? false
1773 : visit(node.finallyPart);
1774 return body || catchPart || finallyPart;
1775 }
1776
1777 @override
1778 bool visitVariableDeclaration(VariableDeclaration node) {
1779 usedNames.add(node.name);
1780 return false;
1781 }
1782
1783 @override
1784 bool visitVariableDeclarationList(VariableDeclarationList node) {
1785 bool result = false;
1786 for (VariableInitialization init in node.declarations) {
1787 if (visit(init)) result = true;
1788 }
1789 return result;
1790 }
1791
1792 @override
1793 bool visitVariableInitialization(VariableInitialization node) {
1794 return visitAssignment(node);
1795 }
1796
1797 @override
1798 bool visitVariableUse(VariableUse node) {
1799 usedNames.add(node.name);
1800 return false;
1801 }
1802
1803 @override
1804 bool visitWhile(While node) {
1805 loopsAndSwitches.add(node);
1806 bool condition = visit(node.condition);
1807 bool body = visit(node.body);
1808 loopsAndSwitches.removeLast();
1809 return condition || body;
1810 }
1811
1812 @override
1813 bool visitDartYield(DartYield node) {
1814 visit(node.expression);
1815 return true;
1816 }
1817 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698