| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library kernel.transformations.async; | 5 library kernel.transformations.async; |
| 6 | 6 |
| 7 import '../kernel.dart'; | 7 import '../kernel.dart'; |
| 8 import 'continuation.dart'; | 8 import 'continuation.dart'; |
| 9 | 9 |
| 10 /// A transformer that introduces temporary variables for all subexpressions | 10 /// A transformer that introduces temporary variables for all subexpressions |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 /// If an expression should be named it is named before visiting its children | 49 /// If an expression should be named it is named before visiting its children |
| 50 /// so the naming assignment appears in the list before all statements | 50 /// so the naming assignment appears in the list before all statements |
| 51 /// implementating the translation of the children. | 51 /// implementating the translation of the children. |
| 52 /// | 52 /// |
| 53 /// Children that are conditionally evaluated, such as some parts of logical | 53 /// Children that are conditionally evaluated, such as some parts of logical |
| 54 /// and conditional expressions, must be delimited so that they do not emit | 54 /// and conditional expressions, must be delimited so that they do not emit |
| 55 /// unguarded statements into [statements]. This is implemented by setting | 55 /// unguarded statements into [statements]. This is implemented by setting |
| 56 /// [statements] to a fresh empty list before transforming those children. | 56 /// [statements] to a fresh empty list before transforming those children. |
| 57 List<Statement> statements = <Statement>[]; | 57 List<Statement> statements = <Statement>[]; |
| 58 | 58 |
| 59 | |
| 60 /// The number of currently live named intermediate values. | 59 /// The number of currently live named intermediate values. |
| 61 /// | 60 /// |
| 62 /// This index is used to allocate names to temporary values. Because | 61 /// This index is used to allocate names to temporary values. Because |
| 63 /// children are visited right-to-left, names are assigned in reverse order of | 62 /// children are visited right-to-left, names are assigned in reverse order of |
| 64 /// index. | 63 /// index. |
| 65 /// | 64 /// |
| 66 /// When an assignment is emitted into [statements] to name an expression | 65 /// When an assignment is emitted into [statements] to name an expression |
| 67 /// before visiting its children, the index is not immediately reserved | 66 /// before visiting its children, the index is not immediately reserved |
| 68 /// because a child can freely use the same name as its parent. In practice, | 67 /// because a child can freely use the same name as its parent. In practice, |
| 69 /// this will be the rightmost named child. | 68 /// this will be the rightmost named child. |
| 70 /// | 69 /// |
| 71 /// After visiting the children of a named expression, [nameIndex] is set to | 70 /// After visiting the children of a named expression, [nameIndex] is set to |
| 72 /// indicate one more live value (the value of the expression) than before | 71 /// indicate one more live value (the value of the expression) than before |
| 73 /// visiting the expression. | 72 /// visiting the expression. |
| 74 /// | 73 /// |
| 75 /// After visiting the children of an expression that is not named, | 74 /// After visiting the children of an expression that is not named, |
| 76 /// [nameIndex] may still account for names of subexpressions. | 75 /// [nameIndex] may still account for names of subexpressions. |
| 77 int nameIndex = 0; | 76 int nameIndex = 0; |
| 78 | 77 |
| 79 final VariableDeclaration asyncResult = | 78 final VariableDeclaration asyncResult = new VariableDeclaration(':result'); |
| 80 new VariableDeclaration(':result'); | |
| 81 final List<VariableDeclaration> variables = <VariableDeclaration>[]; | 79 final List<VariableDeclaration> variables = <VariableDeclaration>[]; |
| 82 | 80 |
| 83 ExpressionLifter(this.continuationRewriter); | 81 ExpressionLifter(this.continuationRewriter); |
| 84 | 82 |
| 85 Block blockOf(List<Statement> stmts) => new Block(stmts.reversed.toList()); | 83 Block blockOf(List<Statement> stmts) => new Block(stmts.reversed.toList()); |
| 86 | 84 |
| 87 /// Rewrite a toplevel expression (toplevel wrt. a statement). | 85 /// Rewrite a toplevel expression (toplevel wrt. a statement). |
| 88 /// | 86 /// |
| 89 /// Rewriting an expression produces a sequence of statements and an | 87 /// Rewriting an expression produces a sequence of statements and an |
| 90 /// expression. The sequence of statements are added to the given list. Pass | 88 /// expression. The sequence of statements are added to the given list. Pass |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 var shouldName = seenAwait; | 167 var shouldName = seenAwait; |
| 170 | 168 |
| 171 // 1. If there is an await in a sibling to the right, emit an assignment to | 169 // 1. If there is an await in a sibling to the right, emit an assignment to |
| 172 // a temporary variable before transforming the children. | 170 // a temporary variable before transforming the children. |
| 173 var result = shouldName ? name(expr) : expr; | 171 var result = shouldName ? name(expr) : expr; |
| 174 | 172 |
| 175 // 2. Remember the number of live temporaries before transforming the | 173 // 2. Remember the number of live temporaries before transforming the |
| 176 // children. | 174 // children. |
| 177 var index = nameIndex; | 175 var index = nameIndex; |
| 178 | 176 |
| 179 | |
| 180 // 3. Transform the children. Initially they do not have an await in a | 177 // 3. Transform the children. Initially they do not have an await in a |
| 181 // sibling to their right. | 178 // sibling to their right. |
| 182 seenAwait = false; | 179 seenAwait = false; |
| 183 action(); | 180 action(); |
| 184 | 181 |
| 185 | |
| 186 // 4. If the expression was named then the variables used for children are | 182 // 4. If the expression was named then the variables used for children are |
| 187 // no longer live but the variable used for the expression is. | 183 // no longer live but the variable used for the expression is. |
| 188 if (shouldName) { | 184 if (shouldName) { |
| 189 nameIndex = index + 1; | 185 nameIndex = index + 1; |
| 190 seenAwait = true; | 186 seenAwait = true; |
| 191 } | 187 } |
| 192 return result; | 188 return result; |
| 193 } | 189 } |
| 194 | 190 |
| 195 // Unary expressions. | 191 // Unary expressions. |
| 196 Expression unary(Expression expr) { | 192 Expression unary(Expression expr) { |
| 197 return transform(expr, () { expr.transformChildren(this); }); | 193 return transform(expr, () { |
| 194 expr.transformChildren(this); |
| 195 }); |
| 198 } | 196 } |
| 199 | 197 |
| 200 TreeNode visitVariableSet(VariableSet expr) => unary(expr); | 198 TreeNode visitVariableSet(VariableSet expr) => unary(expr); |
| 201 TreeNode visitPropertyGet(PropertyGet expr) => unary(expr); | 199 TreeNode visitPropertyGet(PropertyGet expr) => unary(expr); |
| 202 TreeNode visitDirectPropertyGet(DirectPropertyGet expr) => unary(expr); | 200 TreeNode visitDirectPropertyGet(DirectPropertyGet expr) => unary(expr); |
| 203 TreeNode visitSuperPropertySet(SuperPropertySet expr) => unary(expr); | 201 TreeNode visitSuperPropertySet(SuperPropertySet expr) => unary(expr); |
| 204 TreeNode visitStaticSet(StaticSet expr) => unary(expr); | 202 TreeNode visitStaticSet(StaticSet expr) => unary(expr); |
| 205 TreeNode visitNot(Not expr) => unary(expr); | 203 TreeNode visitNot(Not expr) => unary(expr); |
| 206 TreeNode visitIsExpression(IsExpression expr) => unary(expr); | 204 TreeNode visitIsExpression(IsExpression expr) => unary(expr); |
| 207 TreeNode visitAsExpression(AsExpression expr) => unary(expr); | 205 TreeNode visitAsExpression(AsExpression expr) => unary(expr); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 } | 240 } |
| 243 | 241 |
| 244 TreeNode visitDirectMethodInvocation(DirectMethodInvocation expr) { | 242 TreeNode visitDirectMethodInvocation(DirectMethodInvocation expr) { |
| 245 return transform(expr, () { | 243 return transform(expr, () { |
| 246 visitArguments(expr.arguments); | 244 visitArguments(expr.arguments); |
| 247 expr.receiver = expr.receiver.accept(this)..parent = expr; | 245 expr.receiver = expr.receiver.accept(this)..parent = expr; |
| 248 }); | 246 }); |
| 249 } | 247 } |
| 250 | 248 |
| 251 TreeNode visitSuperMethodInvocation(SuperMethodInvocation expr) { | 249 TreeNode visitSuperMethodInvocation(SuperMethodInvocation expr) { |
| 252 return transform(expr, () { visitArguments(expr.arguments); }); | 250 return transform(expr, () { |
| 251 visitArguments(expr.arguments); |
| 252 }); |
| 253 } | 253 } |
| 254 | 254 |
| 255 TreeNode visitStaticInvocation(StaticInvocation expr) { | 255 TreeNode visitStaticInvocation(StaticInvocation expr) { |
| 256 return transform(expr, () { visitArguments(expr.arguments); }); | 256 return transform(expr, () { |
| 257 visitArguments(expr.arguments); |
| 258 }); |
| 257 } | 259 } |
| 258 | 260 |
| 259 TreeNode visitConstructorInvocation(ConstructorInvocation expr) { | 261 TreeNode visitConstructorInvocation(ConstructorInvocation expr) { |
| 260 return transform(expr, () { visitArguments(expr.arguments); }); | 262 return transform(expr, () { |
| 263 visitArguments(expr.arguments); |
| 264 }); |
| 261 } | 265 } |
| 262 | 266 |
| 263 TreeNode visitStringConcatenation(StringConcatenation expr) { | 267 TreeNode visitStringConcatenation(StringConcatenation expr) { |
| 264 return transform(expr, () { | 268 return transform(expr, () { |
| 265 var expressions = expr.expressions; | 269 var expressions = expr.expressions; |
| 266 for (var i = expressions.length - 1; i >= 0; --i) { | 270 for (var i = expressions.length - 1; i >= 0; --i) { |
| 267 expressions[i] = expressions[i].accept(this)..parent = expr; | 271 expressions[i] = expressions[i].accept(this)..parent = expr; |
| 268 } | 272 } |
| 269 }); | 273 }); |
| 270 } | 274 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 288 } | 292 } |
| 289 | 293 |
| 290 // Control flow. | 294 // Control flow. |
| 291 TreeNode visitLogicalExpression(LogicalExpression expr) { | 295 TreeNode visitLogicalExpression(LogicalExpression expr) { |
| 292 var shouldName = seenAwait; | 296 var shouldName = seenAwait; |
| 293 | 297 |
| 294 // Right is delimited because it is conditionally evaluated. | 298 // Right is delimited because it is conditionally evaluated. |
| 295 var rightStatements = <Statement>[]; | 299 var rightStatements = <Statement>[]; |
| 296 seenAwait = false; | 300 seenAwait = false; |
| 297 expr.right = delimit(() => expr.right.accept(this), rightStatements) | 301 expr.right = delimit(() => expr.right.accept(this), rightStatements) |
| 298 ..parent = expr; | 302 ..parent = expr; |
| 299 var rightAwait = seenAwait; | 303 var rightAwait = seenAwait; |
| 300 | 304 |
| 301 if (rightStatements.isEmpty) { | 305 if (rightStatements.isEmpty) { |
| 302 // Easy case: right did not emit any statements. | 306 // Easy case: right did not emit any statements. |
| 303 seenAwait = shouldName; | 307 seenAwait = shouldName; |
| 304 return transform(expr, () { | 308 return transform(expr, () { |
| 305 expr.left = expr.left.accept(this)..parent = expr; | 309 expr.left = expr.left.accept(this)..parent = expr; |
| 306 seenAwait = seenAwait || rightAwait; | 310 seenAwait = seenAwait || rightAwait; |
| 307 }); | 311 }); |
| 308 } | 312 } |
| 309 | 313 |
| 310 // If right has emitted statements we will produce a temporary t and emit | 314 // If right has emitted statements we will produce a temporary t and emit |
| 311 // for && (there is an analogous case for ||): | 315 // for && (there is an analogous case for ||): |
| 312 // | 316 // |
| 313 // t = [left] == true; | 317 // t = [left] == true; |
| 314 // if (t) { | 318 // if (t) { |
| 315 // t = [right] == true; | 319 // t = [right] == true; |
| 316 // } | 320 // } |
| 317 | 321 |
| 318 // Recall that statements are emitted in reverse order, so first emit the if | 322 // Recall that statements are emitted in reverse order, so first emit the if |
| 319 // statement, then the assignment of [left] == true, and then translate left | 323 // statement, then the assignment of [left] == true, and then translate left |
| 320 // so any statements it emits occur after in the accumulated list (that is, | 324 // so any statements it emits occur after in the accumulated list (that is, |
| 321 // so they occur before in the corresponding block). | 325 // so they occur before in the corresponding block). |
| 322 var rightBody = blockOf(rightStatements); | 326 var rightBody = blockOf(rightStatements); |
| 323 var result = allocateTemporary(nameIndex); | 327 var result = allocateTemporary(nameIndex); |
| 324 rightBody.addStatement(new ExpressionStatement( | 328 rightBody.addStatement(new ExpressionStatement(new VariableSet( |
| 325 new VariableSet( | 329 result, |
| 326 result, | 330 new MethodInvocation(expr.right, new Name('=='), |
| 327 new MethodInvocation( | 331 new Arguments(<Expression>[new BoolLiteral(true)]))))); |
| 328 expr.right, | |
| 329 new Name('=='), | |
| 330 new Arguments(<Expression>[new BoolLiteral(true)]))))); | |
| 331 var then, otherwise; | 332 var then, otherwise; |
| 332 if (expr.operator == '&&') { | 333 if (expr.operator == '&&') { |
| 333 then = rightBody; | 334 then = rightBody; |
| 334 otherwise = null; | 335 otherwise = null; |
| 335 } else { | 336 } else { |
| 336 then = new EmptyStatement(); | 337 then = new EmptyStatement(); |
| 337 otherwise = rightBody; | 338 otherwise = rightBody; |
| 338 } | 339 } |
| 339 statements.add(new IfStatement(new VariableGet(result), then, otherwise)); | 340 statements.add(new IfStatement(new VariableGet(result), then, otherwise)); |
| 340 | 341 |
| 341 var test = | 342 var test = new MethodInvocation(expr.left, new Name('=='), |
| 342 new MethodInvocation( | 343 new Arguments(<Expression>[new BoolLiteral(true)])); |
| 343 expr.left, | 344 statements.add(new ExpressionStatement(new VariableSet(result, test))); |
| 344 new Name('=='), | |
| 345 new Arguments(<Expression>[new BoolLiteral(true)])); | |
| 346 statements.add( | |
| 347 new ExpressionStatement(new VariableSet(result, test))); | |
| 348 | 345 |
| 349 seenAwait = false; | 346 seenAwait = false; |
| 350 test.receiver = test.receiver.accept(this)..parent = test; | 347 test.receiver = test.receiver.accept(this)..parent = test; |
| 351 | 348 |
| 352 ++nameIndex; | 349 ++nameIndex; |
| 353 seenAwait = seenAwait || rightAwait; | 350 seenAwait = seenAwait || rightAwait; |
| 354 return new VariableGet(result); | 351 return new VariableGet(result); |
| 355 } | 352 } |
| 356 | 353 |
| 357 TreeNode visitConditionalExpression(ConditionalExpression expr) { | 354 TreeNode visitConditionalExpression(ConditionalExpression expr) { |
| 358 // Then and otherwise are delimited because they are conditionally | 355 // Then and otherwise are delimited because they are conditionally |
| 359 // evaluated. | 356 // evaluated. |
| 360 var shouldName = seenAwait; | 357 var shouldName = seenAwait; |
| 361 | 358 |
| 362 var thenStatements = <Statement>[]; | 359 var thenStatements = <Statement>[]; |
| 363 seenAwait = false; | 360 seenAwait = false; |
| 364 expr.then = delimit(() => expr.then.accept(this), thenStatements) | 361 expr.then = delimit(() => expr.then.accept(this), thenStatements) |
| 365 ..parent = expr; | 362 ..parent = expr; |
| 366 var thenAwait = seenAwait; | 363 var thenAwait = seenAwait; |
| 367 | 364 |
| 368 var otherwiseStatements = <Statement>[]; | 365 var otherwiseStatements = <Statement>[]; |
| 369 seenAwait = false; | 366 seenAwait = false; |
| 370 expr.otherwise = delimit(() => expr.otherwise.accept(this), | 367 expr.otherwise = |
| 371 otherwiseStatements)..parent = expr; | 368 delimit(() => expr.otherwise.accept(this), otherwiseStatements) |
| 369 ..parent = expr; |
| 372 var otherwiseAwait = seenAwait; | 370 var otherwiseAwait = seenAwait; |
| 373 | 371 |
| 374 if (thenStatements.isEmpty && otherwiseStatements.isEmpty) { | 372 if (thenStatements.isEmpty && otherwiseStatements.isEmpty) { |
| 375 // Easy case: neither then nor otherwise emitted any statements. | 373 // Easy case: neither then nor otherwise emitted any statements. |
| 376 seenAwait = shouldName; | 374 seenAwait = shouldName; |
| 377 return transform(expr, () { | 375 return transform(expr, () { |
| 378 expr.condition = expr.condition.accept(this)..parent = expr; | 376 expr.condition = expr.condition.accept(this)..parent = expr; |
| 379 seenAwait = seenAwait || thenAwait || otherwiseAwait; | 377 seenAwait = seenAwait || thenAwait || otherwiseAwait; |
| 380 }); | 378 }); |
| 381 } | 379 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 409 // Others. | 407 // Others. |
| 410 TreeNode visitAwaitExpression(AwaitExpression expr) { | 408 TreeNode visitAwaitExpression(AwaitExpression expr) { |
| 411 final R = continuationRewriter; | 409 final R = continuationRewriter; |
| 412 var shouldName = seenAwait; | 410 var shouldName = seenAwait; |
| 413 var result = new VariableGet(asyncResult); | 411 var result = new VariableGet(asyncResult); |
| 414 // The statements are in reverse order, so name the result first if | 412 // The statements are in reverse order, so name the result first if |
| 415 // necessary and then add the two other statements in reverse. | 413 // necessary and then add the two other statements in reverse. |
| 416 if (shouldName) result = name(result); | 414 if (shouldName) result = name(result); |
| 417 statements.add(R.createContinuationPoint()); | 415 statements.add(R.createContinuationPoint()); |
| 418 Arguments arguments = new Arguments(<Expression>[ | 416 Arguments arguments = new Arguments(<Expression>[ |
| 419 expr.operand, | 417 expr.operand, |
| 420 new VariableGet(R.thenContinuationVariable), | 418 new VariableGet(R.thenContinuationVariable), |
| 421 new VariableGet(R.catchErrorContinuationVariable)]); | 419 new VariableGet(R.catchErrorContinuationVariable) |
| 420 ]); |
| 422 statements.add(new ExpressionStatement( | 421 statements.add(new ExpressionStatement( |
| 423 new StaticInvocation(R.helper.awaitHelper, arguments))); | 422 new StaticInvocation(R.helper.awaitHelper, arguments))); |
| 424 | 423 |
| 425 seenAwait = false; | 424 seenAwait = false; |
| 426 var index = nameIndex; | 425 var index = nameIndex; |
| 427 arguments.positional[0] = expr.operand.accept(this)..parent = arguments; | 426 arguments.positional[0] = expr.operand.accept(this)..parent = arguments; |
| 428 | 427 |
| 429 if (shouldName) nameIndex = index + 1; | 428 if (shouldName) nameIndex = index + 1; |
| 430 seenAwait = true; | 429 seenAwait = true; |
| 431 return result; | 430 return result; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 454 // and return the body's value. | 453 // and return the body's value. |
| 455 // | 454 // |
| 456 // So x is in scope for all the body's statements and the body's value. | 455 // So x is in scope for all the body's statements and the body's value. |
| 457 // This has the unpleasant consequence that all let-bound variables with | 456 // This has the unpleasant consequence that all let-bound variables with |
| 458 // await in the let's body will end up hoisted out the the expression and | 457 // await in the let's body will end up hoisted out the the expression and |
| 459 // allocated to the context in the VM, even if they have no uses | 458 // allocated to the context in the VM, even if they have no uses |
| 460 // (`let _ = e0 in e1` can be used for sequencing of `e0` and `e1`). | 459 // (`let _ = e0 in e1` can be used for sequencing of `e0` and `e1`). |
| 461 statements.add(variable); | 460 statements.add(variable); |
| 462 var index = nameIndex; | 461 var index = nameIndex; |
| 463 seenAwait = false; | 462 seenAwait = false; |
| 464 variable.initializer = | 463 variable.initializer = variable.initializer.accept(this) |
| 465 variable.initializer.accept(this)..parent = variable; | 464 ..parent = variable; |
| 466 // Temporaries used in the initializer or the body are not live but the | 465 // Temporaries used in the initializer or the body are not live but the |
| 467 // temporary used for the body is. | 466 // temporary used for the body is. |
| 468 nameIndex = index + 1; | 467 nameIndex = index + 1; |
| 469 seenAwait = true; | 468 seenAwait = true; |
| 470 return body; | 469 return body; |
| 471 } else { | 470 } else { |
| 472 // The body in `let x = initializer in body` did not contain an await. We | 471 // The body in `let x = initializer in body` did not contain an await. We |
| 473 // can leave a let expression. | 472 // can leave a let expression. |
| 474 seenAwait = shouldName; | 473 seenAwait = shouldName; |
| 475 return transform(expr, () { | 474 return transform(expr, () { |
| 476 // The body has already been translated. | 475 // The body has already been translated. |
| 477 expr.body = body..parent = expr; | 476 expr.body = body..parent = expr; |
| 478 variable.initializer = | 477 variable.initializer = variable.initializer.accept(this) |
| 479 variable.initializer.accept(this)..parent = variable; | 478 ..parent = variable; |
| 480 }); | 479 }); |
| 481 } | 480 } |
| 482 } | 481 } |
| 483 | 482 |
| 484 visitFunctionNode(FunctionNode node) { | 483 visitFunctionNode(FunctionNode node) { |
| 485 var nestedRewriter = new RecursiveContinuationRewriter( | 484 var nestedRewriter = |
| 486 continuationRewriter.helper); | 485 new RecursiveContinuationRewriter(continuationRewriter.helper); |
| 487 return node.accept(nestedRewriter); | 486 return node.accept(nestedRewriter); |
| 488 } | 487 } |
| 489 } | 488 } |
| OLD | NEW |