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

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

Powered by Google App Engine
This is Rietveld 408576698