| 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 |