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