OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 /** | |
6 * Normalizes the AST to make the translation in [AwaitProcessor] simpler. This | |
7 * normalization provides the following guarantees: | |
8 * - await only occurs in top-level assignments. For example: | |
9 * if (await t) return; | |
10 * after normalization should become: | |
11 * final $t = await t; | |
12 * if ($t) return; | |
13 * | |
14 * - await in declarations are split in multiple declarations: | |
15 * int x = 1, y = await t, z = 3, w = y; | |
16 * becomes: | |
17 * int x = 1; | |
18 * int y = await t; | |
19 * int z = 3, w = y; | |
20 * | |
21 * - await cannot occur on complex assignments: | |
22 * x += await t | |
23 * becomes: | |
24 * $t = await t | |
25 * x += $t | |
26 * | |
27 * - await cannot occur outside statement blocks: | |
28 * if (...) x = await t | |
29 * becomes: | |
30 * if (...) { x = await t } | |
31 */ | |
32 class AwaitNormalizer implements TreeVisitor { | |
33 // TODO(sigmund): fix frog to make it possible to switch to '_a:tmp'. The | |
34 // mangling code currently breaks across closure-boundaries. | |
35 static final _PREFIX = '_a_tmp'; | |
36 int _tmpVarCounter = 0; | |
37 | |
38 /** AST nodes that contain await expressions. */ | |
39 NodeSet haveAwait; | |
40 | |
41 AwaitNormalizer(this.haveAwait); | |
42 | |
43 NormalizerResult visitVariableDefinition(VariableDefinition node) { | |
44 // split variable declarations in chuncks: | |
45 List<Statement> res = []; | |
46 List<Identifier> names = []; | |
47 List<Expression> values = []; | |
48 for (int i = 0; i < node.names.length; i++) { | |
49 final val = node.values[i]; | |
50 if (val == null || !haveAwait.contains(val)) { | |
51 names.add(node.names[i]); | |
52 values.add(val); | |
53 } else { | |
54 // an await was found, declare previous vars first: | |
55 if (names.length > 0) { | |
56 res.add(new VariableDefinition( | |
57 node.modifiers, node.type, names, values, node.span)); | |
58 names = []; | |
59 values = []; | |
60 } | |
61 final valRes = _visit(val); | |
62 res.addAll(valRes.stmts); | |
63 // add the declaration directly, since all following vars should | |
64 // be in a separate declaration | |
65 final newDecl = new VariableDefinition(node.modifiers, node.type, | |
66 [node.names[i]], [valRes.exp], node.span); | |
67 res.add(newDecl); | |
68 } | |
69 } | |
70 if (names.length > 0) { | |
71 res.add(new VariableDefinition( | |
72 node.modifiers, node.type, names, values, node.span)); | |
73 } | |
74 return new NormalizerResult(res, null, null); | |
75 } | |
76 | |
77 NormalizerResult visitFunctionDefinition(FunctionDefinition node) { | |
78 if (!haveAwait.contains(node)) return null; | |
79 node.body = _visit(node.body).asStatement(); // normalize and replace body. | |
80 } | |
81 | |
82 NormalizerResult visitReturnStatement(ReturnStatement node) { | |
83 final expRes = _visit(node.value); | |
84 final res = new NormalizerResult([], null, null); | |
85 res.stmts.addAll(expRes.stmts); | |
86 res.stmts.add(new ReturnStatement(expRes.exp, node.span)); | |
87 return res; | |
88 } | |
89 | |
90 NormalizerResult visitThrowStatement(ThrowStatement node) { | |
91 final expRes = _visit(node.value); | |
92 final res = new NormalizerResult([], null, null); | |
93 res.stmts.addAll(expRes.stmts); | |
94 res.stmts.add(new ThrowStatement(expRes.exp, node.span)); | |
95 return res; | |
96 } | |
97 | |
98 NormalizerResult visitIfStatement(IfStatement node) { | |
99 final testRes = _visit(node.test); | |
100 final trueRes = _visit(node.trueBranch).asStatement(); | |
101 final falseRes = _visit(node.falseBranch).asStatement(); | |
102 final res = new NormalizerResult([], null, null); | |
103 res.stmts.addAll(testRes.stmts); | |
104 final exp = _liftExp(testRes, res.stmts); | |
105 res.stmts.add(new IfStatement(exp, trueRes, falseRes, node.span)); | |
106 return res; | |
107 } | |
108 | |
109 NormalizerResult visitWhileStatement(WhileStatement node) { | |
110 final testRes = _visit(node.test); | |
111 final bodyRes = _visit(node.body).asStatement(); | |
112 final res = new NormalizerResult([], null, null); | |
113 res.stmts.addAll(testRes.stmts); | |
114 final exp = _liftExp(testRes, res.stmts); | |
115 res.stmts.add(new WhileStatement(exp, bodyRes, node.span)); | |
116 return res; | |
117 } | |
118 | |
119 NormalizerResult visitDoStatement(DoStatement node) { | |
120 final bodyRes = _visit(node.body); | |
121 final testRes = _visit(node.test); | |
122 final bodyList = [bodyRes.asStatement()]; | |
123 bodyList.addAll(testRes.stmts); | |
124 final exp = _liftExp(testRes, bodyList); | |
125 final body = new BlockStatement(bodyList, node.span); | |
126 return new NormalizerResult.fromNode( | |
127 new DoStatement(body, exp, node.span)); | |
128 } | |
129 | |
130 NormalizerResult visitForStatement(ForStatement node) { | |
131 // TODO(sigmund): implement | |
132 _notSupported("for loops", node); | |
133 return null; | |
134 } | |
135 | |
136 NormalizerResult visitForInStatement(ForInStatement node) { | |
137 _notSupported("for-in loops", node); | |
138 return null; | |
139 } | |
140 | |
141 NormalizerResult visitTryStatement(TryStatement node) { | |
142 final bodyRes = _visit(node.body); | |
143 List<CatchNode> catchesRes = []; | |
144 for (NormalizerResult r in _visitList(node.catches)) { | |
145 assert (r.node != null && r.node is CatchNode); | |
146 catchesRes.add(r.node); | |
147 } | |
148 final finallyRes = _visit(node.finallyBlock); | |
149 | |
150 return new NormalizerResult.fromNode( | |
151 new TryStatement(bodyRes.asStatement(), | |
152 catchesRes, finallyRes.asStatement(), node.span)); | |
153 } | |
154 | |
155 NormalizerResult visitSwitchStatement(SwitchStatement node) { | |
156 _notSupported("switch statements", node); | |
157 return null; | |
158 } | |
159 | |
160 NormalizerResult visitBlockStatement(BlockStatement node) { | |
161 final bodyRes = _visitList(node.body); | |
162 final newBlock = []; | |
163 for (NormalizerResult b in bodyRes) { | |
164 newBlock.addAll(b.stmts); | |
165 if (b.exp != null) { | |
166 world.fatal( | |
167 "unexpected: expression result from normalizing block", node.span); | |
168 | |
169 } | |
170 } | |
171 return new NormalizerResult.fromNode( | |
172 new BlockStatement(newBlock, node.span)); | |
173 } | |
174 | |
175 NormalizerResult visitLabeledStatement(LabeledStatement node) { | |
176 return new NormalizerResult.fromNode(new LabeledStatement( | |
177 node.name, _visit(node.body).asStatement(), node.span)); | |
178 } | |
179 | |
180 visitAssertStatement(AssertStatement node) { | |
181 // We can normalize | |
182 // assert await M; | |
183 // as: | |
184 // var x = false; | |
185 // assert (x = true); // ensure await is only evaluated when assertions | |
186 // // are enabled | |
187 // if (x) { | |
188 // var t = await M; | |
189 // assert t; | |
190 // } | |
191 _notSupported("assert statements", node); | |
192 return null; | |
193 } | |
194 | |
195 NormalizerResult visitExpressionStatement(ExpressionStatement node) { | |
196 final bodyRes = _visit(node.body); | |
197 final res = new NormalizerResult([], null, null); | |
198 res.stmts.addAll(bodyRes.stmts); | |
199 res.stmts.add(new ExpressionStatement(bodyRes.exp, node.span)); | |
200 return res; | |
201 } | |
202 | |
203 NormalizerResult visitCallExpression(CallExpression node) { | |
204 final targetRes = _visit(node.target); | |
205 final argsRes = _visitList(node.arguments); | |
206 final extraStmts = []; | |
207 final newArgs = []; | |
208 extraStmts.addAll(targetRes.stmts); | |
209 final target = _liftExp(targetRes, extraStmts); | |
210 for (final arg in argsRes) { | |
211 extraStmts.addAll(arg.stmts); | |
212 newArgs.add(arg.node); | |
213 } | |
214 return new NormalizerResult(extraStmts, | |
215 new CallExpression(target, newArgs, node.span), null); | |
216 } | |
217 | |
218 NormalizerResult visitIndexExpression(IndexExpression node) { | |
219 final targetRes = _visit(node.target); | |
220 final indexRes = _visit(node.index); | |
221 final extraStmts = []; | |
222 extraStmts.addAll(targetRes.stmts); | |
223 final target = _liftExp(targetRes, extraStmts); | |
224 extraStmts.addAll(indexRes.stmts); | |
225 final index = _liftExp(indexRes, extraStmts); | |
226 return new NormalizerResult(extraStmts, | |
227 new IndexExpression(target, index, node.span), null); | |
228 } | |
229 | |
230 NormalizerResult visitBinaryExpression(BinaryExpression node) { | |
231 final xRes = _visit(node.x); | |
232 final yRes = _visit(node.y); | |
233 final extraStmts = []; | |
234 extraStmts.addAll(xRes.stmts); | |
235 final x = _liftExp(xRes, extraStmts); | |
236 if (node.op.kind == TokenKind.OR || node.op.kind == TokenKind.AND) { | |
237 // AND and OR require short-circuiting code: | |
238 final otherStmts = []; | |
239 otherStmts.addAll(yRes.stmts); | |
240 final y = _liftExp(yRes, otherStmts); | |
241 final tmpId = _newTmp(node.span); | |
242 extraStmts.add(_declareVar(tmpId, x, false)); | |
243 final tmpVar = new VarExpression(tmpId, node.span); | |
244 Expression test = tmpVar; | |
245 if (node.op.kind == TokenKind.OR) { | |
246 // (a && b) becomes t = a; if (t) t = b; (t) | |
247 // (a || b) becomes t = a; if (!t) t = b; (t) | |
248 test = new UnaryExpression( | |
249 new Token.fake(TokenKind.NOT, node.op.span), test, node.op.span); | |
250 } | |
251 otherStmts.add(new ExpressionStatement( | |
252 new BinaryExpression(new Token.fake(TokenKind.ASSIGN, node.span), | |
253 tmpVar, y, y.span), y.span)); | |
254 extraStmts.add(new IfStatement(test, | |
255 new BlockStatement(otherStmts, node.y.span), null, node.y.span)); | |
256 return new NormalizerResult(extraStmts, tmpVar, null); | |
257 } else { | |
258 extraStmts.addAll(yRes.stmts); | |
259 // Other operators require a var-lifting the rhs if they contain an await: | |
260 final y = _liftExp(yRes, extraStmts); | |
261 return new NormalizerResult(extraStmts, | |
262 new BinaryExpression(node.op, x, y, node.span), null); | |
263 } | |
264 } | |
265 | |
266 NormalizerResult visitUnaryExpression(UnaryExpression node) { | |
267 // TODO(sigmund): implement | |
268 _notSupported("unary expressions", node); | |
269 return null; | |
270 } | |
271 | |
272 NormalizerResult visitPostfixExpression(PostfixExpression node) { | |
273 // TODO(sigmund): implement | |
274 _notSupported("postfix expressions", node); | |
275 return null; | |
276 } | |
277 | |
278 NormalizerResult visitNewExpression(NewExpression node) { | |
279 // TODO(sigmund): implement | |
280 _notSupported("new expressions", node); | |
281 return null; | |
282 } | |
283 | |
284 NormalizerResult visitListExpression(ListExpression node) { | |
285 // TODO(sigmund): implement | |
286 _notSupported("list literals", node); | |
287 return null; | |
288 } | |
289 | |
290 NormalizerResult visitMapExpression(MapExpression node) { | |
291 // TODO(sigmund): implement | |
292 _notSupported("map literals", node); | |
293 return null; | |
294 } | |
295 | |
296 NormalizerResult visitConditionalExpression(ConditionalExpression node) { | |
297 // TODO(sigmund): implement | |
298 _notSupported("ternary expressions", node); | |
299 return null; | |
300 } | |
301 | |
302 NormalizerResult visitIsExpression(IsExpression node) { | |
303 // TODO(sigmund): implement | |
304 _notSupported("is expressions", node); | |
305 return null; | |
306 } | |
307 | |
308 NormalizerResult visitParenExpression(ParenExpression node) { | |
309 final res = _visit(node.body); | |
310 final extraStmts = []; | |
311 extraStmts.addAll(res.stmts); | |
312 final exp = _liftExp(res, extraStmts); | |
313 return new NormalizerResult(extraStmts, | |
314 new ParenExpression(exp, node.span), res.node); | |
315 } | |
316 | |
317 NormalizerResult visitAwaitExpression(AwaitExpression node) { | |
318 final stmts = []; | |
319 final bodyRes = _visit(node.body); | |
320 stmts.addAll(bodyRes.stmts); | |
321 final val = _asVariable(bodyRes, stmts); | |
322 return new NormalizerResult(stmts, | |
323 val != node.body ? new AwaitExpression(val, node.span) : node, null); | |
324 } | |
325 | |
326 NormalizerResult visitDotExpression(DotExpression node) { | |
327 final selfRes = _visit(node.self); | |
328 final extraStmts = []; | |
329 extraStmts.addAll(selfRes.stmts); | |
330 final self = _liftExp(selfRes, extraStmts); | |
331 return new NormalizerResult(extraStmts, | |
332 new DotExpression(self, node.name, node.span), null); | |
333 } | |
334 | |
335 NormalizerResult visitArgumentNode(ArgumentNode node) { | |
336 final valueRes = _visit(node.value); | |
337 final extraStmts = []; | |
338 extraStmts.addAll(valueRes.stmts); | |
339 final value = _liftExp(valueRes, extraStmts); | |
340 return new NormalizerResult(extraStmts, null, | |
341 new ArgumentNode(node.label, value, node.span)); | |
342 } | |
343 | |
344 CatchNode visitCatchNode(CatchNode node) { | |
345 // TODO(sigmund): implement | |
346 _notSupported("catch node", node); | |
347 return node; | |
348 } | |
349 | |
350 NormalizerResult visitCaseNode(CaseNode node) { | |
351 // TODO(sigmund): implement | |
352 _notSupported("case node", node); | |
353 return null; | |
354 } | |
355 | |
356 List _visitList(List nodes) { | |
357 if (nodes == null) return null; | |
358 List res = []; | |
359 for (final n in nodes) { | |
360 res.add(_visit(n)); | |
361 } | |
362 return res; | |
363 } | |
364 | |
365 _visit(node) { | |
366 if (node == null || !haveAwait.contains(node)) { | |
367 return new NormalizerResult.fromNode(node); | |
368 } | |
369 return node.visit(this); | |
370 } | |
371 | |
372 Identifier _newTmp(SourceSpan span) { | |
373 return new Identifier(_PREFIX + _tmpVarCounter++, span); | |
374 } | |
375 | |
376 VariableDefinition _declareVar(name, value, bool isFinal) { | |
377 return new VariableDefinition( | |
378 isFinal ? [new Token.fake(TokenKind.FINAL, value.span)] : null, | |
379 null, [name], [value], value.span); | |
380 } | |
381 | |
382 Expression _liftExp(NormalizerResult r, List<Statement> extraStmts) { | |
383 if (r.exp is! AwaitExpression) return r.exp; | |
384 Identifier name = _newTmp(r.exp.span); | |
385 extraStmts.add(_declareVar(name, r.exp, true)); | |
386 return new VarExpression(name, r.exp.span); | |
387 } | |
388 | |
389 Expression _asVariable(NormalizerResult r, List<Statement> extraStmts) { | |
390 if (r.exp is VarExpression) return r.exp; | |
391 Identifier name = _newTmp(r.exp.span); | |
392 extraStmts.add(_declareVar(name, r.exp, true)); | |
393 return new VarExpression(name, r.exp.span); | |
394 } | |
395 | |
396 _notSupported(what, node) { | |
397 world.error("Normalization of $what is not supported yet.", node.span); | |
398 } | |
399 } | |
400 | |
401 /** A temporary result of the normalization process. */ | |
402 class NormalizerResult { | |
403 /** A list of statements, in order. */ | |
404 List<Statement> stmts; | |
405 | |
406 /** Normalized expression (null for normalized statements). */ | |
407 Expression exp; | |
408 | |
409 /** Resulting node for AST nodes that are not stmts or expressions. */ | |
410 Node node; | |
411 | |
412 NormalizerResult(this.stmts, this.exp, this.node); | |
413 | |
414 factory NormalizerResult.fromNode(node) { | |
415 if (node == null) { | |
416 return new NormalizerResult(null, null, null); | |
417 } else if (node is Expression) { | |
418 return new NormalizerResult(const [], node, null); | |
419 } else if (node is Statement) { | |
420 return new NormalizerResult([node], null, null); | |
421 } else { | |
422 return new NormalizerResult(const [], null, node); | |
423 } | |
424 } | |
425 | |
426 Statement asStatement() { | |
427 if (exp != null) { | |
428 world.fatal("asStatement only supported on statement results.", exp.span); | |
429 } | |
430 if (stmts == null) { | |
431 return null; | |
432 } | |
433 if (stmts.length == 1) { | |
434 // Return the underlying statement without wrapping it in a block, unless | |
435 // the statement contains an await. | |
436 Statement stmt = stmts.last(); | |
437 | |
438 if (stmt is! ExpressionStatement) return stmt; | |
439 ExpressionStatement expStmt = stmt; | |
440 Expression body = expStmt.body; | |
441 | |
442 if (body is! AwaitExpression || body is! BinaryExpression) return stmt; | |
443 | |
444 if (body is BinaryExpression) { | |
445 BinaryExpression binExp = body; | |
446 if (binExp.y is! AwaitExpression) return stmt; | |
447 } | |
448 } | |
449 return new BlockStatement(stmts, stmts.last().span); | |
450 } | |
451 } | |
OLD | NEW |