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

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: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/compiler/dart2js/async_await_js_transform_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 import "js.dart";
8 import "../helpers/helpers.dart";
9 import 'dart:collection';
10 import "dart:math" show max;
11 import 'package:compiler/src/util/util.dart';
12
13 class AsyncFinder extends BaseVisitor {
14 @override
15 visitFun(Fun node) {
16 switch (node.asyncModifier) {
17 case const AsyncModifier.sync():
18 node.visitChildren(this);
19 break;
20 case const AsyncModifier.async():
21
22 node.visitChildren(this);
23 break;
24 case const AsyncModifier.syncStar():
25 throw "Sync* is not supported yet";
26 case const AsyncModifier.asyncStar():
27 throw "Async* is not supported yet";
28 }
29 }
30
31 }
32
33 class AnalysisPrinter extends BaseVisitor {
34 int indent = 0;
35 var awaits;
36 visitNode(Node node) {
37 indent ++;
38 node.visitChildren(this);
39 indent --;
40 }
41 }
42
43 class Analysis extends NodeVisitor {
44 Set<Node> awaits = new Set<Node>();
45
46 Map<Node, Node> targets = new Map<Node, Node>();
47 List<Node> loopsAndSwitches = new List<Node>();
48 List<LabeledStatement> labelledStatements = new List<LabeledStatement>();
49
50 bool visit(Node node) {
51 bool result = node.accept(this);
floitsch 2015/01/20 16:06:15 don't call it "result".
sigurdm 2015/02/02 10:20:15 Called it containsAwait
52 if (result) {
53 awaits.add(node);
54 }
55 return result;
56 }
57
58 analyze(Fun node) {
59 visit(node.body);
60 }
61
62 @override
63 visitAccess(PropertyAccess node) {
64 bool x = visit(node.receiver);
65 bool y = visit(node.selector);
66 return x || y;
67 }
68
69 @override
70 visitArrayHole(ArrayHole node) {
71 // TODO: implement visitArrayHole
floitsch 2015/01/20 16:06:15 should be trivial. There is no expression in the h
sigurdm 2015/02/02 10:20:14 Done.
72 }
73
74 @override
75 visitArrayInitializer(ArrayInitializer node) {
76 // TODO: implement visitArrayInitializer
77 }
78
79 @override
80 visitAssignment(Assignment node) {
81 bool x = visit(node.leftHandSide);
floitsch 2015/01/20 16:06:15 x -> left y -> right
sigurdm 2015/02/02 10:20:15 Done.
82 bool y = node.value == null
83 ? false
84 : visit(node.value);
85 return x || y;
86 }
87
88 @override
89 visitAwait(Await node) {
90 visit(node.expression);
91 return true;
92 }
93
94 @override
95 visitBinary(Binary node) {
96 bool x = visit(node.left);
97 bool y = visit(node.right);
98 return x || y;
99 }
100
101 @override
102 visitBlob(Blob node) {
103 return false;
104 }
105
106 @override
107 visitBlock(Block node) {
108 bool result = false;
109 for (var s in node.statements) {
110 if (visit(s)) result = true;
111 }
112 return result;
113 }
114
115 @override
116 visitBreak(Break node) {
117 if (node.targetLabel != null) {
118 targets[node] = labelledStatements.lastWhere(
119 (LabeledStatement stm) => stm.label == node.targetLabel);
120 } else {
121 targets[node] = loopsAndSwitches.last;
122 }
123 return false;
124 }
125
126 @override
127 visitCall(Call node) {
128 bool result = visit(node.target);
129 for (var s in node.arguments) {
130 if (visit(s)) result = true;
131 }
132 return result;
133 }
134
135 @override
136 visitCase(Case node) {
137 bool x = visit(node.expression);
138 bool y = visit(node.body);
139 return x || y;
140 }
141
142 @override
143 visitCatch(Catch node) {
144 bool x = visit(node.declaration);
145 bool y = visit(node.body);
146 return x || y;
147 }
148
149 @override
150 visitComment(Comment node) {
151 return false;
152 }
153
154 @override
155 visitConditional(Conditional node) {
156 bool x = visit(node.condition);
157 bool y = visit(node.then);
158 bool z = visit(node.otherwise);
159 return x || y || z;
160 }
161
162 @override
163 visitContinue(Continue node) {
164 if (node.targetLabel != null) {
165 targets[node] = labelledStatements.lastWhere(
166 (LabeledStatement stm) => stm.label == node.targetLabel);
167 } else {
168 targets[node] = loopsAndSwitches.lastWhere(
169 (Node node) => node is! Switch);
170 }
171 return false;
172 }
173
174 @override
175 visitDefault(Default node) {
176 return visit(node.body);
177 }
178
179 @override
180 visitDo(Do node) {
181 loopsAndSwitches.add(node);
182 bool x = visit(node.body);
183 bool y = visit(node.condition);
184 loopsAndSwitches.removeLast();
185 return x || y;
186 }
187
188 @override
189 visitEmptyStatement(EmptyStatement node) {
190 return false;
191 }
192
193 @override
194 visitExpressionStatement(ExpressionStatement node) {
195 return visit(node.expression);
196 }
197
198 @override
199 visitFor(For node) {
200 loopsAndSwitches.add(node);
floitsch 2015/01/20 16:06:15 I would add the loopsAndSwitches only for the body
sigurdm 2015/02/02 10:20:15 Good point Done
201 bool x = visit(node.init);
202 bool y = visit(node.condition);
203 bool z = visit(node.update);
204 bool u = visit(node.body);
205 loopsAndSwitches.removeLast();
206 return x || y || z || u;
207 }
208
209 @override
210 visitForIn(ForIn node) {
211 loopsAndSwitches.add(node);
212 loopsAndSwitches.removeLast();
213 // TODO: implement visitForIn
214 throw "BADDD";
215 }
216
217 @override
218 visitFun(Fun node) {
219 return false;
220 }
221
222 @override
223 visitFunctionDeclaration(FunctionDeclaration node) {
224 return false;
225 }
226
227 @override
228 visitIf(If node) {
229 bool x = visit(node.condition);
230 bool y = visit(node.then);
231 bool z = visit(node.otherwise);
232 return x || y || z;
233 }
234
235 @override
236 visitInterpolatedExpression(InterpolatedExpression node) {
237 // TODO: implement visitInterpolatedExpression
238 }
239
240 @override
241 visitInterpolatedLiteral(InterpolatedLiteral node) {
242 // TODO: implement visitInterpolatedLiteral
243 }
244
245 @override
246 visitInterpolatedParameter(InterpolatedParameter node) {
247 // TODO: implement visitInterpolatedParameter
248 }
249
250 @override
251 visitInterpolatedSelector(InterpolatedSelector node) {
252 // TODO: implement visitInterpolatedSelector
253 }
254
255 @override
256 visitInterpolatedStatement(InterpolatedStatement node) {
257 // TODO: implement visitInterpolatedStatement
258 }
259
260 @override
261 visitLabeledStatement(LabeledStatement node) {
262 labelledStatements.add(node);
263 bool result = visit(node.body);
264 labelledStatements.removeLast();
265 return result;
266 }
267
268 @override
269 visitLiteralBool(LiteralBool node) {
270 return false;
271 }
272
273 @override
274 visitLiteralExpression(LiteralExpression node) {
275 // TODO: implement visitLiteralExpression
276 }
277
278 @override
279 visitLiteralNull(LiteralNull node) {
280 return false;
281 }
282
283 @override
284 visitLiteralNumber(LiteralNumber node) {
285 return false;
286 }
287
288 @override
289 visitLiteralStatement(LiteralStatement node) {
290 // TODO: implement visitLiteralStatement
291 }
292
293 @override
294 visitLiteralString(LiteralString node) {
295 return false;
296 }
297
298 @override
299 visitNamedFunction(NamedFunction node) {
300 return false;
301 }
302
303 @override
304 visitNew(New node) {
305 return visitCall(node);
306 }
307
308 @override
309 visitObjectInitializer(ObjectInitializer node) {
310 bool result = false;
311 for (Property property in node.properties) {
312 if (visit(property)) result = true;
313 }
314 return result;
315 }
316
317 @override
318 visitParameter(Parameter node) {
319 }
320
321 @override
322 visitPostfix(Postfix node) {
323 return visit(node.argument);
324 }
325
326 @override
327 visitPrefix(Prefix node) {
328 return visit(node.argument);
329 }
330
331 @override
332 visitProgram(Program node) {
333 throw "Unexpected";
334 }
335
336 @override
337 visitProperty(Property node) {
338 return visit(node.value);
339 }
340
341 @override
342 visitRegExpLiteral(RegExpLiteral node) {
343 return false;
344 }
345
346 @override
347 visitReturn(Return node) {
348 if (node.value == null) return false;
349 return visit(node.value);
350 }
351
352 @override
353 visitSwitch(Switch node) {
354 loopsAndSwitches.add(node);
355 bool result = visit(node.key);
356 for (SwitchClause clause in node.cases) {
357 if (visit(clause)) result = true;
358 }
359 loopsAndSwitches.removeLast();
360 return result;
361 }
362
363 @override
364 visitThis(This node) {
365 return false;
366 }
367
368 @override
369 visitThrow(Throw node) {
370 return visit(node.expression);
371 }
372
373 @override
374 visitTry(Try node) {
375 bool x = visit(node.body);
376 bool y = node.catchPart == null ? false : visit(node.catchPart);
377 bool z = node.finallyPart == null ? false : visit(node.finallyPart);
378 return x || y || z;
379 }
380
381 @override
382 visitVariableDeclaration(VariableDeclaration node) {
383 return false;
384 }
385
386 @override
387 visitVariableDeclarationList(VariableDeclarationList node) {
388 bool result = false;
389 for (VariableInitialization init in node.declarations) {
390 if (visit(init)) result = true;
391 }
392 return result;
393 }
394
395 @override
396 visitVariableInitialization(VariableInitialization node) {
397 return visitAssignment(node);
398 }
399
400 @override
401 visitVariableUse(VariableUse node) {
402 return false;
403 }
404
405 @override
406 visitWhile(While node) {
407 loopsAndSwitches.add(node);
408 // Be careful to avoid short-circuit.
409 bool x = visit(node.condition);
410 bool y = visit(node.body);
411 loopsAndSwitches.removeLast();
412 return x || y;
413 }
414 }
415
416 class AsyncRewriter extends NodeVisitor {
417
418 // The set of all nodes containing an await nested somewhere.
419 Set<Node> __containsAwait;
420
421 // Local variables are hoisted to the top of the function, so we collect them
422 // here.
423 List<VariableDeclaration> localVariables = new List<VariableDeclaration>();
424
425 Map<Node, Node> __targets = new Map<Node, Node>();
426 Map<Node, int> continueLabels = new Map<Node, int>();
427 Map<Node, int> breakLabels = new Map<Node, int>();
428
429 List<Pair<String, String>> variableRenamings =
430 new List<Pair<String, String>>();
431 Set<String> usedNames = new Set<String>();
432
433 List<int> errorHandlerLabels = new List<int>();
434
435 // TODO(sigurdm): use namer for these.
436 String resultName = "__result";
437 String helperName = "__helper";
438 String gotoName = "__goto";
439 String handlerName = "__handler";
440 String errorName = "__error";
441
442 int __currentLabel = 0;
443
444 int highWaterMark = 0;
445 int currentTempVarIndex = 0;
446
447 int allocateTempVar() {
448 assert(highWaterMark >= currentTempVarIndex);
449 currentTempVarIndex++;
450 highWaterMark = max(currentTempVarIndex, highWaterMark);
451 return currentTempVarIndex;
452 }
453
454 deallocateTempVar([howMany = 1]) {
455 currentTempVarIndex -= howMany;
456 }
457
458 Expression useTempVar(int i) {
459 return new VariableUse("__temp$i");
460 }
461
462
463 String freshName(String originalName) {
464 String result = "__$originalName";
465 int counter = 1;
466 while (usedNames.contains(result)) {
467 result = "__$originalName$counter";
468 ++counter;
469 }
470 usedNames.add(result);
471 return result;
472 }
473
474 /// We collect all the pieces in this map, and output a switch with a case
475 /// for each label.
476 /// The order is important, therefore the type is explicitly LinkedHashMap.
floitsch 2015/01/20 16:06:15 New line before.
sigurdm 2015/02/02 10:20:15 Done.
477 LinkedHashMap<int, List<Statement>> labelledParts =
478 new LinkedHashMap<int, List<Statement>>();
479
480 // Description of each label for readability of the non-minified output.
481 Map<int, String> labelComments = new Map<int, String>();
482
483 // True if the function has any try blocks containing await.
484 bool tryBlocks = false;
485
486 List<Statement> currentStatementBuffer;
487
488 void addStatement(Statement node) {
489 currentStatementBuffer.add(node);
490 }
491
492 void addExpressionStatement(Expression node) {
493 addStatement(new ExpressionStatement(node));
494 }
495
496 Expression methodCall(Expression callee,
497 String methodName,
498 List<Expression> args) {
499 return new Call(new PropertyAccess.field(callee, methodName), args);
500 }
501
502 int newLabel([String comment]) {
503 int result = __currentLabel;
504 __currentLabel++;
505 if (comment != null) {
506 labelComments[result] = comment;
507 }
508 return result;
509 }
510
511 void beginLabel(int label) {
512 assert(!labelledParts.containsKey(label));
513 labelledParts[label] = currentStatementBuffer = new List<Statement>();
514 addStatement(new Comment(labelComments[label]));
515 }
516
517 /// Returns a statement assigning to the `__goto` variable. This should be
518 /// followed by a break for the goto to be executed. Use [gotoWithBreak] or
519 /// [addGoto] for this.
520 Statement goto(int label) {
521 return new ExpressionStatement(
522 new Assignment(new VariableUse(gotoName), new LiteralNumber("$label")));
523 }
524
525 /// Returns a block with the same effect as [addGoto].
526 Block gotoAndBreak(int label) {
527 List<Statement> statements = new List<Statement>();
528 if (labelComments.containsKey(label)) {
529 statements.add(new Comment("goto ${labelComments[label]}"));
530 }
531 statements.add(goto(label));
532 statements.add(new Break(null));
533 return new Block(statements);
534 }
535
536 /// Add a goto to [label] including the break.
537 /// Will also insert a comment describing the label if available.
538 void addGoto(int label) {
539 if (labelComments.containsKey(label)) {
540 addStatement(new Comment("goto ${labelComments[label]}"));
541 }
542 addStatement(goto(label));
543 addStatement(new Break(null));
544 }
545
546 bool transform(Node node) {
floitsch 2015/01/20 16:06:14 shouldTransform
sigurdm 2015/02/02 10:20:15 Done.
547 return node is Break || node is Continue || __containsAwait.contains(node);
548 }
549
550 /// Main entry point.
551 Fun rewrite(Fun node) {
552 assert(node.asyncModifier == const AsyncModifier.async());
553 Analysis analysis = new Analysis();
554 analysis.analyze(node);
555 __containsAwait = analysis.awaits;
556 __targets = analysis.targets;
557 new AnalysisPrinter()..awaits = analysis.awaits
558 ..visitNode(node);
559 return node.accept(this);
560 }
561
562 visitStatement(Statement node) {
563 node.accept(this);
564 }
565
566 void visitExpressionIgnoreResult(Expression node) {
567 addExpressionStatement(node.accept(this));
568 }
569
570 Expression visitExpression(Expression node) {
571 return node.accept(this);
572 }
573
574 Expression _storeIfNecessary(Expression result) {
575 if (result is Literal) return result;
floitsch 2015/01/20 16:06:15 I guess this will be wrong, once you have list-lit
sigurdm 2015/02/02 10:20:14 Actually list-literals are not Literal, but Object
576 Expression tempVar = useTempVar(allocateTempVar());
577 addExpressionStatement(new Assignment(tempVar, result));
578 return tempVar;
579 }
580
581 withExpression(Expression node, fn(Expression result), {bool store}) {
582 int oldTempVarIndex = currentTempVarIndex;
583 Expression visited = visitExpression(node);
584 if (store) {
585 visited = _storeIfNecessary(visited);
586 }
587 var result = fn(visited);
588 currentTempVarIndex = oldTempVarIndex;
589 return result;
590 }
591
592 withExpression2(Expression node1, Expression node2,
593 fn(Expression result1, Expression result2)) {
594 int oldTempVarIndex = currentTempVarIndex;
595 Expression r1 = visitExpression(node1);
596 if (transform(node2)) {
597 r1 = _storeIfNecessary(r1);
598 }
599 Expression r2 = visitExpression(node2);
600 var result = fn(r1, r2);
601 currentTempVarIndex = oldTempVarIndex;
602 return result;
603 }
604
605 withExpressions(List<Node> nodes, fn(List<Node> results)) {
606 int oldTempVarIndex = currentTempVarIndex;
607 // Find last occurence of a 'transform' expression in [nodes].
608 // All expressions before that must be stored in temp-vars.
609 int lastTransformIndex = 0;
610 for (int i = nodes.length - 1; i >= 0; --i) {
611 if (transform(nodes[i])) {
612 lastTransformIndex = i;
613 break;
614 }
615 }
616 List<Node> visited = nodes.take(lastTransformIndex).map((Node node) {
617 return _storeIfNecessary(visitExpression(node));
618 }).toList();
619 visited.addAll(nodes.skip(lastTransformIndex).map((Node node) {
620 return visitExpression(node);
621 }));
622 var result = fn(visited);
623 currentTempVarIndex = oldTempVarIndex;
624 return result;
625 }
626
627 // [[Fun(a1, ..., an) S ]] =>
628 // Fun(a1, ..., an) {
629 // vars(S); // Declaration of local variables of S.
630 // var __goto = 0;
631 // var __handler = null; // The current error handler.
632 // Fun helper(__result) {
633 // while(true) {
634 // try {
635 // switch(__goto) {
636 // case 0:
637 // [[S]];
638 // }
639 // } catch(__error) {
640 // if (handler === null) throw error;
641 // __goto = __handler;
642 // __result = __error;
643 // }
644 // }
645 // }
646 // }
647 visitAsyncFun(Fun node) {
648 beginLabel(newLabel("Function start"));
649 visitStatement(node.body);
650 List<SwitchClause> clauses = labelledParts.keys.map((label) {
651 return new Case(new LiteralNumber("$label"),
652 new Block(labelledParts[label]));
653 }).toList();
654 Statement helperBody = new Switch(new VariableUse(gotoName),
655 clauses);
656 if (tryBlocks) {
657 helperBody = new Try(new Block([helperBody]),
floitsch 2015/01/20 16:06:15 consider using the `js` templates. You will actual
sigurdm 2015/02/02 10:20:14 Yeah - it is better like that.
658 new Catch((new VariableDeclaration(errorName)),
659 new Block([new If.noElse(new Binary("===",
660 new VariableUse(handlerName), new LiteralNull()),
661 new Throw(new VariableUse(errorName))),
662 new ExpressionStatement(new Assignment(
663 new VariableUse(resultName),
664 new VariableUse(errorName))),
665 new ExpressionStatement(new Assignment(
666 new VariableUse(gotoName),
667 new VariableUse(handlerName))),
668 ])), null);
669 }
670 helperBody = new While(new LiteralBool(true), helperBody);
671 helperBody = new Block([helperBody]);
672 List<VariableInitialization> inits =
673 [new VariableInitialization(new VariableDeclaration(gotoName),
674 new LiteralNumber("0")),
675 new VariableInitialization(new VariableDeclaration(handlerName),
676 new LiteralNull())];
677 inits.addAll(localVariables.map(
678 (VariableDeclaration decl) => new VariableInitialization(decl, null)));
679 VariableDeclarationList varDecl = new VariableDeclarationList(inits);
680 inits.addAll(new Iterable.generate(highWaterMark, (int i) {
681 return new VariableInitialization(
682 new VariableDeclaration("__temp${i + 1}"),
683 null);
684 }));
685 Fun helper = new Fun([new Parameter(resultName)], helperBody);
686 return new Fun(node.params,
687 new Block([new ExpressionStatement(varDecl),
688 new FunctionDeclaration(new VariableDeclaration(helperName),
689 helper),
690 new Return(
691 new New(new PropertyAccess.field(
692 new VariableUse("Future"), "microtask"),
693 [new VariableUse(helperName)]))]));
694 }
695
696 @override
697 visitFun(Fun node) {
698 switch (node.asyncModifier) {
699 case const AsyncModifier.sync():
700 return node;
701 case const AsyncModifier.async():
702 return visitAsyncFun(node);
703 case const AsyncModifier.syncStar():
704 throw "Sync* is not supported yet";
705 case const AsyncModifier.asyncStar():
706 throw "Async* is not supported yet";
707 }
708
709 }
710
711 @override
712 visitAccess(PropertyAccess node) {
713 return withExpression2(node.receiver, node.selector,
714 (receiver, selector) => new PropertyAccess(receiver, selector));
715 }
716
717 unreachable(Node node) {
718 throw("The transformer should never meet: $node");
floitsch 2015/01/20 16:06:15 If the function is intendend to stay, make it have
sigurdm 2015/02/02 10:20:14 Done.
719 }
720
721 @override
722 visitArrayHole(ArrayHole node) {
723 return node;
724 }
725
726 @override
727 visitArrayInitializer(ArrayInitializer node) {
728 withExpressions(node.elements, (elements) {
729 return new ArrayInitializer(elements);
730 });
731 }
732
733 // C([[e1 = e2]] =>
734 // C([[e1]] = [[e2]])
735 @override
736 visitAssignment(Assignment node) {
737 Expression leftHandSide = node.leftHandSide;
738 if (leftHandSide is VariableUse) {
739 return withExpression(node.value, (Expression value) {
740 return new Assignment(leftHandSide, value);
741 }, store: false);
742 } else if (leftHandSide is PropertyAccess) {
743 return withExpressions(
744 [leftHandSide.receiver, leftHandSide.selector, node.value],
745 (evaluated) {
746 return new Assignment(
747 new PropertyAccess(evaluated[0], evaluated[1]),
748 evaluated[2]);
749 });
750 } else {
751 throw "Unexpected assignment left hand side $leftHandSide";
752 }
753 }
754
755 // C([[await e]]) =>
756 // __goto = `newLabel(afterCall)
757 // [[e]].then(__helper, onError: (error) {__goto = `currentHandler } );
758 // case afterCall:
759 // C(__result);
760 @override
761 Expression visitAwait(Await node) {
762 int afterWait = newLabel("Returning from await.");
763 withExpression(node.expression, (Expression expression) {
764 addStatement(goto(afterWait));
765 addStatement(new Return(new Call(
766 new PropertyAccess.field(expression, "then"),
767 [new VariableUse(helperName),
768 new Fun([new Parameter(errorName)],
769 new Block([new ExpressionStatement(
770 new Assignment(new VariableUse(gotoName),
771 currentErrorHandler)),
772 new ExpressionStatement(new Call(
773 new VariableUse(helperName),
774 [new VariableUse(errorName)]))]))])));
775 }, store: false);
776 beginLabel(afterWait);
777 return new VariableUse(resultName);
778 }
779
780 Expression get currentErrorHandler {
781 return errorHandlerLabels.isEmpty
782 ? new LiteralNull()
783 : new LiteralNumber("${errorHandlerLabels.last}");
784 }
785
786 // TODO short-circuiting operators!!
787 @override
788 Expression visitBinary(Binary node) {
789 return withExpression2(node.left, node.right,
790 (left, right) => new Binary(node.op, left, right));
791 }
792
793 @override
794 visitBlob(Blob node) {
795 return node;
796 }
797
798 @override
799 void visitBlock(Block node) {
800 for (Statement statement in node.statements) {
801 visitStatement(statement);
802 }
803 }
804
805 @override
806 void visitBreak(Break node) {
807 int target = breakLabels[__targets[node]];
808 if (target != null) {
809 addGoto(target);
810 } else {
811 addStatement(node);
812 }
813 }
814
815 @override
816 Expression visitCall(Call node) {
817 bool storeTarget = node.arguments.any(transform);
818 return withExpression(node.target, (target) {
819 return withExpressions(node.arguments, (List<Expression> arguments) {
820 return new Call(target, arguments);
821 });
822 }, store: storeTarget);
823 }
824
825 @override
826 visitCase(Case node) {
827 return unreachable(node);
828 }
829
830 @override
831 visitCatch(Catch node) {
832 return unreachable(node);
833 }
834
835 @override
836 visitComment(Comment node) {
837 addStatement(node);
838 }
839
840 @override
841 Expression visitConditional(Conditional node) {
842 if (!transform(node.then) && !transform(node.otherwise)) {
843 return withExpression(node.condition, (Expression condition) {
844 return new Conditional(condition, node.then, node.otherwise);
845 });
846 }
847 int thenLabel = newLabel("then");
848 int joinLabel = newLabel("join");
849 int elseLabel = newLabel("else");
850 withExpression(node.condition, (Expression condition) {
851 addExpressionStatement(new Assignment(new VariableUse(gotoName),
852 new Conditional(condition, new LiteralNumber("$thenLabel"),
853 new LiteralNumber("$elseLabel"))));
854 }, store: false);
855 addStatement(new Break(null));
856 beginLabel(thenLabel);
857 withExpression(node.then, (Expression value) {
858 addExpressionStatement(new Assignment(
859 new VariableUse(resultName),
860 value));
861 }, store: false);
862 addGoto(joinLabel);
863 beginLabel(elseLabel);
864 withExpression(node.otherwise, (Expression value) {
865 addExpressionStatement(new Assignment(
866 new VariableUse(resultName),
867 value));
868 }, store: false);
869 beginLabel(joinLabel);
870 return new VariableUse(resultName);
871 }
872
873 @override
874 visitContinue(Continue node) {
875 int target = continueLabels[__targets[node]];
876 if (target != null) {
877 addGoto(target);
878 } else {
879 addStatement(node);
880 }
881 }
882
883 @override
884 visitDefault(Default node) => unreachable(node);
885
886 @override
887 visitDo(Do node) {
888 throw "TODO";
889 }
890
891 @override
892 visitEmptyStatement(EmptyStatement node) => node;
893
894 @override
895 visitExpressionStatement(ExpressionStatement node) {
896 if (node.expression is VariableDeclarationList) {
897 // Treat VariableDeclarationList as a statement.
898 visitVariableDeclarationList(node.expression);
899 } else {
900 visitExpressionIgnoreResult(node.expression);
901 }
902 }
903
904 @override
905 visitFor(For node) {
906 throw "TODO";
907 }
908
909 @override
910 visitForIn(ForIn node) {
911 throw "TODO";
912 }
913
914 @override
915 visitFunctionDeclaration(FunctionDeclaration node) {
916 return node;
917 }
918
919 Block translateInBlock(Statement node) {
920 List<Statement> oldBuffer = currentStatementBuffer;
921 List<Statement> resultBuffer = currentStatementBuffer = new List();
922 visitStatement(node);
923 currentStatementBuffer = oldBuffer;
924 return new Block(resultBuffer);
925 }
926
927 @override
928 visitIf(If node) {
929 if (!transform(node.then) && !transform(node.otherwise)) {
930 withExpression(node.condition, (Expression condition) {
931 addStatement(new If(condition,
932 translateInBlock(node.then),
933 translateInBlock(node.otherwise)));
934 }, store: false);
935 return;
936 }
937 int thenLabel = newLabel("then");
938 int joinLabel = newLabel("join");
939 int elseLabel = node.otherwise is EmptyStatement
940 ? joinLabel
941 : newLabel("else");
942
943 withExpression(node.condition, (Expression condition) {
944 addExpressionStatement(new Assignment(new VariableUse(gotoName),
945 new Conditional(condition, new LiteralNumber("$thenLabel"),
946 new LiteralNumber("$elseLabel"))));
947 }, store: false);
948 addStatement(new Break(null));
949 beginLabel(thenLabel);
950 visitStatement(node.then);
951 if (node.otherwise is EmptyStatement) {
952 addGoto(joinLabel);
953 beginLabel(elseLabel);
954 visitStatement(node.otherwise);
955 }
956 beginLabel(joinLabel);
957 }
958
959 @override
960 visitInterpolatedExpression(InterpolatedExpression node) {
961 throw "Unsupported";
962 }
963
964 @override
965 visitInterpolatedLiteral(InterpolatedLiteral node) => throw "Unsupported";
966
967 @override
968 visitInterpolatedParameter(InterpolatedParameter node) => throw "Unsupported";
969
970 @override
971 visitInterpolatedSelector(InterpolatedSelector node) => throw "Unsupported";
972
973 @override
974 visitInterpolatedStatement(InterpolatedStatement node) => throw "Unsupported";
975
976 @override
977 visitLabeledStatement(LabeledStatement node) {
978 int breakLabel = newLabel("break ${node.label}");
979 int continueLabel = newLabel("continue ${node.label}");
980 breakLabels[node] = breakLabel;
981 continueLabels[node] = continueLabel;
982
983 beginLabel(continueLabel);
984 visitStatement(node.body);
985 beginLabel(breakLabel);
986 }
987
988 @override
989 visitLiteralBool(LiteralBool node) => node;
990
991 @override
992 visitLiteralExpression(LiteralExpression node) => throw "Unsupported";
993
994 @override
995 visitLiteralNull(LiteralNull node) => node;
996
997 @override
998 visitLiteralNumber(LiteralNumber node) => node;
999
1000 @override
1001 visitLiteralStatement(LiteralStatement node) => throw "Unsupported";
1002
1003 @override
1004 visitLiteralString(LiteralString node) => node;
1005
1006 @override
1007 visitNamedFunction(NamedFunction node) {
1008 throw "TODO";
1009 }
1010
1011 @override
1012 visitNew(New node) {
1013 bool storeTarget = node.arguments.any(transform);
1014 return withExpression(node.target, (target) {
1015 return withExpressions(node.arguments, (List<Expression> arguments) {
1016 return new New(target, arguments);
1017 });
1018 }, store: storeTarget);
1019 }
1020
1021 @override
1022 visitObjectInitializer(ObjectInitializer node) {
1023 return withExpressions(node.properties, (List<Node> properties) {
1024 return new ObjectInitializer(properties);
1025 });
1026 }
1027
1028 @override
1029 visitParameter(Parameter node) {
1030 throw "Unexpected";
1031 }
1032
1033 @override
1034 visitPostfix(Postfix node) {
1035 return withExpression(node.argument,
1036 (Expression argument) => new Postfix(node.op, argument), store: false);
1037 }
1038
1039 @override
1040 visitPrefix(Prefix node) {
1041 return withExpression(node.argument,
1042 (Expression argument) => new Prefix(node.op, argument), store: false);
1043 }
1044
1045 @override
1046 visitProgram(Program node) => throw "Unsupported";
1047
1048 @override
1049 visitProperty(Property node) {
1050 return withExpression(node.value,
1051 (Expression value) => new Property(node.name, value), store: false);
1052 }
1053
1054 @override
1055 visitRegExpLiteral(RegExpLiteral node) => node;
1056
1057 @override
1058 visitReturn(Return node) {
1059 if (node.value == null) {
1060 addStatement(node);
1061 } else {
1062 withExpression(node.value, (Expression value) {
1063 addStatement(new Return(value));
1064 }, store: false);
1065 }
1066 }
1067
1068 // [[switch(e) {
1069 // case i: Si;
1070 // ...
1071 // default: Sd;
1072 // }]] =>
1073 // case #before
1074 // switch([[e]]) {
1075 // case i: __goto = #i;
1076 // ...
1077 // default: __goto = #defaultLabel;
1078 // }
1079 // case #i:
1080 // [[Si, breakLabels|#after, continueLabels|#before]]
1081 // // Notice fallthough/
1082 // ...
1083 // case #defaultLabel:
1084 // [[Si, breakLabels|#after, continueLabels|#before]]
1085 // case #afterSwitch:
1086 @override
1087 visitSwitch(Switch node) {
1088 if (!node.cases.any(transform)) {
1089 // If only the key has an await, translation can be simplified.
1090 withExpression(node.key, (Expression key) {
1091 List<SwitchClause> cases = node.cases.map((SwitchClause clause) {
1092 if (clause is Case) {
1093 return new Case(clause.expression, translateInBlock(clause.body));
1094 } else if (clause is Default) {
1095 return new Default(translateInBlock(clause.body));
1096 }
1097 }).toList();
1098 addStatement(new Switch(key, cases));
1099 });
1100 return;
1101 }
1102 int before = newLabel("switch");
1103 int after = newLabel("after switch");
1104 breakLabels[node] = after;
1105
1106 beginLabel(before);
1107 List<int> labels = new List<int>(node.cases.length);
1108
1109 if (!node.cases.every(
1110 (SwitchClause x) => !(x is Case && transform(x.expression)))){
1111 int defaultIndex = null; // Null means no default was found.
1112 // If there is an await in one of the keys, we have to use a chain of ifs.
1113
1114 withExpression(node.key, (Expression key) {
1115 int i = 0;
1116 for (SwitchClause clause in node.cases) {
1117 if (clause is Default) {
1118 // The default case is handled last.
1119 defaultIndex = i;
1120 labels[i] = newLabel("default");
1121 continue;
1122 } else if (clause is Case) {
1123 labels[i] = newLabel("case");
1124 withExpression(clause.expression, (expression) {
1125 addStatement(new If.noElse(new Binary("===", key, expression),
1126 gotoAndBreak(labels[i])));
1127 }, store: false);
1128 }
1129 i++;
1130 }
1131 }, store: true);
1132
1133 if (defaultIndex == null) {
1134 addGoto(after);
1135 } else {
1136 addGoto(labels[defaultIndex]);
1137 }
1138
1139 } else {
1140 bool hasDefault = false;
1141 int i = 0;
1142 List<SwitchClause> clauses = new List<SwitchClause>();
1143 for (SwitchClause clause in node.cases) {
1144 if (clause is Case) {
1145 labels[i] = newLabel("case");
1146 clauses.add(new Case(clause.expression, gotoAndBreak(labels[i])));
1147 } else if (i is Default) {
1148 labels[i] = newLabel("default");
1149 clauses.add(new Default(gotoAndBreak(labels[i])));
1150 hasDefault = true;
1151 } else {
1152 throw "Unknown switchclause $i";
1153 }
1154 i++;
1155 }
1156 withExpression(node.key, (Expression key) {
1157 addStatement(new Switch(key, clauses));
1158 }, store: false);
1159 if (!hasDefault) {
1160 addGoto(after);
1161 }
1162 }
1163 for (int i = 0; i < labels.length; i++) {
1164 beginLabel(labels[i]);
1165 visitStatement(node.cases[i].body);
1166 }
1167 beginLabel(after);
1168 }
1169
1170 @override
1171 visitThis(This node) => node;
1172
1173 @override
1174 visitThrow(Throw node) {
1175 withExpression(node.expression, (Expression expression) {
1176 addStatement(new Throw(expression));
1177 }, store: false);
1178 }
1179
1180 setHandler() {
1181 addExpressionStatement(
1182 new Assignment(new VariableUse(handlerName), currentErrorHandler));
1183 }
1184
1185 @override
1186 void visitTry(Try node) {
1187 if (!transform(node)) {
1188 Block body = translateInBlock(node.body);
1189 Catch catchPart = node.catchPart == null
1190 ? null
1191 : new Catch(node.catchPart.declaration,
1192 translateInBlock(node.catchPart.body));
1193 Block finallyPart = node.finallyPart == null
1194 ? null
1195 : translateInBlock(node.finallyPart);
1196 addStatement(new Try(body,
1197 catchPart,
1198 finallyPart));
1199 return;
1200 }
1201 tryBlocks = true;
1202 int handlerLabel = newLabel("catch");
1203 int finallyLabel = newLabel("finally");
1204 errorHandlerLabels.add(handlerLabel);
1205 setHandler();
1206 visitStatement(node.body);
1207 errorHandlerLabels.removeLast();
1208 setHandler();
1209 addGoto(finallyLabel);
1210 beginLabel(handlerLabel);
1211 if (node.catchPart != null) {
1212 String errorRename = freshName(node.catchPart.declaration.name);
1213 localVariables.add(new VariableDeclaration(errorRename));
1214 variableRenamings.add(
1215 new Pair(node.catchPart.declaration.name, errorRename));
1216 addExpressionStatement(new Assignment(
1217 new VariableUse(errorRename),
1218 new VariableUse(resultName)));
1219 visitStatement(node.catchPart.body);
1220 variableRenamings.removeLast();
1221 }
1222 beginLabel(finallyLabel);
1223 if (node.finallyPart != null) {
1224 visitStatement(node.finallyPart);
1225 }
1226 }
1227
1228 @override
1229 visitVariableDeclaration(VariableDeclaration node) {
1230 throw "TODO";
1231 }
1232
1233 @override
1234 void visitVariableDeclarationList(VariableDeclarationList node) {
1235 // Declaration of local variables is hoisted outside the helper
1236 // But the initialization is done here.
1237 for (VariableInitialization initialization in node.declarations) {
1238 VariableDeclaration declaration = initialization.declaration;
1239 localVariables.add(declaration);
1240 if (initialization.value != null) {
1241 withExpression(initialization.value, (Expression value) {
1242 addStatement(new ExpressionStatement(
1243 new Assignment(declaration, value)));
1244 }, store: false);
1245 }
1246 }
1247 }
1248
1249 @override
1250 visitVariableInitialization(VariableInitialization node) {
1251 // These should be handled by visitVariableDeclarationList.
1252 throw "Unexpected variableInitialization";
1253 }
1254
1255 @override
1256 visitVariableUse(VariableUse node) {
1257 String renaming = variableRenamings.lastWhere(
1258 (Pair renaming) => renaming.a == node.name, orElse: () {
1259 return new Pair(null, null);
1260 }).b;
1261 if (renaming == null) return node;
1262 return new VariableUse(renaming);
1263 }
1264
1265 @override
1266 visitWhile(While node) {
1267 if (!transform(node.body)) {
1268 // If only the condition has an await, translation can be simplified.
1269 withExpression(node.condition, (Expression condition) {
1270 addStatement(new While(condition, translateInBlock(node.body)));
1271 }, store: false);
1272 return;
1273 }
1274 int continueLabel = newLabel("while condition");
1275 continueLabels[node] = continueLabel;
1276 beginLabel(continueLabel);
1277
1278 int bodyLabel = newLabel("while body");
1279 int after = newLabel("after while");
1280 breakLabels[node] = after;
1281 withExpression(node.condition, (Expression cond) {
1282 addExpressionStatement(new Assignment(new VariableUse(gotoName),
1283 new Conditional(cond, new LiteralNumber("$bodyLabel"),
1284 new LiteralNumber("$after"))));
1285 }, store: false);
1286 addStatement(new Break(null));
1287 beginLabel(bodyLabel);
1288 visitStatement(node.body);
1289 addGoto(continueLabel);
1290 beginLabel(after);
1291 }
1292 }
OLDNEW
« no previous file with comments | « no previous file | tests/compiler/dart2js/async_await_js_transform_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698