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 |