Chromium Code Reviews| 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.continuation; | 5 library kernel.transformations.continuation; |
| 6 | 6 |
| 7 import 'dart:math' as math; | 7 import 'dart:math' as math; |
| 8 | 8 |
| 9 import '../ast.dart'; | 9 import '../ast.dart'; |
| 10 import '../visitor.dart'; | 10 import '../visitor.dart'; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 26 | 26 |
| 27 RecursiveContinuationRewriter(this.helper); | 27 RecursiveContinuationRewriter(this.helper); |
| 28 | 28 |
| 29 Program rewriteProgram(Program node) { | 29 Program rewriteProgram(Program node) { |
| 30 return node.accept(this); | 30 return node.accept(this); |
| 31 } | 31 } |
| 32 | 32 |
| 33 visitFunctionNode(FunctionNode node) { | 33 visitFunctionNode(FunctionNode node) { |
| 34 switch (node.asyncMarker) { | 34 switch (node.asyncMarker) { |
| 35 case AsyncMarker.Sync: | 35 case AsyncMarker.Sync: |
| 36 return super.visitFunctionNode(node); | 36 case AsyncMarker.SyncYielding: |
| 37 node.transformChildren(new RecursiveContinuationRewriter(helper)); | |
|
Kevin Millikin (Google)
2016/10/31 12:29:32
super.visitFunctionNode(node) is node.transformChi
| |
| 38 return node; | |
| 37 case AsyncMarker.SyncStar: | 39 case AsyncMarker.SyncStar: |
| 38 return new SyncStarFunctionRewriter(helper, node).rewrite(); | 40 return new SyncStarFunctionRewriter(helper, node).rewrite(); |
| 39 case AsyncMarker.Async: | 41 case AsyncMarker.Async: |
| 40 return new AsyncFunctionRewriter(helper, node).rewrite(); | 42 return new AsyncFunctionRewriter(helper, node).rewrite(); |
| 41 case AsyncMarker.AsyncStar: | 43 case AsyncMarker.AsyncStar: |
| 42 return new AsyncStarFunctionRewriter(helper, node).rewrite(); | 44 return new AsyncStarFunctionRewriter(helper, node).rewrite(); |
| 43 case AsyncMarker.SyncYielding: | |
| 44 return super.visitFunctionNode(node); | |
| 45 } | 45 } |
| 46 } | 46 } |
| 47 } | 47 } |
| 48 | 48 |
| 49 abstract class ContinuationRewriterBase extends RecursiveContinuationRewriter { | 49 abstract class ContinuationRewriterBase extends RecursiveContinuationRewriter { |
| 50 final FunctionNode enclosingFunction; | 50 final FunctionNode enclosingFunction; |
| 51 | 51 |
| 52 int currentTryDepth; // Nesting depth for try-blocks. | 52 int currentTryDepth; // Nesting depth for try-blocks. |
| 53 int currentCatchDepth = 0; // Nesting depth for catch-blocks. | 53 int currentCatchDepth = 0; // Nesting depth for catch-blocks. |
| 54 int capturedTryDepth = 0; // Deepest yield point within a try-block. | 54 int capturedTryDepth = 0; // Deepest yield point within a try-block. |
| 55 int capturedCatchDepth = 0; // Deepest yield point within a catch-block. | 55 int capturedCatchDepth = 0; // Deepest yield point within a catch-block. |
| 56 | 56 |
| 57 ContinuationRewriterBase(HelperNodes helper, | 57 ContinuationRewriterBase(HelperNodes helper, |
| 58 this.enclosingFunction, | 58 this.enclosingFunction, |
| 59 {this.currentTryDepth: 0}) | 59 {this.currentTryDepth: 0}) |
| 60 : super(helper); | 60 : super(helper); |
| 61 | 61 |
| 62 Statement createContinuationPoint([value]) { | 62 Statement createContinuationPoint([Expression value]) { |
| 63 if (value == null) value = new NullLiteral(); | 63 if (value == null) value = new NullLiteral(); |
| 64 capturedTryDepth = math.max(capturedTryDepth, currentTryDepth); | 64 capturedTryDepth = math.max(capturedTryDepth, currentTryDepth); |
| 65 capturedCatchDepth = math.max(capturedCatchDepth, currentCatchDepth); | 65 capturedCatchDepth = math.max(capturedCatchDepth, currentCatchDepth); |
| 66 return new YieldStatement(value, isNative: true); | 66 return new YieldStatement(value, isNative: true); |
| 67 } | 67 } |
| 68 | 68 |
| 69 TreeNode visitTryCatch(TryCatch node) { | 69 TreeNode visitTryCatch(TryCatch node) { |
| 70 if (node.body != null) { | 70 if (node.body != null) { |
| 71 currentTryDepth++; | 71 currentTryDepth++; |
| 72 node.body = node.body.accept(this); | 72 node.body = node.body.accept(this); |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 182 final VariableDeclaration catchErrorContinuationVariable = | 182 final VariableDeclaration catchErrorContinuationVariable = |
| 183 new VariableDeclaration(":async_op_error"); | 183 new VariableDeclaration(":async_op_error"); |
| 184 | 184 |
| 185 ExpressionLifter expressionRewriter; | 185 ExpressionLifter expressionRewriter; |
| 186 | 186 |
| 187 AsyncRewriterBase(helper, enclosingFunction) | 187 AsyncRewriterBase(helper, enclosingFunction) |
| 188 // Body is wrapped in the try-catch so initial currentTryDepth is 1. | 188 // Body is wrapped in the try-catch so initial currentTryDepth is 1. |
| 189 : super(helper, enclosingFunction, currentTryDepth: 1) { | 189 : super(helper, enclosingFunction, currentTryDepth: 1) { |
| 190 } | 190 } |
| 191 | 191 |
| 192 setupAsyncContinuations(List<Statement> statements) { | 192 void setupAsyncContinuations(List<Statement> statements) { |
| 193 expressionRewriter = new ExpressionLifter(this); | 193 expressionRewriter = new ExpressionLifter(this); |
| 194 | 194 |
| 195 // var :async_op_then; | 195 // var :async_op_then; |
| 196 statements.add(thenContinuationVariable); | 196 statements.add(thenContinuationVariable); |
| 197 | 197 |
| 198 // var :async_op_error; | 198 // var :async_op_error; |
| 199 statements.add(catchErrorContinuationVariable); | 199 statements.add(catchErrorContinuationVariable); |
| 200 | 200 |
| 201 // :async_op([:result, :exception, :stack_trace]) { | 201 // :async_op([:result, :exception, :stack_trace]) { |
| 202 // modified <node.body>; | 202 // modified <node.body>; |
| 203 // } | 203 // } |
| 204 final parameters = [ | 204 final parameters = <VariableDeclaration>[ |
| 205 expressionRewriter.asyncResult, | 205 expressionRewriter.asyncResult, |
| 206 new VariableDeclaration(':exception'), | 206 new VariableDeclaration(':exception'), |
| 207 new VariableDeclaration(':stack_trace'), | 207 new VariableDeclaration(':stack_trace'), |
| 208 ]; | 208 ]; |
| 209 final function = new FunctionNode( | 209 final function = new FunctionNode( |
| 210 buildWrappedBody(), | 210 buildWrappedBody(), |
| 211 positionalParameters: parameters, | 211 positionalParameters: parameters, |
| 212 requiredParameterCount: 0, | 212 requiredParameterCount: 0, |
| 213 asyncMarker: AsyncMarker.SyncYielding); | 213 asyncMarker: AsyncMarker.SyncYielding); |
| 214 | 214 |
| 215 // The await expression lifter might have created a number of | 215 // The await expression lifter might have created a number of |
| 216 // [VariableDeclarations]. | 216 // [VariableDeclarations]. |
| 217 // TODO(kustermann): If we didn't need any variables we should not emit | 217 // TODO(kustermann): If we didn't need any variables we should not emit |
| 218 // these. | 218 // these. |
| 219 statements.addAll(variableDeclarations()); | 219 statements.addAll(variableDeclarations()); |
| 220 statements.addAll(expressionRewriter.variables); | 220 statements.addAll(expressionRewriter.variables); |
| 221 | 221 |
| 222 // Now add the closure function itself. | 222 // Now add the closure function itself. |
| 223 final closureFunction = | 223 final closureFunction = |
| 224 new FunctionDeclaration(nestedClosureVariable, function); | 224 new FunctionDeclaration(nestedClosureVariable, function); |
| 225 statements.add(closureFunction); | 225 statements.add(closureFunction); |
| 226 | 226 |
| 227 // :async_op_then = _asyncThenWrapperHelper(asyncBody); | 227 // :async_op_then = _asyncThenWrapperHelper(asyncBody); |
| 228 final boundThenClosure = new StaticInvocation( | 228 final boundThenClosure = new StaticInvocation( |
| 229 helper.asyncThenWrapper, | 229 helper.asyncThenWrapper, |
| 230 new Arguments([new VariableGet(nestedClosureVariable)])); | 230 new Arguments(<Expression>[new VariableGet(nestedClosureVariable)])); |
| 231 final thenClosureVariableAssign = new ExpressionStatement(new VariableSet( | 231 final thenClosureVariableAssign = new ExpressionStatement(new VariableSet( |
| 232 thenContinuationVariable, boundThenClosure)); | 232 thenContinuationVariable, boundThenClosure)); |
| 233 statements.add(thenClosureVariableAssign); | 233 statements.add(thenClosureVariableAssign); |
| 234 | 234 |
| 235 // :async_op_error = _asyncErrorWrapperHelper(asyncBody); | 235 // :async_op_error = _asyncErrorWrapperHelper(asyncBody); |
| 236 final boundCatchErrorClosure = new StaticInvocation( | 236 final boundCatchErrorClosure = new StaticInvocation( |
| 237 helper.asyncErrorWrapper, | 237 helper.asyncErrorWrapper, |
| 238 new Arguments([new VariableGet(nestedClosureVariable)])); | 238 new Arguments(<Expression>[new VariableGet(nestedClosureVariable)])); |
| 239 final catchErrorClosureVariableAssign = | 239 final catchErrorClosureVariableAssign = |
| 240 new ExpressionStatement(new VariableSet( | 240 new ExpressionStatement(new VariableSet( |
| 241 catchErrorContinuationVariable , boundCatchErrorClosure)); | 241 catchErrorContinuationVariable , boundCatchErrorClosure)); |
| 242 statements.add(catchErrorClosureVariableAssign); | 242 statements.add(catchErrorClosureVariableAssign); |
| 243 } | 243 } |
| 244 | 244 |
| 245 Statement buildWrappedBody() { | 245 Statement buildWrappedBody() { |
| 246 // No explicit return at the end of the body => we will add one! | 246 // No explicit return at the end of the body => we will add one! |
| 247 var body = addReturnStatementIfNecessary(enclosingFunction.body); | 247 var body = addReturnStatementIfNecessary(enclosingFunction.body); |
| 248 var userBody = buildClosureBody(body); | 248 var userBody = visitDelimited(body); |
| 249 | 249 |
| 250 var exceptionVariable = new VariableDeclaration(":exception"); | 250 var exceptionVariable = new VariableDeclaration(":exception"); |
| 251 var stackTraceVariable = new VariableDeclaration(":stack_trace"); | 251 var stackTraceVariable = new VariableDeclaration(":stack_trace"); |
| 252 | 252 |
| 253 var completeErrorStatement = | 253 var completeErrorStatement = |
| 254 buildCatchBody(exceptionVariable, stackTraceVariable); | 254 buildCatchBody(exceptionVariable, stackTraceVariable); |
| 255 | 255 |
| 256 var catchBody = new Block(<Statement>[completeErrorStatement]); | 256 var catchBody = new Block(<Statement>[completeErrorStatement]); |
| 257 var catches = [new Catch(exceptionVariable, | 257 var catches = <Catch>[new Catch(exceptionVariable, |
| 258 catchBody, | 258 catchBody, |
| 259 stackTrace: stackTraceVariable)]; | 259 stackTrace: stackTraceVariable)]; |
| 260 return new TryCatch(userBody, catches); | 260 return new TryCatch(userBody, catches); |
| 261 } | 261 } |
| 262 | 262 |
| 263 addReturnStatementIfNecessary(Statement body) { | 263 Statement addReturnStatementIfNecessary(Statement body) { |
| 264 if (body is Block) { | 264 if (body is Block) { |
| 265 Block block = body; | 265 Block block = body; |
| 266 if (block.statements.isEmpty || | 266 if (block.statements.isEmpty || |
| 267 block.statements.last is! ReturnStatement) { | 267 block.statements.last is! ReturnStatement) { |
| 268 var returnStatement = new ReturnStatement(); | 268 var returnStatement = new ReturnStatement(); |
| 269 block.statements.add(returnStatement); | 269 block.statements.add(returnStatement); |
| 270 returnStatement.parent = block; | 270 returnStatement.parent = block; |
| 271 } | 271 } |
| 272 } else if (body is! ReturnStatement) { | 272 } else if (body is! ReturnStatement) { |
| 273 var returnStatement = new ReturnStatement(); | 273 var returnStatement = new ReturnStatement(); |
| 274 body = new Block([body, returnStatement]); | 274 body = new Block(<Statement>[body, returnStatement]); |
| 275 } | 275 } |
| 276 return body; | 276 return body; |
| 277 } | 277 } |
| 278 | 278 |
| 279 Statement buildClosureBody(Statement node); | |
| 280 | |
| 281 Statement buildCatchBody(Statement exceptionVariable, | 279 Statement buildCatchBody(Statement exceptionVariable, |
| 282 Statement stackTraceVariable); | 280 Statement stackTraceVariable); |
| 283 | 281 |
| 284 visitForInStatement(ForInStatement node) { | 282 List<Statement> statements = <Statement>[]; |
| 285 if (node.isAsync) { | 283 |
| 284 TreeNode visitInvalidStatement(InvalidStatemnt stmt) { | |
|
asgerf
2016/11/01 10:44:45
InvalidStatemnt -> InvalidStatement
Kevin Millikin (Google)
2016/11/01 13:11:31
Done.
| |
| 285 statements.add(stmt); | |
| 286 return null; | |
|
Kevin Millikin (Google)
2016/10/31 12:29:32
The statement translation functions always return
| |
| 287 } | |
| 288 | |
| 289 TreeNode visitExpressionStatement(ExpressionStatement stmt) { | |
| 290 stmt.expression = expressionRewriter.rewrite(stmt.expression, statements) | |
| 291 ..parent = stmt; | |
| 292 statements.add(stmt); | |
| 293 return null; | |
| 294 } | |
| 295 | |
| 296 TreeNode visitBlock(Block stmt) { | |
| 297 var saved = statements; | |
| 298 statements = <Statement>[]; | |
| 299 for (var statement in stmt.statements) { | |
| 300 statement.accept(this); | |
| 301 } | |
| 302 saved.add(new Block(statements)); | |
| 303 statements = saved; | |
| 304 return null; | |
| 305 } | |
| 306 | |
| 307 TreeNode visitEmptyStatement(EmptyStatement stmt) { | |
| 308 statements.add(stmt); | |
| 309 return null; | |
| 310 } | |
| 311 | |
| 312 TreeNode visitAssertStatement(AssertStatement stmt) { | |
| 313 // TODO! | |
| 314 return null; | |
| 315 } | |
| 316 | |
| 317 Statement visitDelimited(Statement stmt) { | |
| 318 var saved = statements; | |
| 319 statements = <Statement>[]; | |
| 320 stmt.accept(this); | |
| 321 Statement result = | |
| 322 statements.length == 1 ? statements.first : new Block(statements); | |
| 323 statements = saved; | |
| 324 return result; | |
| 325 } | |
| 326 | |
| 327 Statement visitLabeledStatement(LabeledStatement stmt) { | |
| 328 stmt.body = visitDelimited(stmt.body)..parent = stmt; | |
| 329 statements.add(stmt); | |
| 330 return null; | |
| 331 } | |
| 332 | |
| 333 Statement visitBreakStatement(BreakStatement stmt) { | |
| 334 statements.add(stmt); | |
| 335 return null; | |
| 336 } | |
| 337 | |
| 338 TreeNode visitWhileStatement(WhileStatement stmt) { | |
| 339 Statement body = visitDelimited(stmt.body); | |
| 340 List<Statement> effects = <Statement>[]; | |
| 341 Expression cond = expressionRewriter.rewrite(stmt.condition, effects); | |
| 342 if (effects.isEmpty) { | |
| 343 stmt.condition = cond..parent = stmt; | |
| 344 stmt.body = body..parent = stmt; | |
| 345 statements.add(stmt); | |
| 346 } else { | |
| 347 // The condition rewrote to a non-empty sequence of statements S* and | |
| 348 // value V. Rewrite the loop to: | |
| 349 // | |
| 350 // L: while (true) { | |
| 351 // S* | |
| 352 // if (V) { | |
| 353 // [body] | |
| 354 // else { | |
| 355 // break L; | |
| 356 // } | |
| 357 // } | |
| 358 LabeledStatement labeled = new LabeledStatement(stmt); | |
| 359 stmt.condition = new BoolLiteral(true)..parent = stmt; | |
| 360 effects.add(new IfStatement(cond, body, new BreakStatement(labeled))); | |
| 361 stmt.body = new Block(effects)..parent = stmt; | |
| 362 statements.add(labeled); | |
| 363 } | |
| 364 return null; | |
| 365 } | |
| 366 | |
| 367 TreeNode visitDoStatement(DoStatement stmt) { | |
| 368 Statement body = visitDelimited(stmt.body); | |
| 369 List<Statement> effects = <Statement>[]; | |
| 370 stmt.condition = expressionRewriter.rewrite(stmt.condition, effects) | |
| 371 ..parent = stmt; | |
| 372 if (effects.isNotEmpty) { | |
| 373 // The condition rewrote to a non-empty sequence of statements S* and | |
| 374 // value V. Add the statements to the end of the loop body. | |
| 375 Block block = body is Block ? body : body = new Block(<Statement>[body]); | |
| 376 for (var effect in effects) { | |
| 377 block.statements.add(effect); | |
| 378 effect.parent = body; | |
| 379 } | |
| 380 } | |
| 381 stmt.body = body..parent = stmt; | |
| 382 statements.add(stmt); | |
| 383 return null; | |
| 384 } | |
| 385 | |
| 386 TreeNode visitForStatement(ForStatement stmt) { | |
| 387 // Because of for-loop scoping and variable capture, it is tricky to deal | |
| 388 // with await in the loop's variable initializers or update expressions. | |
| 389 bool isSimple = true; | |
| 390 int length = stmt.variables.length; | |
| 391 List<List<Statement>> initEffects = new List<List<Statement>>(length); | |
| 392 for (int i = 0; i < length; ++i) { | |
| 393 VariableDeclaration decl = stmt.variables[i]; | |
| 394 initEffects[i] = <Statement>[]; | |
| 395 if (decl.initializer != null) { | |
| 396 decl.initializer = | |
| 397 expressionRewriter.rewrite(decl.initializer, initEffects[i]) | |
| 398 ..parent = decl; | |
| 399 } | |
| 400 isSimple = isSimple && initEffects[i].isEmpty; | |
| 401 } | |
| 402 | |
| 403 length = stmt.updates.length; | |
| 404 List<List<Statement>> updateEffects = new List<List<Statement>>(length); | |
| 405 for (int i = 0; i < stmt.updates.length; ++i) { | |
|
asgerf
2016/11/01 10:44:45
Did you mean to use "i < length" here? (since we h
Kevin Millikin (Google)
2016/11/01 13:11:31
Yes. Done.
| |
| 406 updateEffects[i] = <Statement>[]; | |
| 407 stmt.updates[i] = | |
| 408 expressionRewriter.rewrite(stmt.updates[i], updateEffects[i]) | |
| 409 ..parent = stmt; | |
| 410 isSimple = isSimple && updateEffects[i].isEmpty; | |
| 411 } | |
| 412 | |
| 413 Statement body = visitDelimited(stmt.body); | |
| 414 Expression cond = stmt.condition; | |
| 415 List<Statement> condEffects; | |
| 416 if (cond != null) { | |
| 417 condEffects = <Statement>[]; | |
| 418 cond = expressionRewriter.rewrite(stmt.condition, condEffects); | |
| 419 } | |
| 420 | |
| 421 if (isSimple) { | |
| 422 // If the condition contains await, we use a translation like the one for | |
| 423 // while loops, but leaving the variable declarations and the update | |
| 424 // expressions in place. | |
| 425 if (condEffects == null || condEffects.isEmpty) { | |
| 426 if (cond != null) stmt.condition = cond..parent = stmt; | |
| 427 stmt.body = body..parent = stmt; | |
| 428 statements.add(stmt); | |
| 429 } else { | |
| 430 LabeledStatement labeled = new LabeledStatement(stmt); | |
| 431 stmt.condition = null; // No condition in a for loop is the same as tru e. | |
|
asgerf
2016/11/01 10:44:45
Long line.
Kevin Millikin (Google)
2016/11/01 13:11:31
I just ran the whole file through the formatter.
| |
| 432 condEffects.add( | |
| 433 new IfStatement(cond, body, new BreakStatement(labeled))); | |
| 434 stmt.body = new Block(condEffects)..parent = stmt; | |
| 435 statements.add(labeled); | |
| 436 } | |
| 437 return null; | |
| 438 } | |
| 439 | |
| 440 // If the rewrite of the initializer or update expressions produces a | |
| 441 // non-empty sequence of statements then the loop is desugared. If the loop | |
| 442 // has the form: | |
| 443 // | |
| 444 // label: for (Type x = init; cond; update) body | |
| 445 // | |
| 446 // it is translated as if it were: | |
| 447 // | |
| 448 // { | |
| 449 // bool first = true; | |
| 450 // Type temp; | |
| 451 // label: while (true) { | |
| 452 // Type x; | |
| 453 // if (first) { | |
| 454 // first = false; | |
| 455 // x = init; | |
| 456 // } else { | |
| 457 // x = temp; | |
| 458 // update; | |
| 459 // } | |
| 460 // if (cond) { | |
| 461 // body; | |
| 462 // temp = x; | |
| 463 // } else { | |
| 464 // break; | |
| 465 // } | |
| 466 // } | |
| 467 // } | |
| 468 | |
| 469 // Place the loop variable declarations at the beginning of the body | |
| 470 // statements and move their initializers to a guarded list of statements. | |
| 471 // Add assignments to the loop variables from the previous iteration's temp | |
| 472 // variables before the updates. | |
| 473 // | |
| 474 // temps.first is the flag 'first'. | |
| 475 // TODO(kmillikin) bool type for first. | |
| 476 List<VariableDeclaration> temps = <VariableDeclaration>[ | |
| 477 new VariableDeclaration.forValue(new BoolLiteral(true), | |
| 478 isFinal: false)]; | |
| 479 List<Statement> loopBody = <Statement>[]; | |
| 480 List<Statement> initializers = <Statement>[ | |
| 481 new ExpressionStatement( | |
| 482 new VariableSet(temps.first, new BoolLiteral(false)))]; | |
| 483 List<Statment> updates = <Statement>[]; | |
|
asgerf
2016/11/01 10:44:45
Statment -> Statement
Kevin Millikin (Google)
2016/11/01 13:11:31
Done.
| |
| 484 List<Statement> newBody = <Statement>[body]; | |
| 485 for (int i = 0; i < stmt.variables.length; ++i) { | |
| 486 VariableDeclaration decl = stmt.variables[i]; | |
| 487 temps.add(new VariableDeclaration(null, type: decl.type)); | |
| 488 loopBody.add(decl); | |
| 489 if (decl.initializer != null) { | |
| 490 initializers.addAll(initEffects[i]); | |
| 491 initializers.add( | |
| 492 new ExpressionStatement(new VariableSet(decl, decl.initializer))); | |
| 493 decl.initializer = null; | |
| 494 } | |
| 495 updates.add(new ExpressionStatement( | |
| 496 new VariableSet(decl, new VariableGet(temps.last)))); | |
| 497 newBody.add(new ExpressionStatement( | |
| 498 new VariableSet(temps.last, new VariableGet(decl)))); | |
| 499 } | |
| 500 // Add the updates to their guarded list of statements. | |
| 501 for (int i = 0; i < stmt.updates.length; ++i) { | |
| 502 updates.addAll(updateEffects[i]); | |
| 503 updates.add(new ExpressionStatement(stmt.updates[i])); | |
| 504 } | |
| 505 // Initializers or updates could be empty. | |
| 506 loopBody.add(new IfStatement(new VariableGet(temps.first), | |
| 507 new Block(initializers), | |
| 508 new Block(updates))); | |
| 509 | |
| 510 LabeledStatement labeled = new LabeledStatement(null); | |
| 511 if (cond != null) { | |
| 512 loopBody.addAll(condEffects); | |
| 513 } else { | |
| 514 cond = new BoolLiteral(true); | |
| 515 } | |
| 516 loopBody.add( | |
| 517 new IfStatement(cond, new Block(newBody), new BreakStatement(labeled))); | |
| 518 labeled.body = | |
| 519 new WhileStatement(new BoolLiteral(true), new Block(loopBody)); | |
| 520 statements.add(new Block(<Statement>[]..addAll(temps)..add(labeled))); | |
| 521 return null; | |
| 522 } | |
| 523 | |
| 524 TreeNode visitForInStatement(ForInStatement stmt) { | |
| 525 if (stmt.isAsync) { | |
| 286 // Transform | 526 // Transform |
| 287 // | 527 // |
| 288 // await for (var variable in <stream-expression>) { ... } | 528 // await for (var variable in <stream-expression>) { ... } |
| 289 // | 529 // |
| 290 // To: | 530 // To: |
| 291 // | 531 // |
| 292 // { | 532 // { |
| 293 // var :for-iterator = new StreamIterator(<stream-expression>); | 533 // var :for-iterator = new StreamIterator(<stream-expression>); |
| 294 // try { | 534 // try { |
| 295 // while (await :for-iterator.moveNext()) { | 535 // while (await :for-iterator.moveNext()) { |
| 296 // var <variable> = :for-iterator.current; | 536 // var <variable> = :for-iterator.current; |
| 297 // ... | 537 // ... |
| 298 // } | 538 // } |
| 299 // } finally { | 539 // } finally { |
| 300 // :for-iterator.cancel(); | 540 // :for-iterator.cancel(); |
| 301 // } | 541 // } |
| 302 // } | 542 // } |
| 303 var iteratorVariable = new VariableDeclaration( | 543 var iteratorVariable = new VariableDeclaration( |
| 304 ':for-iterator', | 544 ':for-iterator', |
| 305 initializer: new ConstructorInvocation( | 545 initializer: new ConstructorInvocation( |
| 306 helper.streamIteratorConstructor, | 546 helper.streamIteratorConstructor, |
| 307 new Arguments([expressionRewriter.rewrite(node.iterable)]))); | 547 new Arguments(<Expression>[stmt.iterable]))); |
| 308 | 548 |
| 309 // await iterator.moveNext() | 549 // await iterator.moveNext() |
| 310 var condition = new AwaitExpression(new MethodInvocation( | 550 var condition = new AwaitExpression(new MethodInvocation( |
| 311 new VariableGet(iteratorVariable), | 551 new VariableGet(iteratorVariable), |
| 312 new Name('moveNext'), | 552 new Name('moveNext'), |
| 313 new Arguments([]))); | 553 new Arguments(<Expression>[]))); |
| 314 | 554 |
| 315 // var <variable> = iterator.current; | 555 // var <variable> = iterator.current; |
| 316 var valueVariable = node.variable; | 556 var valueVariable = stmt.variable; |
| 317 valueVariable.initializer = new PropertyGet( | 557 valueVariable.initializer = new PropertyGet( |
| 318 new VariableGet(iteratorVariable), | 558 new VariableGet(iteratorVariable), |
| 319 new Name('current')); | 559 new Name('current')); |
| 320 valueVariable.initializer.parent = valueVariable; | 560 valueVariable.initializer.parent = valueVariable; |
| 321 | 561 |
| 322 var whileBody = new Block([valueVariable, node.body]); | 562 var whileBody = new Block(<Statement>[valueVariable, stmt.body]); |
| 323 var tryBody = new WhileStatement(condition, whileBody); | 563 var tryBody = new WhileStatement(condition, whileBody); |
| 324 | 564 |
| 325 // iterator.cancel(); | 565 // iterator.cancel(); |
| 326 var tryFinalizer = new ExpressionStatement( | 566 var tryFinalizer = new ExpressionStatement( |
| 327 new MethodInvocation( | 567 new MethodInvocation( |
| 328 new VariableGet(iteratorVariable), | 568 new VariableGet(iteratorVariable), |
| 329 new Name('cancel'), | 569 new Name('cancel'), |
| 330 new Arguments([]))); | 570 new Arguments(<Expression>[]))); |
| 331 | 571 |
| 332 var tryFinally = new TryFinally(tryBody, tryFinalizer); | 572 var tryFinally = new TryFinally(tryBody, tryFinalizer); |
| 333 | 573 |
| 334 var block = new Block([ | 574 var block = new Block(<Statement>[iteratorVariable, tryFinally]); |
| 335 iteratorVariable, | 575 block.accept(this); |
| 336 tryFinally, | |
| 337 ]); | |
| 338 return block.accept(this); | |
| 339 } else { | 576 } else { |
| 340 return super.visitForInStatement(node); | 577 stmt.iterable = |
| 578 expressionRewriter.rewrite(stmt.iterable, statements)..parent = stmt; | |
| 579 stmt.body = visitDelimited(stmt.body)..parent = stmt; | |
| 580 statements.add(stmt); | |
| 341 } | 581 } |
| 582 return null; | |
| 342 } | 583 } |
| 343 | 584 |
| 344 defaultExpression(TreeNode node) { | 585 TreeNode visitSwitchStatement(SwitchStatement stmt) { |
| 345 return expressionRewriter.rewrite(node); | 586 stmt.expression = |
| 587 expressionRewriter.rewrite(stmt.expression, statements)..parent = stmt; | |
| 588 for (var switchCase in stmt.cases) { | |
| 589 // Expressions in switch cases cannot contain await so they do not need to | |
| 590 // be translated. | |
| 591 switchCase.body = visitDelimited(switchCase.body)..parent = switchCase; | |
| 592 } | |
| 593 statements.add(stmt); | |
| 594 return null; | |
| 346 } | 595 } |
| 596 | |
| 597 TreeNode visitContinueSwitchStatement(ContinueSwitchStatement stmt) { | |
| 598 statements.add(stmt); | |
| 599 return null; | |
| 600 } | |
| 601 | |
| 602 TreeNode visitIfStatement(IfStatement stmt) { | |
| 603 stmt.condition = | |
| 604 expressionRewriter.rewrite(stmt.condition, statements)..parent = stmt; | |
| 605 stmt.then = visitDelimited(stmt.then)..parent = stmt; | |
| 606 if (stmt.otherwise != null) { | |
| 607 stmt.otherwise = visitDelimited(stmt.otherwise)..parent = stmt; | |
| 608 } | |
| 609 statements.add(stmt); | |
| 610 return null; | |
| 611 } | |
| 612 | |
| 613 TreeNode visitReturnStatement(ReturnStatement stmt) { | |
| 614 if (stmt.expression != null) { | |
| 615 stmt.expression = expressionRewriter.rewrite(stmt.expression, statements) | |
| 616 ..parent = stmt; | |
| 617 } | |
| 618 statements.add(stmt); | |
| 619 return null; | |
| 620 } | |
| 621 | |
| 622 TreeNode visitTryCatch(TryCatch stmt) { | |
| 623 ++currentTryDepth; | |
| 624 stmt.body = visitDelimited(stmt.body)..parent = stmt; | |
| 625 --currentTryDepth; | |
| 626 | |
| 627 ++currentCatchDepth; | |
| 628 for (var clause in stmt.catches) { | |
| 629 clause.body = visitDelimited(clause.body)..parent = clause; | |
| 630 } | |
| 631 --currentCatchDepth; | |
| 632 statements.add(stmt); | |
| 633 return null; | |
| 634 } | |
| 635 | |
| 636 TreeNode visitTryFinally(TryFinally stmt) { | |
| 637 ++currentTryDepth; | |
| 638 stmt.body = visitDelimited(stmt.body)..parent = stmt; | |
| 639 --currentTryDepth; | |
| 640 stmt.finalizer = visitDelimited(stmt.finalizer)..parent = stmt; | |
| 641 statements.add(stmt); | |
| 642 return null; | |
| 643 } | |
| 644 | |
| 645 TreeNode visitYieldStatement(YieldStatement stmt) { | |
| 646 stmt.expression = | |
| 647 expressionRewriter.rewrite(stmt.expression, statements)..parent = stmt; | |
| 648 statements.add(stmt); | |
| 649 return null; | |
| 650 } | |
| 651 | |
| 652 TreeNode visitVariableDeclaration(VariableDeclaration stmt) { | |
| 653 if (stmt.initializer != null) { | |
| 654 stmt.initializer = | |
| 655 expressionRewriter.rewrite(stmt.initializer, statements) | |
| 656 ..parent = stmt; | |
| 657 } | |
| 658 statements.add(stmt); | |
| 659 return null; | |
| 660 } | |
| 661 | |
| 662 TreeNode visitFunctionDeclaration(FunctionDeclaration stmt) { | |
| 663 stmt.function = stmt.function.accept(this)..parent = stmt; | |
| 664 statements.add(stmt); | |
| 665 return null; | |
| 666 } | |
| 667 | |
| 668 defaultExpression(TreeNode node) => throw 'unreachable'; | |
| 347 } | 669 } |
| 348 | 670 |
| 349 class AsyncStarFunctionRewriter extends AsyncRewriterBase { | 671 class AsyncStarFunctionRewriter extends AsyncRewriterBase { |
| 350 VariableDeclaration controllerVariable; | 672 VariableDeclaration controllerVariable; |
| 351 | 673 |
| 352 AsyncStarFunctionRewriter(helper, enclosingFunction) | 674 AsyncStarFunctionRewriter(helper, enclosingFunction) |
| 353 : super(helper, enclosingFunction); | 675 : super(helper, enclosingFunction); |
| 354 | 676 |
| 355 FunctionNode rewrite() { | 677 FunctionNode rewrite() { |
| 356 var statements = <Statement>[]; | 678 var statements = <Statement>[]; |
| 357 | 679 |
| 358 // var :controller; | 680 // var :controller; |
| 359 controllerVariable = new VariableDeclaration(":controller"); | 681 controllerVariable = new VariableDeclaration(":controller"); |
| 360 statements.add(controllerVariable); | 682 statements.add(controllerVariable); |
| 361 | 683 |
| 362 super.setupAsyncContinuations(statements); | 684 super.setupAsyncContinuations(statements); |
| 363 | 685 |
| 364 // :controller = new _AsyncController(:async_op); | 686 // :controller = new _AsyncController(:async_op); |
| 365 var arguments = new Arguments([new VariableGet(nestedClosureVariable)]); | 687 var arguments = |
| 688 new Arguments(<Expression>[new VariableGet(nestedClosureVariable)]); | |
| 366 var buildController = new ConstructorInvocation( | 689 var buildController = new ConstructorInvocation( |
| 367 helper.streamControllerConstructor, arguments); | 690 helper.streamControllerConstructor, arguments); |
| 368 var setController = new ExpressionStatement( | 691 var setController = new ExpressionStatement( |
| 369 new VariableSet(controllerVariable, buildController)); | 692 new VariableSet(controllerVariable, buildController)); |
| 370 statements.add(setController); | 693 statements.add(setController); |
| 371 | 694 |
| 372 // return :controller.stream; | 695 // return :controller.stream; |
| 373 var completerGet = new VariableGet(controllerVariable); | 696 var completerGet = new VariableGet(controllerVariable); |
| 374 var returnStatement = new ReturnStatement( | 697 var returnStatement = new ReturnStatement( |
| 375 new PropertyGet(completerGet, new Name('stream', helper.asyncLibrary))); | 698 new PropertyGet(completerGet, new Name('stream', helper.asyncLibrary))); |
| 376 statements.add(returnStatement); | 699 statements.add(returnStatement); |
| 377 | 700 |
| 378 enclosingFunction.body = new Block(statements); | 701 enclosingFunction.body = new Block(statements); |
| 379 enclosingFunction.body.parent = enclosingFunction; | 702 enclosingFunction.body.parent = enclosingFunction; |
| 380 enclosingFunction.asyncMarker = AsyncMarker.Sync; | 703 enclosingFunction.asyncMarker = AsyncMarker.Sync; |
| 381 return enclosingFunction; | 704 return enclosingFunction; |
| 382 } | 705 } |
| 383 | 706 |
| 384 Statement buildClosureBody(Statement node) { | |
| 385 // The body will insert calls to | |
| 386 // :controller.add() | |
| 387 // :controller.addStream() | |
| 388 // :controller.addError() | |
| 389 // :controller.close() | |
| 390 return node.accept(this); | |
| 391 } | |
| 392 | |
| 393 Statement buildCatchBody(exceptionVariable, stackTraceVariable) { | 707 Statement buildCatchBody(exceptionVariable, stackTraceVariable) { |
| 394 return new ExpressionStatement( | 708 return new ExpressionStatement( |
| 395 new MethodInvocation( | 709 new MethodInvocation( |
| 396 new VariableGet(controllerVariable), | 710 new VariableGet(controllerVariable), |
| 397 new Name("completeError", helper.asyncLibrary), | 711 new Name("completeError", helper.asyncLibrary), |
| 398 new Arguments([new VariableGet(exceptionVariable), | 712 new Arguments(<Expression>[new VariableGet(exceptionVariable), |
| 399 new VariableGet(stackTraceVariable)]))); | 713 new VariableGet(stackTraceVariable)]))); |
| 400 } | 714 } |
| 401 | 715 |
| 402 visitYieldStatement(YieldStatement node) { | 716 TreeNode visitYieldStatement(YieldStatement stmt) { |
| 403 var transformedExpression = node.expression.accept(this); | 717 Expression expr = expressionRewriter.rewrite(stmt.expression, statements); |
| 404 | 718 |
| 405 var addExpression = new MethodInvocation( | 719 var addExpression = new MethodInvocation( |
| 406 new VariableGet(controllerVariable), | 720 new VariableGet(controllerVariable), |
| 407 new Name(node.isYieldStar ? 'addStream' : 'add', helper.asyncLibrary), | 721 new Name(stmt.isYieldStar ? 'addStream' : 'add', helper.asyncLibrary), |
| 408 new Arguments([transformedExpression])); | 722 new Arguments(<Expression>[expr])); |
| 409 | 723 |
| 410 var addAndReturnOrYield = new IfStatement( | 724 statements.add(new IfStatement( |
| 411 addExpression, | 725 addExpression, |
| 412 new ReturnStatement(new NullLiteral()), | 726 new ReturnStatement(new NullLiteral()), |
| 413 createContinuationPoint()); | 727 createContinuationPoint())); |
| 414 return new Block([addAndReturnOrYield]); | 728 return null; |
| 415 } | 729 } |
| 416 | 730 |
| 417 visitReturnStatement(ReturnStatement node) { | 731 TreeNode visitReturnStatement(ReturnStatement node) { |
| 418 // async* functions cannot have normal [ReturnStatement]s in them. | 732 // async* functions cannot have normal [ReturnStatement]s in them. |
| 419 assert(node.expression == null || node.expression is NullLiteral); | 733 assert(node.expression == null || node.expression is NullLiteral); |
| 420 | 734 |
| 421 var close = new ExpressionStatement( | 735 statements.add(new ExpressionStatement( |
| 422 new MethodInvocation( | 736 new MethodInvocation( |
| 423 new VariableGet(controllerVariable), | 737 new VariableGet(controllerVariable), |
| 424 new Name("close", helper.asyncLibrary), | 738 new Name("close", helper.asyncLibrary), |
| 425 new Arguments([]))); | 739 new Arguments(<Expression>[])))); |
| 426 var returnStatement = new ReturnStatement(); | 740 statements.add(new ReturnStatement()); |
| 427 return new Block([close, returnStatement]); | 741 return null; |
| 428 } | 742 } |
| 429 } | 743 } |
| 430 | 744 |
| 431 class AsyncFunctionRewriter extends AsyncRewriterBase { | 745 class AsyncFunctionRewriter extends AsyncRewriterBase { |
| 432 VariableDeclaration completerVariable; | 746 VariableDeclaration completerVariable; |
| 433 | 747 |
| 434 AsyncFunctionRewriter(helper, enclosingFunction) | 748 AsyncFunctionRewriter(helper, enclosingFunction) |
| 435 : super(helper, enclosingFunction); | 749 : super(helper, enclosingFunction); |
| 436 | 750 |
| 437 FunctionNode rewrite() { | 751 FunctionNode rewrite() { |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 459 var returnStatement = new ReturnStatement( | 773 var returnStatement = new ReturnStatement( |
| 460 new PropertyGet(completerGet, new Name('future', helper.asyncLibrary))); | 774 new PropertyGet(completerGet, new Name('future', helper.asyncLibrary))); |
| 461 statements.add(returnStatement); | 775 statements.add(returnStatement); |
| 462 | 776 |
| 463 enclosingFunction.body = new Block(statements); | 777 enclosingFunction.body = new Block(statements); |
| 464 enclosingFunction.body.parent = enclosingFunction; | 778 enclosingFunction.body.parent = enclosingFunction; |
| 465 enclosingFunction.asyncMarker = AsyncMarker.Sync; | 779 enclosingFunction.asyncMarker = AsyncMarker.Sync; |
| 466 return enclosingFunction; | 780 return enclosingFunction; |
| 467 } | 781 } |
| 468 | 782 |
| 469 Statement buildClosureBody(Statement node) { | |
| 470 // TODO(kustermann): Can we assume the frontend will insert proper | |
| 471 // [ReturnStatement]s? | |
| 472 | |
| 473 // Translating the body will insert calls to | |
| 474 // :completer.completeError() | |
| 475 // :completer.complete() | |
| 476 return node.accept(this); | |
| 477 } | |
| 478 | |
| 479 Statement buildCatchBody(exceptionVariable, stackTraceVariable) { | 783 Statement buildCatchBody(exceptionVariable, stackTraceVariable) { |
| 480 return new ExpressionStatement( | 784 return new ExpressionStatement( |
| 481 new MethodInvocation( | 785 new MethodInvocation( |
| 482 new VariableGet(completerVariable), | 786 new VariableGet(completerVariable), |
| 483 new Name("completeError", helper.asyncLibrary), | 787 new Name("completeError", helper.asyncLibrary), |
| 484 new Arguments([new VariableGet(exceptionVariable), | 788 new Arguments([new VariableGet(exceptionVariable), |
| 485 new VariableGet(stackTraceVariable)]))); | 789 new VariableGet(stackTraceVariable)]))); |
| 486 } | 790 } |
| 487 | 791 |
| 488 visitReturnStatement(ReturnStatement node) { | 792 visitReturnStatement(ReturnStatement node) { |
| 489 var transformedExpression; | 793 var expr; |
| 490 if (node.expression == null) { | 794 if (node.expression == null) { |
| 491 transformedExpression = new NullLiteral(); | 795 expr = new NullLiteral(); |
| 492 } else { | 796 } else { |
| 493 transformedExpression = expressionRewriter.rewrite(node.expression); | 797 expr = expressionRewriter.rewrite(node.expression, statements); |
| 494 } | 798 } |
| 495 | 799 |
| 496 // Note: transformed expression can't be used directly as part of the | 800 statements.add(new ExpressionStatement( |
| 497 // method invocation because it might contain yield points and | |
| 498 // expression stack might not be empty. | |
| 499 var resultVar = new VariableDeclaration(':async_temp', | |
| 500 initializer: transformedExpression); | |
| 501 var completeCompleter = new ExpressionStatement( | |
| 502 new MethodInvocation( | 801 new MethodInvocation( |
| 503 new VariableGet(completerVariable), | 802 new VariableGet(completerVariable), |
| 504 new Name("complete", helper.asyncLibrary), | 803 new Name("complete", helper.asyncLibrary), |
| 505 new Arguments([new VariableGet(resultVar)]))); | 804 new Arguments([expr])))); |
| 506 var returnStatement = new ReturnStatement(new NullLiteral()); | 805 statements.add(new ReturnStatement(new NullLiteral())); |
| 507 return new Block([resultVar, completeCompleter, returnStatement]); | 806 return null; |
| 508 } | 807 } |
| 509 } | 808 } |
| 510 | 809 |
| 511 class HelperNodes { | 810 class HelperNodes { |
| 512 final Library asyncLibrary; | 811 final Library asyncLibrary; |
| 513 final Library coreLibrary; | 812 final Library coreLibrary; |
| 514 final Procedure printProcedure; | 813 final Procedure printProcedure; |
| 515 final Procedure completerConstructor; | 814 final Procedure completerConstructor; |
| 516 final Procedure futureMicrotaskConstructor; | 815 final Procedure futureMicrotaskConstructor; |
| 517 final Constructor streamControllerConstructor; | 816 final Constructor streamControllerConstructor; |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 586 coreLibrary, | 885 coreLibrary, |
| 587 findProcedure(coreLibrary, 'print'), | 886 findProcedure(coreLibrary, 'print'), |
| 588 findFactoryConstructor(completerClass, 'sync'), | 887 findFactoryConstructor(completerClass, 'sync'), |
| 589 findConstructor(syncIterableClass, ''), | 888 findConstructor(syncIterableClass, ''), |
| 590 findConstructor(streamIteratorClass , ''), | 889 findConstructor(streamIteratorClass , ''), |
| 591 findFactoryConstructor(futureClass, 'microtask'), | 890 findFactoryConstructor(futureClass, 'microtask'), |
| 592 findConstructor(streamControllerClass, ''), | 891 findConstructor(streamControllerClass, ''), |
| 593 findProcedure(asyncLibrary, '_asyncThenWrapperHelper'), | 892 findProcedure(asyncLibrary, '_asyncThenWrapperHelper'), |
| 594 findProcedure(asyncLibrary, '_asyncErrorWrapperHelper'), | 893 findProcedure(asyncLibrary, '_asyncErrorWrapperHelper'), |
| 595 findProcedure(asyncLibrary, '_awaitHelper')); | 894 findProcedure(asyncLibrary, '_awaitHelper')); |
| 596 } | 895 } |
| 597 } | 896 } |
| OLD | NEW |