OLD | NEW |
---|---|
(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 } | |
OLD | NEW |