| 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'; |
| 11 | 11 |
| 12 import 'async.dart'; | 12 import 'async.dart'; |
| 13 | 13 |
| 14 Program transformProgram(Program program) { | 14 Program transformProgram(Program program) { |
| 15 var helper = new HelperNodes.fromProgram(program); | 15 var helper = new HelperNodes.fromProgram(program); |
| 16 var rewriter = new RecursiveContinuationRewriter(helper); | 16 var rewriter = new RecursiveContinuationRewriter(helper); |
| 17 return rewriter.rewriteProgram(program); | 17 return rewriter.rewriteProgram(program); |
| 18 } | 18 } |
| 19 | 19 |
| 20 class RecursiveContinuationRewriter extends Transformer { | 20 class RecursiveContinuationRewriter extends Transformer { |
| 21 final HelperNodes helper; | 21 final HelperNodes helper; |
| 22 final VariableDeclaration asyncJumpVariable = | 22 final VariableDeclaration asyncJumpVariable = new VariableDeclaration( |
| 23 new VariableDeclaration(":await_jump_var", initializer: new IntLiteral(0))
; | 23 ":await_jump_var", |
| 24 initializer: new IntLiteral(0)); |
| 24 final VariableDeclaration asyncContextVariable = | 25 final VariableDeclaration asyncContextVariable = |
| 25 new VariableDeclaration(":await_ctx_var"); | 26 new VariableDeclaration(":await_ctx_var"); |
| 26 | 27 |
| 27 RecursiveContinuationRewriter(this.helper); | 28 RecursiveContinuationRewriter(this.helper); |
| 28 | 29 |
| 29 Program rewriteProgram(Program node) { | 30 Program rewriteProgram(Program node) { |
| 30 return node.accept(this); | 31 return node.accept(this); |
| 31 } | 32 } |
| 32 | 33 |
| 33 visitFunctionNode(FunctionNode node) { | 34 visitFunctionNode(FunctionNode node) { |
| 34 switch (node.asyncMarker) { | 35 switch (node.asyncMarker) { |
| 35 case AsyncMarker.Sync: | 36 case AsyncMarker.Sync: |
| 36 return super.visitFunctionNode(node); | 37 case AsyncMarker.SyncYielding: |
| 38 node.transformChildren(new RecursiveContinuationRewriter(helper)); |
| 39 return node; |
| 37 case AsyncMarker.SyncStar: | 40 case AsyncMarker.SyncStar: |
| 38 return new SyncStarFunctionRewriter(helper, node).rewrite(); | 41 return new SyncStarFunctionRewriter(helper, node).rewrite(); |
| 39 case AsyncMarker.Async: | 42 case AsyncMarker.Async: |
| 40 return new AsyncFunctionRewriter(helper, node).rewrite(); | 43 return new AsyncFunctionRewriter(helper, node).rewrite(); |
| 41 case AsyncMarker.AsyncStar: | 44 case AsyncMarker.AsyncStar: |
| 42 return new AsyncStarFunctionRewriter(helper, node).rewrite(); | 45 return new AsyncStarFunctionRewriter(helper, node).rewrite(); |
| 43 case AsyncMarker.SyncYielding: | |
| 44 return super.visitFunctionNode(node); | |
| 45 } | 46 } |
| 46 } | 47 } |
| 47 } | 48 } |
| 48 | 49 |
| 49 abstract class ContinuationRewriterBase extends RecursiveContinuationRewriter { | 50 abstract class ContinuationRewriterBase extends RecursiveContinuationRewriter { |
| 50 final FunctionNode enclosingFunction; | 51 final FunctionNode enclosingFunction; |
| 51 | 52 |
| 52 int currentTryDepth; // Nesting depth for try-blocks. | 53 int currentTryDepth; // Nesting depth for try-blocks. |
| 53 int currentCatchDepth = 0; // Nesting depth for catch-blocks. | 54 int currentCatchDepth = 0; // Nesting depth for catch-blocks. |
| 54 int capturedTryDepth = 0; // Deepest yield point within a try-block. | 55 int capturedTryDepth = 0; // Deepest yield point within a try-block. |
| 55 int capturedCatchDepth = 0; // Deepest yield point within a catch-block. | 56 int capturedCatchDepth = 0; // Deepest yield point within a catch-block. |
| 56 | 57 |
| 57 ContinuationRewriterBase(HelperNodes helper, | 58 ContinuationRewriterBase(HelperNodes helper, 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 15 matching lines...) Expand all Loading... |
| 88 currentTryDepth--; | 88 currentTryDepth--; |
| 89 } | 89 } |
| 90 if (node.finalizer != null) { | 90 if (node.finalizer != null) { |
| 91 node.finalizer = node.finalizer.accept(this); | 91 node.finalizer = node.finalizer.accept(this); |
| 92 node.finalizer?.parent = node; | 92 node.finalizer?.parent = node; |
| 93 } | 93 } |
| 94 return node; | 94 return node; |
| 95 } | 95 } |
| 96 | 96 |
| 97 Iterable<VariableDeclaration> createCapturedTryVariables() => | 97 Iterable<VariableDeclaration> createCapturedTryVariables() => |
| 98 new Iterable.generate(capturedTryDepth, (depth) => | 98 new Iterable.generate(capturedTryDepth, |
| 99 new VariableDeclaration(":saved_try_context_var${depth}")); | 99 (depth) => new VariableDeclaration(":saved_try_context_var${depth}")); |
| 100 | 100 |
| 101 Iterable<VariableDeclaration> createCapturedCatchVariables() => | 101 Iterable<VariableDeclaration> createCapturedCatchVariables() => |
| 102 new Iterable.generate(capturedCatchDepth).expand((depth) => [ | 102 new Iterable.generate(capturedCatchDepth).expand((depth) => [ |
| 103 new VariableDeclaration(":exception${depth}"), | 103 new VariableDeclaration(":exception${depth}"), |
| 104 new VariableDeclaration(":stack_trace${depth}"), | 104 new VariableDeclaration(":stack_trace${depth}"), |
| 105 ]); | 105 ]); |
| 106 | 106 |
| 107 List<VariableDeclaration> variableDeclarations() => | 107 List<VariableDeclaration> variableDeclarations() => |
| 108 [asyncJumpVariable, asyncContextVariable] | 108 [asyncJumpVariable, asyncContextVariable] |
| 109 ..addAll(createCapturedTryVariables()) | 109 ..addAll(createCapturedTryVariables()) |
| 110 ..addAll(createCapturedCatchVariables()); | 110 ..addAll(createCapturedCatchVariables()); |
| 111 } | 111 } |
| 112 | 112 |
| 113 class SyncStarFunctionRewriter extends ContinuationRewriterBase { | 113 class SyncStarFunctionRewriter extends ContinuationRewriterBase { |
| 114 final VariableDeclaration iteratorVariable = | 114 final VariableDeclaration iteratorVariable = |
| 115 new VariableDeclaration(":iterator"); | 115 new VariableDeclaration(":iterator"); |
| 116 | 116 |
| 117 SyncStarFunctionRewriter(helper, enclosingFunction) | 117 SyncStarFunctionRewriter(helper, enclosingFunction) |
| 118 : super(helper, enclosingFunction); | 118 : super(helper, enclosingFunction); |
| 119 | 119 |
| 120 FunctionNode rewrite() { | 120 FunctionNode rewrite() { |
| 121 // :sync_body(:iterator) { | 121 // :sync_body(:iterator) { |
| 122 // modified <node.body>; | 122 // modified <node.body>; |
| 123 // } | 123 // } |
| 124 final nestedClosureVariable = new VariableDeclaration(":sync_op"); | 124 final nestedClosureVariable = new VariableDeclaration(":sync_op"); |
| 125 final function = new FunctionNode( | 125 final function = new FunctionNode(buildClosureBody(), |
| 126 buildClosureBody(), | |
| 127 positionalParameters: [iteratorVariable], | 126 positionalParameters: [iteratorVariable], |
| 128 requiredParameterCount: 1, | 127 requiredParameterCount: 1, |
| 129 asyncMarker: AsyncMarker.SyncYielding); | 128 asyncMarker: AsyncMarker.SyncYielding); |
| 130 final closureFunction = | 129 final closureFunction = |
| 131 new FunctionDeclaration(nestedClosureVariable, function); | 130 new FunctionDeclaration(nestedClosureVariable, function); |
| 132 | 131 |
| 133 // return new _SyncIterable(:sync_body); | 132 // return new _SyncIterable(:sync_body); |
| 134 final arguments = new Arguments([new VariableGet(nestedClosureVariable)]); | 133 final arguments = new Arguments([new VariableGet(nestedClosureVariable)]); |
| 135 final returnStatement = new ReturnStatement(new ConstructorInvocation( | 134 final returnStatement = new ReturnStatement( |
| 136 helper.syncIterableConstructor, arguments)); | 135 new ConstructorInvocation(helper.syncIterableConstructor, arguments)); |
| 137 | 136 |
| 138 enclosingFunction.body = new Block([] | 137 enclosingFunction.body = new Block([] |
| 139 ..addAll(variableDeclarations()) | 138 ..addAll(variableDeclarations()) |
| 140 ..addAll([closureFunction, returnStatement])); | 139 ..addAll([closureFunction, returnStatement])); |
| 141 enclosingFunction.body.parent = enclosingFunction; | 140 enclosingFunction.body.parent = enclosingFunction; |
| 142 enclosingFunction.asyncMarker = AsyncMarker.Sync; | 141 enclosingFunction.asyncMarker = AsyncMarker.Sync; |
| 143 return enclosingFunction; | 142 return enclosingFunction; |
| 144 } | 143 } |
| 145 | 144 |
| 146 Statement buildClosureBody() { | 145 Statement buildClosureBody() { |
| 147 // The body will insert calls to | 146 // The body will insert calls to |
| 148 // :iterator.current_= | 147 // :iterator.current_= |
| 149 // :iterator.isYieldEach= | 148 // :iterator.isYieldEach= |
| 150 // and return `true` as long as it did something and `false` when it's done. | 149 // and return `true` as long as it did something and `false` when it's done. |
| 151 return enclosingFunction.body.accept(this); | 150 return enclosingFunction.body.accept(this); |
| 152 } | 151 } |
| 153 | 152 |
| 154 visitYieldStatement(YieldStatement node) { | 153 visitYieldStatement(YieldStatement node) { |
| 155 var transformedExpression = node.expression.accept(this); | 154 var transformedExpression = node.expression.accept(this); |
| 156 | 155 |
| 157 var statements = []; | 156 var statements = []; |
| 158 if (node.isYieldStar) { | 157 if (node.isYieldStar) { |
| 159 var markYieldEach = new ExpressionStatement(new PropertySet( | 158 var markYieldEach = new ExpressionStatement(new PropertySet( |
| 160 new VariableGet(iteratorVariable), | 159 new VariableGet(iteratorVariable), |
| 161 new Name("isYieldEach", helper.coreLibrary), | 160 new Name("isYieldEach", helper.coreLibrary), |
| 162 new BoolLiteral(true))); | 161 new BoolLiteral(true))); |
| 163 statements.add(markYieldEach); | 162 statements.add(markYieldEach); |
| 164 } | 163 } |
| 165 | 164 |
| 166 var setCurrentIteratorValue = new ExpressionStatement(new PropertySet( | 165 var setCurrentIteratorValue = new ExpressionStatement(new PropertySet( |
| 167 new VariableGet(iteratorVariable), | 166 new VariableGet(iteratorVariable), |
| 168 new Name("_current", helper.coreLibrary), | 167 new Name("_current", helper.coreLibrary), |
| 169 transformedExpression)); | 168 transformedExpression)); |
| 170 | 169 |
| 171 statements.add(setCurrentIteratorValue); | 170 statements.add(setCurrentIteratorValue); |
| 172 statements.add(createContinuationPoint(new BoolLiteral(true))); | 171 statements.add(createContinuationPoint(new BoolLiteral(true))); |
| 173 return new Block(statements); | 172 return new Block(statements); |
| 174 } | 173 } |
| 175 } | 174 } |
| 176 | 175 |
| 177 abstract class AsyncRewriterBase extends ContinuationRewriterBase { | 176 abstract class AsyncRewriterBase extends ContinuationRewriterBase { |
| 178 final VariableDeclaration nestedClosureVariable = | 177 final VariableDeclaration nestedClosureVariable = |
| 179 new VariableDeclaration(":async_op"); | 178 new VariableDeclaration(":async_op"); |
| 180 final VariableDeclaration thenContinuationVariable = | 179 final VariableDeclaration thenContinuationVariable = |
| 181 new VariableDeclaration(":async_op_then"); | 180 new VariableDeclaration(":async_op_then"); |
| 182 final VariableDeclaration catchErrorContinuationVariable = | 181 final VariableDeclaration catchErrorContinuationVariable = |
| 183 new VariableDeclaration(":async_op_error"); | 182 new VariableDeclaration(":async_op_error"); |
| 184 | 183 |
| 185 ExpressionLifter expressionRewriter; | 184 ExpressionLifter expressionRewriter; |
| 186 | 185 |
| 187 AsyncRewriterBase(helper, enclosingFunction) | 186 AsyncRewriterBase(helper, enclosingFunction) |
| 188 // Body is wrapped in the try-catch so initial currentTryDepth is 1. | 187 // Body is wrapped in the try-catch so initial currentTryDepth is 1. |
| 189 : super(helper, enclosingFunction, currentTryDepth: 1) { | 188 : super(helper, enclosingFunction, currentTryDepth: 1) {} |
| 190 } | |
| 191 | 189 |
| 192 setupAsyncContinuations(List<Statement> statements) { | 190 void setupAsyncContinuations(List<Statement> statements) { |
| 193 expressionRewriter = new ExpressionLifter(this); | 191 expressionRewriter = new ExpressionLifter(this); |
| 194 | 192 |
| 195 // var :async_op_then; | 193 // var :async_op_then; |
| 196 statements.add(thenContinuationVariable); | 194 statements.add(thenContinuationVariable); |
| 197 | 195 |
| 198 // var :async_op_error; | 196 // var :async_op_error; |
| 199 statements.add(catchErrorContinuationVariable); | 197 statements.add(catchErrorContinuationVariable); |
| 200 | 198 |
| 201 // :async_op([:result, :exception, :stack_trace]) { | 199 // :async_op([:result, :exception, :stack_trace]) { |
| 202 // modified <node.body>; | 200 // modified <node.body>; |
| 203 // } | 201 // } |
| 204 final parameters = [ | 202 final parameters = <VariableDeclaration>[ |
| 205 expressionRewriter.asyncResult, | 203 expressionRewriter.asyncResult, |
| 206 new VariableDeclaration(':exception'), | 204 new VariableDeclaration(':exception'), |
| 207 new VariableDeclaration(':stack_trace'), | 205 new VariableDeclaration(':stack_trace'), |
| 208 ]; | 206 ]; |
| 209 final function = new FunctionNode( | 207 final function = new FunctionNode(buildWrappedBody(), |
| 210 buildWrappedBody(), | |
| 211 positionalParameters: parameters, | 208 positionalParameters: parameters, |
| 212 requiredParameterCount: 0, | 209 requiredParameterCount: 0, |
| 213 asyncMarker: AsyncMarker.SyncYielding); | 210 asyncMarker: AsyncMarker.SyncYielding); |
| 214 | 211 |
| 215 // The await expression lifter might have created a number of | 212 // The await expression lifter might have created a number of |
| 216 // [VariableDeclarations]. | 213 // [VariableDeclarations]. |
| 217 // TODO(kustermann): If we didn't need any variables we should not emit | 214 // TODO(kustermann): If we didn't need any variables we should not emit |
| 218 // these. | 215 // these. |
| 219 statements.addAll(variableDeclarations()); | 216 statements.addAll(variableDeclarations()); |
| 220 statements.addAll(expressionRewriter.variables); | 217 statements.addAll(expressionRewriter.variables); |
| 221 | 218 |
| 222 // Now add the closure function itself. | 219 // Now add the closure function itself. |
| 223 final closureFunction = | 220 final closureFunction = |
| 224 new FunctionDeclaration(nestedClosureVariable, function); | 221 new FunctionDeclaration(nestedClosureVariable, function); |
| 225 statements.add(closureFunction); | 222 statements.add(closureFunction); |
| 226 | 223 |
| 227 // :async_op_then = _asyncThenWrapperHelper(asyncBody); | 224 // :async_op_then = _asyncThenWrapperHelper(asyncBody); |
| 228 final boundThenClosure = new StaticInvocation( | 225 final boundThenClosure = new StaticInvocation(helper.asyncThenWrapper, |
| 229 helper.asyncThenWrapper, | 226 new Arguments(<Expression>[new VariableGet(nestedClosureVariable)])); |
| 230 new Arguments([new VariableGet(nestedClosureVariable)])); | 227 final thenClosureVariableAssign = new ExpressionStatement( |
| 231 final thenClosureVariableAssign = new ExpressionStatement(new VariableSet( | 228 new VariableSet(thenContinuationVariable, boundThenClosure)); |
| 232 thenContinuationVariable, boundThenClosure)); | |
| 233 statements.add(thenClosureVariableAssign); | 229 statements.add(thenClosureVariableAssign); |
| 234 | 230 |
| 235 // :async_op_error = _asyncErrorWrapperHelper(asyncBody); | 231 // :async_op_error = _asyncErrorWrapperHelper(asyncBody); |
| 236 final boundCatchErrorClosure = new StaticInvocation( | 232 final boundCatchErrorClosure = new StaticInvocation( |
| 237 helper.asyncErrorWrapper, | 233 helper.asyncErrorWrapper, |
| 238 new Arguments([new VariableGet(nestedClosureVariable)])); | 234 new Arguments(<Expression>[new VariableGet(nestedClosureVariable)])); |
| 239 final catchErrorClosureVariableAssign = | 235 final catchErrorClosureVariableAssign = new ExpressionStatement( |
| 240 new ExpressionStatement(new VariableSet( | 236 new VariableSet( |
| 241 catchErrorContinuationVariable , boundCatchErrorClosure)); | 237 catchErrorContinuationVariable, boundCatchErrorClosure)); |
| 242 statements.add(catchErrorClosureVariableAssign); | 238 statements.add(catchErrorClosureVariableAssign); |
| 243 } | 239 } |
| 244 | 240 |
| 245 Statement buildWrappedBody() { | 241 Statement buildWrappedBody() { |
| 246 // No explicit return at the end of the body => we will add one! | 242 // No explicit return at the end of the body => we will add one! |
| 247 var body = addReturnStatementIfNecessary(enclosingFunction.body); | 243 var body = addReturnStatementIfNecessary(enclosingFunction.body); |
| 248 var userBody = buildClosureBody(body); | 244 var userBody = visitDelimited(body); |
| 249 | 245 |
| 250 var exceptionVariable = new VariableDeclaration(":exception"); | 246 var exceptionVariable = new VariableDeclaration(":exception"); |
| 251 var stackTraceVariable = new VariableDeclaration(":stack_trace"); | 247 var stackTraceVariable = new VariableDeclaration(":stack_trace"); |
| 252 | 248 |
| 253 var completeErrorStatement = | 249 var completeErrorStatement = |
| 254 buildCatchBody(exceptionVariable, stackTraceVariable); | 250 buildCatchBody(exceptionVariable, stackTraceVariable); |
| 255 | 251 |
| 256 var catchBody = new Block(<Statement>[completeErrorStatement]); | 252 var catchBody = new Block(<Statement>[completeErrorStatement]); |
| 257 var catches = [new Catch(exceptionVariable, | 253 var catches = <Catch>[ |
| 258 catchBody, | 254 new Catch(exceptionVariable, catchBody, stackTrace: stackTraceVariable) |
| 259 stackTrace: stackTraceVariable)]; | 255 ]; |
| 260 return new TryCatch(userBody, catches); | 256 return new TryCatch(userBody, catches); |
| 261 } | 257 } |
| 262 | 258 |
| 263 addReturnStatementIfNecessary(Statement body) { | 259 Statement addReturnStatementIfNecessary(Statement body) { |
| 264 if (body is Block) { | 260 if (body is Block) { |
| 265 Block block = body; | 261 Block block = body; |
| 266 if (block.statements.isEmpty || | 262 if (block.statements.isEmpty || |
| 267 block.statements.last is! ReturnStatement) { | 263 block.statements.last is! ReturnStatement) { |
| 268 var returnStatement = new ReturnStatement(); | 264 var returnStatement = new ReturnStatement(); |
| 269 block.statements.add(returnStatement); | 265 block.statements.add(returnStatement); |
| 270 returnStatement.parent = block; | 266 returnStatement.parent = block; |
| 271 } | 267 } |
| 272 } else if (body is! ReturnStatement) { | 268 } else if (body is! ReturnStatement) { |
| 273 var returnStatement = new ReturnStatement(); | 269 var returnStatement = new ReturnStatement(); |
| 274 body = new Block([body, returnStatement]); | 270 body = new Block(<Statement>[body, returnStatement]); |
| 275 } | 271 } |
| 276 return body; | 272 return body; |
| 277 } | 273 } |
| 278 | 274 |
| 279 Statement buildClosureBody(Statement node); | 275 Statement buildCatchBody( |
| 280 | 276 Statement exceptionVariable, Statement stackTraceVariable); |
| 281 Statement buildCatchBody(Statement exceptionVariable, | 277 |
| 282 Statement stackTraceVariable); | 278 List<Statement> statements = <Statement>[]; |
| 283 | 279 |
| 284 visitForInStatement(ForInStatement node) { | 280 TreeNode visitInvalidStatement(InvalidStatement stmt) { |
| 285 if (node.isAsync) { | 281 statements.add(stmt); |
| 282 return null; |
| 283 } |
| 284 |
| 285 TreeNode visitExpressionStatement(ExpressionStatement stmt) { |
| 286 stmt.expression = expressionRewriter.rewrite(stmt.expression, statements) |
| 287 ..parent = stmt; |
| 288 statements.add(stmt); |
| 289 return null; |
| 290 } |
| 291 |
| 292 TreeNode visitBlock(Block stmt) { |
| 293 var saved = statements; |
| 294 statements = <Statement>[]; |
| 295 for (var statement in stmt.statements) { |
| 296 statement.accept(this); |
| 297 } |
| 298 saved.add(new Block(statements)); |
| 299 statements = saved; |
| 300 return null; |
| 301 } |
| 302 |
| 303 TreeNode visitEmptyStatement(EmptyStatement stmt) { |
| 304 statements.add(stmt); |
| 305 return null; |
| 306 } |
| 307 |
| 308 TreeNode visitAssertStatement(AssertStatement stmt) { |
| 309 // TODO! |
| 310 return null; |
| 311 } |
| 312 |
| 313 Statement visitDelimited(Statement stmt) { |
| 314 var saved = statements; |
| 315 statements = <Statement>[]; |
| 316 stmt.accept(this); |
| 317 Statement result = |
| 318 statements.length == 1 ? statements.first : new Block(statements); |
| 319 statements = saved; |
| 320 return result; |
| 321 } |
| 322 |
| 323 Statement visitLabeledStatement(LabeledStatement stmt) { |
| 324 stmt.body = visitDelimited(stmt.body)..parent = stmt; |
| 325 statements.add(stmt); |
| 326 return null; |
| 327 } |
| 328 |
| 329 Statement visitBreakStatement(BreakStatement stmt) { |
| 330 statements.add(stmt); |
| 331 return null; |
| 332 } |
| 333 |
| 334 TreeNode visitWhileStatement(WhileStatement stmt) { |
| 335 Statement body = visitDelimited(stmt.body); |
| 336 List<Statement> effects = <Statement>[]; |
| 337 Expression cond = expressionRewriter.rewrite(stmt.condition, effects); |
| 338 if (effects.isEmpty) { |
| 339 stmt.condition = cond..parent = stmt; |
| 340 stmt.body = body..parent = stmt; |
| 341 statements.add(stmt); |
| 342 } else { |
| 343 // The condition rewrote to a non-empty sequence of statements S* and |
| 344 // value V. Rewrite the loop to: |
| 345 // |
| 346 // L: while (true) { |
| 347 // S* |
| 348 // if (V) { |
| 349 // [body] |
| 350 // else { |
| 351 // break L; |
| 352 // } |
| 353 // } |
| 354 LabeledStatement labeled = new LabeledStatement(stmt); |
| 355 stmt.condition = new BoolLiteral(true)..parent = stmt; |
| 356 effects.add(new IfStatement(cond, body, new BreakStatement(labeled))); |
| 357 stmt.body = new Block(effects)..parent = stmt; |
| 358 statements.add(labeled); |
| 359 } |
| 360 return null; |
| 361 } |
| 362 |
| 363 TreeNode visitDoStatement(DoStatement stmt) { |
| 364 Statement body = visitDelimited(stmt.body); |
| 365 List<Statement> effects = <Statement>[]; |
| 366 stmt.condition = expressionRewriter.rewrite(stmt.condition, effects) |
| 367 ..parent = stmt; |
| 368 if (effects.isNotEmpty) { |
| 369 // The condition rewrote to a non-empty sequence of statements S* and |
| 370 // value V. Add the statements to the end of the loop body. |
| 371 Block block = body is Block ? body : body = new Block(<Statement>[body]); |
| 372 for (var effect in effects) { |
| 373 block.statements.add(effect); |
| 374 effect.parent = body; |
| 375 } |
| 376 } |
| 377 stmt.body = body..parent = stmt; |
| 378 statements.add(stmt); |
| 379 return null; |
| 380 } |
| 381 |
| 382 TreeNode visitForStatement(ForStatement stmt) { |
| 383 // Because of for-loop scoping and variable capture, it is tricky to deal |
| 384 // with await in the loop's variable initializers or update expressions. |
| 385 bool isSimple = true; |
| 386 int length = stmt.variables.length; |
| 387 List<List<Statement>> initEffects = new List<List<Statement>>(length); |
| 388 for (int i = 0; i < length; ++i) { |
| 389 VariableDeclaration decl = stmt.variables[i]; |
| 390 initEffects[i] = <Statement>[]; |
| 391 if (decl.initializer != null) { |
| 392 decl.initializer = expressionRewriter.rewrite( |
| 393 decl.initializer, initEffects[i])..parent = decl; |
| 394 } |
| 395 isSimple = isSimple && initEffects[i].isEmpty; |
| 396 } |
| 397 |
| 398 length = stmt.updates.length; |
| 399 List<List<Statement>> updateEffects = new List<List<Statement>>(length); |
| 400 for (int i = 0; i < length; ++i) { |
| 401 updateEffects[i] = <Statement>[]; |
| 402 stmt.updates[i] = expressionRewriter.rewrite( |
| 403 stmt.updates[i], updateEffects[i])..parent = stmt; |
| 404 isSimple = isSimple && updateEffects[i].isEmpty; |
| 405 } |
| 406 |
| 407 Statement body = visitDelimited(stmt.body); |
| 408 Expression cond = stmt.condition; |
| 409 List<Statement> condEffects; |
| 410 if (cond != null) { |
| 411 condEffects = <Statement>[]; |
| 412 cond = expressionRewriter.rewrite(stmt.condition, condEffects); |
| 413 } |
| 414 |
| 415 if (isSimple) { |
| 416 // If the condition contains await, we use a translation like the one for |
| 417 // while loops, but leaving the variable declarations and the update |
| 418 // expressions in place. |
| 419 if (condEffects == null || condEffects.isEmpty) { |
| 420 if (cond != null) stmt.condition = cond..parent = stmt; |
| 421 stmt.body = body..parent = stmt; |
| 422 statements.add(stmt); |
| 423 } else { |
| 424 LabeledStatement labeled = new LabeledStatement(stmt); |
| 425 // No condition in a for loop is the same as true. |
| 426 stmt.condition = null; |
| 427 condEffects |
| 428 .add(new IfStatement(cond, body, new BreakStatement(labeled))); |
| 429 stmt.body = new Block(condEffects)..parent = stmt; |
| 430 statements.add(labeled); |
| 431 } |
| 432 return null; |
| 433 } |
| 434 |
| 435 // If the rewrite of the initializer or update expressions produces a |
| 436 // non-empty sequence of statements then the loop is desugared. If the loop |
| 437 // has the form: |
| 438 // |
| 439 // label: for (Type x = init; cond; update) body |
| 440 // |
| 441 // it is translated as if it were: |
| 442 // |
| 443 // { |
| 444 // bool first = true; |
| 445 // Type temp; |
| 446 // label: while (true) { |
| 447 // Type x; |
| 448 // if (first) { |
| 449 // first = false; |
| 450 // x = init; |
| 451 // } else { |
| 452 // x = temp; |
| 453 // update; |
| 454 // } |
| 455 // if (cond) { |
| 456 // body; |
| 457 // temp = x; |
| 458 // } else { |
| 459 // break; |
| 460 // } |
| 461 // } |
| 462 // } |
| 463 |
| 464 // Place the loop variable declarations at the beginning of the body |
| 465 // statements and move their initializers to a guarded list of statements. |
| 466 // Add assignments to the loop variables from the previous iteration's temp |
| 467 // variables before the updates. |
| 468 // |
| 469 // temps.first is the flag 'first'. |
| 470 // TODO(kmillikin) bool type for first. |
| 471 List<VariableDeclaration> temps = <VariableDeclaration>[ |
| 472 new VariableDeclaration.forValue(new BoolLiteral(true), isFinal: false) |
| 473 ]; |
| 474 List<Statement> loopBody = <Statement>[]; |
| 475 List<Statement> initializers = <Statement>[ |
| 476 new ExpressionStatement( |
| 477 new VariableSet(temps.first, new BoolLiteral(false))) |
| 478 ]; |
| 479 List<Statement> updates = <Statement>[]; |
| 480 List<Statement> newBody = <Statement>[body]; |
| 481 for (int i = 0; i < stmt.variables.length; ++i) { |
| 482 VariableDeclaration decl = stmt.variables[i]; |
| 483 temps.add(new VariableDeclaration(null, type: decl.type)); |
| 484 loopBody.add(decl); |
| 485 if (decl.initializer != null) { |
| 486 initializers.addAll(initEffects[i]); |
| 487 initializers.add( |
| 488 new ExpressionStatement(new VariableSet(decl, decl.initializer))); |
| 489 decl.initializer = null; |
| 490 } |
| 491 updates.add(new ExpressionStatement( |
| 492 new VariableSet(decl, new VariableGet(temps.last)))); |
| 493 newBody.add(new ExpressionStatement( |
| 494 new VariableSet(temps.last, new VariableGet(decl)))); |
| 495 } |
| 496 // Add the updates to their guarded list of statements. |
| 497 for (int i = 0; i < stmt.updates.length; ++i) { |
| 498 updates.addAll(updateEffects[i]); |
| 499 updates.add(new ExpressionStatement(stmt.updates[i])); |
| 500 } |
| 501 // Initializers or updates could be empty. |
| 502 loopBody.add(new IfStatement(new VariableGet(temps.first), |
| 503 new Block(initializers), new Block(updates))); |
| 504 |
| 505 LabeledStatement labeled = new LabeledStatement(null); |
| 506 if (cond != null) { |
| 507 loopBody.addAll(condEffects); |
| 508 } else { |
| 509 cond = new BoolLiteral(true); |
| 510 } |
| 511 loopBody.add( |
| 512 new IfStatement(cond, new Block(newBody), new BreakStatement(labeled))); |
| 513 labeled.body = |
| 514 new WhileStatement(new BoolLiteral(true), new Block(loopBody)); |
| 515 statements.add(new Block(<Statement>[] |
| 516 ..addAll(temps) |
| 517 ..add(labeled))); |
| 518 return null; |
| 519 } |
| 520 |
| 521 TreeNode visitForInStatement(ForInStatement stmt) { |
| 522 if (stmt.isAsync) { |
| 286 // Transform | 523 // Transform |
| 287 // | 524 // |
| 288 // await for (var variable in <stream-expression>) { ... } | 525 // await for (var variable in <stream-expression>) { ... } |
| 289 // | 526 // |
| 290 // To: | 527 // To: |
| 291 // | 528 // |
| 292 // { | 529 // { |
| 293 // var :for-iterator = new StreamIterator(<stream-expression>); | 530 // var :for-iterator = new StreamIterator(<stream-expression>); |
| 294 // try { | 531 // try { |
| 295 // while (await :for-iterator.moveNext()) { | 532 // while (await :for-iterator.moveNext()) { |
| 296 // var <variable> = :for-iterator.current; | 533 // var <variable> = :for-iterator.current; |
| 297 // ... | 534 // ... |
| 298 // } | 535 // } |
| 299 // } finally { | 536 // } finally { |
| 300 // :for-iterator.cancel(); | 537 // :for-iterator.cancel(); |
| 301 // } | 538 // } |
| 302 // } | 539 // } |
| 303 var iteratorVariable = new VariableDeclaration( | 540 var iteratorVariable = new VariableDeclaration(':for-iterator', |
| 304 ':for-iterator', | |
| 305 initializer: new ConstructorInvocation( | 541 initializer: new ConstructorInvocation( |
| 306 helper.streamIteratorConstructor, | 542 helper.streamIteratorConstructor, |
| 307 new Arguments([expressionRewriter.rewrite(node.iterable)]))); | 543 new Arguments(<Expression>[stmt.iterable]))); |
| 308 | 544 |
| 309 // await iterator.moveNext() | 545 // await iterator.moveNext() |
| 310 var condition = new AwaitExpression(new MethodInvocation( | 546 var condition = new AwaitExpression(new MethodInvocation( |
| 311 new VariableGet(iteratorVariable), | 547 new VariableGet(iteratorVariable), |
| 312 new Name('moveNext'), | 548 new Name('moveNext'), |
| 313 new Arguments([]))); | 549 new Arguments(<Expression>[]))); |
| 314 | 550 |
| 315 // var <variable> = iterator.current; | 551 // var <variable> = iterator.current; |
| 316 var valueVariable = node.variable; | 552 var valueVariable = stmt.variable; |
| 317 valueVariable.initializer = new PropertyGet( | 553 valueVariable.initializer = new PropertyGet( |
| 318 new VariableGet(iteratorVariable), | 554 new VariableGet(iteratorVariable), new Name('current')); |
| 319 new Name('current')); | |
| 320 valueVariable.initializer.parent = valueVariable; | 555 valueVariable.initializer.parent = valueVariable; |
| 321 | 556 |
| 322 var whileBody = new Block([valueVariable, node.body]); | 557 var whileBody = new Block(<Statement>[valueVariable, stmt.body]); |
| 323 var tryBody = new WhileStatement(condition, whileBody); | 558 var tryBody = new WhileStatement(condition, whileBody); |
| 324 | 559 |
| 325 // iterator.cancel(); | 560 // iterator.cancel(); |
| 326 var tryFinalizer = new ExpressionStatement( | 561 var tryFinalizer = new ExpressionStatement(new MethodInvocation( |
| 327 new MethodInvocation( | 562 new VariableGet(iteratorVariable), |
| 328 new VariableGet(iteratorVariable), | 563 new Name('cancel'), |
| 329 new Name('cancel'), | 564 new Arguments(<Expression>[]))); |
| 330 new Arguments([]))); | |
| 331 | 565 |
| 332 var tryFinally = new TryFinally(tryBody, tryFinalizer); | 566 var tryFinally = new TryFinally(tryBody, tryFinalizer); |
| 333 | 567 |
| 334 var block = new Block([ | 568 var block = new Block(<Statement>[iteratorVariable, tryFinally]); |
| 335 iteratorVariable, | 569 block.accept(this); |
| 336 tryFinally, | |
| 337 ]); | |
| 338 return block.accept(this); | |
| 339 } else { | 570 } else { |
| 340 return super.visitForInStatement(node); | 571 stmt.iterable = expressionRewriter.rewrite(stmt.iterable, statements) |
| 572 ..parent = stmt; |
| 573 stmt.body = visitDelimited(stmt.body)..parent = stmt; |
| 574 statements.add(stmt); |
| 341 } | 575 } |
| 576 return null; |
| 342 } | 577 } |
| 343 | 578 |
| 344 defaultExpression(TreeNode node) { | 579 TreeNode visitSwitchStatement(SwitchStatement stmt) { |
| 345 return expressionRewriter.rewrite(node); | 580 stmt.expression = expressionRewriter.rewrite(stmt.expression, statements) |
| 581 ..parent = stmt; |
| 582 for (var switchCase in stmt.cases) { |
| 583 // Expressions in switch cases cannot contain await so they do not need to |
| 584 // be translated. |
| 585 switchCase.body = visitDelimited(switchCase.body)..parent = switchCase; |
| 586 } |
| 587 statements.add(stmt); |
| 588 return null; |
| 346 } | 589 } |
| 590 |
| 591 TreeNode visitContinueSwitchStatement(ContinueSwitchStatement stmt) { |
| 592 statements.add(stmt); |
| 593 return null; |
| 594 } |
| 595 |
| 596 TreeNode visitIfStatement(IfStatement stmt) { |
| 597 stmt.condition = expressionRewriter.rewrite(stmt.condition, statements) |
| 598 ..parent = stmt; |
| 599 stmt.then = visitDelimited(stmt.then)..parent = stmt; |
| 600 if (stmt.otherwise != null) { |
| 601 stmt.otherwise = visitDelimited(stmt.otherwise)..parent = stmt; |
| 602 } |
| 603 statements.add(stmt); |
| 604 return null; |
| 605 } |
| 606 |
| 607 TreeNode visitReturnStatement(ReturnStatement stmt) { |
| 608 if (stmt.expression != null) { |
| 609 stmt.expression = expressionRewriter.rewrite(stmt.expression, statements) |
| 610 ..parent = stmt; |
| 611 } |
| 612 statements.add(stmt); |
| 613 return null; |
| 614 } |
| 615 |
| 616 TreeNode visitTryCatch(TryCatch stmt) { |
| 617 ++currentTryDepth; |
| 618 stmt.body = visitDelimited(stmt.body)..parent = stmt; |
| 619 --currentTryDepth; |
| 620 |
| 621 ++currentCatchDepth; |
| 622 for (var clause in stmt.catches) { |
| 623 clause.body = visitDelimited(clause.body)..parent = clause; |
| 624 } |
| 625 --currentCatchDepth; |
| 626 statements.add(stmt); |
| 627 return null; |
| 628 } |
| 629 |
| 630 TreeNode visitTryFinally(TryFinally stmt) { |
| 631 ++currentTryDepth; |
| 632 stmt.body = visitDelimited(stmt.body)..parent = stmt; |
| 633 --currentTryDepth; |
| 634 stmt.finalizer = visitDelimited(stmt.finalizer)..parent = stmt; |
| 635 statements.add(stmt); |
| 636 return null; |
| 637 } |
| 638 |
| 639 TreeNode visitYieldStatement(YieldStatement stmt) { |
| 640 stmt.expression = expressionRewriter.rewrite(stmt.expression, statements) |
| 641 ..parent = stmt; |
| 642 statements.add(stmt); |
| 643 return null; |
| 644 } |
| 645 |
| 646 TreeNode visitVariableDeclaration(VariableDeclaration stmt) { |
| 647 if (stmt.initializer != null) { |
| 648 stmt.initializer = expressionRewriter.rewrite( |
| 649 stmt.initializer, statements)..parent = stmt; |
| 650 } |
| 651 statements.add(stmt); |
| 652 return null; |
| 653 } |
| 654 |
| 655 TreeNode visitFunctionDeclaration(FunctionDeclaration stmt) { |
| 656 stmt.function = stmt.function.accept(this)..parent = stmt; |
| 657 statements.add(stmt); |
| 658 return null; |
| 659 } |
| 660 |
| 661 defaultExpression(TreeNode node) => throw 'unreachable'; |
| 347 } | 662 } |
| 348 | 663 |
| 349 class AsyncStarFunctionRewriter extends AsyncRewriterBase { | 664 class AsyncStarFunctionRewriter extends AsyncRewriterBase { |
| 350 VariableDeclaration controllerVariable; | 665 VariableDeclaration controllerVariable; |
| 351 | 666 |
| 352 AsyncStarFunctionRewriter(helper, enclosingFunction) | 667 AsyncStarFunctionRewriter(helper, enclosingFunction) |
| 353 : super(helper, enclosingFunction); | 668 : super(helper, enclosingFunction); |
| 354 | 669 |
| 355 FunctionNode rewrite() { | 670 FunctionNode rewrite() { |
| 356 var statements = <Statement>[]; | 671 var statements = <Statement>[]; |
| 357 | 672 |
| 358 // var :controller; | 673 // var :controller; |
| 359 controllerVariable = new VariableDeclaration(":controller"); | 674 controllerVariable = new VariableDeclaration(":controller"); |
| 360 statements.add(controllerVariable); | 675 statements.add(controllerVariable); |
| 361 | 676 |
| 362 super.setupAsyncContinuations(statements); | 677 super.setupAsyncContinuations(statements); |
| 363 | 678 |
| 364 // :controller = new _AsyncController(:async_op); | 679 // :controller = new _AsyncController(:async_op); |
| 365 var arguments = new Arguments([new VariableGet(nestedClosureVariable)]); | 680 var arguments = |
| 681 new Arguments(<Expression>[new VariableGet(nestedClosureVariable)]); |
| 366 var buildController = new ConstructorInvocation( | 682 var buildController = new ConstructorInvocation( |
| 367 helper.streamControllerConstructor, arguments); | 683 helper.streamControllerConstructor, arguments); |
| 368 var setController = new ExpressionStatement( | 684 var setController = new ExpressionStatement( |
| 369 new VariableSet(controllerVariable, buildController)); | 685 new VariableSet(controllerVariable, buildController)); |
| 370 statements.add(setController); | 686 statements.add(setController); |
| 371 | 687 |
| 372 // return :controller.stream; | 688 // return :controller.stream; |
| 373 var completerGet = new VariableGet(controllerVariable); | 689 var completerGet = new VariableGet(controllerVariable); |
| 374 var returnStatement = new ReturnStatement( | 690 var returnStatement = new ReturnStatement( |
| 375 new PropertyGet(completerGet, new Name('stream', helper.asyncLibrary))); | 691 new PropertyGet(completerGet, new Name('stream', helper.asyncLibrary))); |
| 376 statements.add(returnStatement); | 692 statements.add(returnStatement); |
| 377 | 693 |
| 378 enclosingFunction.body = new Block(statements); | 694 enclosingFunction.body = new Block(statements); |
| 379 enclosingFunction.body.parent = enclosingFunction; | 695 enclosingFunction.body.parent = enclosingFunction; |
| 380 enclosingFunction.asyncMarker = AsyncMarker.Sync; | 696 enclosingFunction.asyncMarker = AsyncMarker.Sync; |
| 381 return enclosingFunction; | 697 return enclosingFunction; |
| 382 } | 698 } |
| 383 | 699 |
| 384 Statement buildClosureBody(Statement node) { | 700 Statement buildCatchBody(exceptionVariable, stackTraceVariable) { |
| 385 // The body will insert calls to | 701 return new ExpressionStatement(new MethodInvocation( |
| 386 // :controller.add() | 702 new VariableGet(controllerVariable), |
| 387 // :controller.addStream() | 703 new Name("completeError", helper.asyncLibrary), |
| 388 // :controller.addError() | 704 new Arguments(<Expression>[ |
| 389 // :controller.close() | 705 new VariableGet(exceptionVariable), |
| 390 return node.accept(this); | 706 new VariableGet(stackTraceVariable) |
| 707 ]))); |
| 391 } | 708 } |
| 392 | 709 |
| 393 Statement buildCatchBody(exceptionVariable, stackTraceVariable) { | 710 TreeNode visitYieldStatement(YieldStatement stmt) { |
| 394 return new ExpressionStatement( | 711 Expression expr = expressionRewriter.rewrite(stmt.expression, statements); |
| 395 new MethodInvocation( | 712 |
| 396 new VariableGet(controllerVariable), | 713 var addExpression = new MethodInvocation( |
| 397 new Name("completeError", helper.asyncLibrary), | 714 new VariableGet(controllerVariable), |
| 398 new Arguments([new VariableGet(exceptionVariable), | 715 new Name(stmt.isYieldStar ? 'addStream' : 'add', helper.asyncLibrary), |
| 399 new VariableGet(stackTraceVariable)]))); | 716 new Arguments(<Expression>[expr])); |
| 717 |
| 718 statements.add(new IfStatement(addExpression, |
| 719 new ReturnStatement(new NullLiteral()), createContinuationPoint())); |
| 720 return null; |
| 400 } | 721 } |
| 401 | 722 |
| 402 visitYieldStatement(YieldStatement node) { | 723 TreeNode visitReturnStatement(ReturnStatement node) { |
| 403 var transformedExpression = node.expression.accept(this); | |
| 404 | |
| 405 var addExpression = new MethodInvocation( | |
| 406 new VariableGet(controllerVariable), | |
| 407 new Name(node.isYieldStar ? 'addStream' : 'add', helper.asyncLibrary), | |
| 408 new Arguments([transformedExpression])); | |
| 409 | |
| 410 var addAndReturnOrYield = new IfStatement( | |
| 411 addExpression, | |
| 412 new ReturnStatement(new NullLiteral()), | |
| 413 createContinuationPoint()); | |
| 414 return new Block([addAndReturnOrYield]); | |
| 415 } | |
| 416 | |
| 417 visitReturnStatement(ReturnStatement node) { | |
| 418 // async* functions cannot have normal [ReturnStatement]s in them. | 724 // async* functions cannot have normal [ReturnStatement]s in them. |
| 419 assert(node.expression == null || node.expression is NullLiteral); | 725 assert(node.expression == null || node.expression is NullLiteral); |
| 420 | 726 |
| 421 var close = new ExpressionStatement( | 727 statements.add(new ExpressionStatement(new MethodInvocation( |
| 422 new MethodInvocation( | 728 new VariableGet(controllerVariable), |
| 423 new VariableGet(controllerVariable), | 729 new Name("close", helper.asyncLibrary), |
| 424 new Name("close", helper.asyncLibrary), | 730 new Arguments(<Expression>[])))); |
| 425 new Arguments([]))); | 731 statements.add(new ReturnStatement()); |
| 426 var returnStatement = new ReturnStatement(); | 732 return null; |
| 427 return new Block([close, returnStatement]); | |
| 428 } | 733 } |
| 429 } | 734 } |
| 430 | 735 |
| 431 class AsyncFunctionRewriter extends AsyncRewriterBase { | 736 class AsyncFunctionRewriter extends AsyncRewriterBase { |
| 432 VariableDeclaration completerVariable; | 737 VariableDeclaration completerVariable; |
| 433 | 738 |
| 434 AsyncFunctionRewriter(helper, enclosingFunction) | 739 AsyncFunctionRewriter(helper, enclosingFunction) |
| 435 : super(helper, enclosingFunction); | 740 : super(helper, enclosingFunction); |
| 436 | 741 |
| 437 FunctionNode rewrite() { | 742 FunctionNode rewrite() { |
| 438 var statements = <Statement>[]; | 743 var statements = <Statement>[]; |
| 439 | 744 |
| 440 // var :completer = new Completer.sync(); | 745 // var :completer = new Completer.sync(); |
| 441 completerVariable = new VariableDeclaration( | 746 completerVariable = new VariableDeclaration(":completer", |
| 442 ":completer", | 747 initializer: new StaticInvocation( |
| 443 initializer: new StaticInvocation(helper.completerConstructor, | 748 helper.completerConstructor, new Arguments([])), |
| 444 new Arguments([])), | |
| 445 isFinal: true); | 749 isFinal: true); |
| 446 statements.add(completerVariable); | 750 statements.add(completerVariable); |
| 447 | 751 |
| 448 super.setupAsyncContinuations(statements); | 752 super.setupAsyncContinuations(statements); |
| 449 | 753 |
| 450 // new Future.microtask(:async_op); | 754 // new Future.microtask(:async_op); |
| 451 var newMicrotaskStatement = new ExpressionStatement( | 755 var newMicrotaskStatement = new ExpressionStatement(new StaticInvocation( |
| 452 new StaticInvocation( | 756 helper.futureMicrotaskConstructor, |
| 453 helper.futureMicrotaskConstructor, | 757 new Arguments([new VariableGet(nestedClosureVariable)]))); |
| 454 new Arguments([new VariableGet(nestedClosureVariable)]))); | |
| 455 statements.add(newMicrotaskStatement); | 758 statements.add(newMicrotaskStatement); |
| 456 | 759 |
| 457 // return :completer.future; | 760 // return :completer.future; |
| 458 var completerGet = new VariableGet(completerVariable); | 761 var completerGet = new VariableGet(completerVariable); |
| 459 var returnStatement = new ReturnStatement( | 762 var returnStatement = new ReturnStatement( |
| 460 new PropertyGet(completerGet, new Name('future', helper.asyncLibrary))); | 763 new PropertyGet(completerGet, new Name('future', helper.asyncLibrary))); |
| 461 statements.add(returnStatement); | 764 statements.add(returnStatement); |
| 462 | 765 |
| 463 enclosingFunction.body = new Block(statements); | 766 enclosingFunction.body = new Block(statements); |
| 464 enclosingFunction.body.parent = enclosingFunction; | 767 enclosingFunction.body.parent = enclosingFunction; |
| 465 enclosingFunction.asyncMarker = AsyncMarker.Sync; | 768 enclosingFunction.asyncMarker = AsyncMarker.Sync; |
| 466 return enclosingFunction; | 769 return enclosingFunction; |
| 467 } | 770 } |
| 468 | 771 |
| 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) { | 772 Statement buildCatchBody(exceptionVariable, stackTraceVariable) { |
| 480 return new ExpressionStatement( | 773 return new ExpressionStatement(new MethodInvocation( |
| 481 new MethodInvocation( | 774 new VariableGet(completerVariable), |
| 482 new VariableGet(completerVariable), | 775 new Name("completeError", helper.asyncLibrary), |
| 483 new Name("completeError", helper.asyncLibrary), | 776 new Arguments([ |
| 484 new Arguments([new VariableGet(exceptionVariable), | 777 new VariableGet(exceptionVariable), |
| 485 new VariableGet(stackTraceVariable)]))); | 778 new VariableGet(stackTraceVariable) |
| 779 ]))); |
| 486 } | 780 } |
| 487 | 781 |
| 488 visitReturnStatement(ReturnStatement node) { | 782 visitReturnStatement(ReturnStatement node) { |
| 489 var transformedExpression; | 783 var expr; |
| 490 if (node.expression == null) { | 784 if (node.expression == null) { |
| 491 transformedExpression = new NullLiteral(); | 785 expr = new NullLiteral(); |
| 492 } else { | 786 } else { |
| 493 transformedExpression = expressionRewriter.rewrite(node.expression); | 787 expr = expressionRewriter.rewrite(node.expression, statements); |
| 494 } | 788 } |
| 495 | 789 |
| 496 // Note: transformed expression can't be used directly as part of the | 790 statements.add(new ExpressionStatement(new MethodInvocation( |
| 497 // method invocation because it might contain yield points and | 791 new VariableGet(completerVariable), |
| 498 // expression stack might not be empty. | 792 new Name("complete", helper.asyncLibrary), |
| 499 var resultVar = new VariableDeclaration(':async_temp', | 793 new Arguments([expr])))); |
| 500 initializer: transformedExpression); | 794 statements.add(new ReturnStatement(new NullLiteral())); |
| 501 var completeCompleter = new ExpressionStatement( | 795 return null; |
| 502 new MethodInvocation( | |
| 503 new VariableGet(completerVariable), | |
| 504 new Name("complete", helper.asyncLibrary), | |
| 505 new Arguments([new VariableGet(resultVar)]))); | |
| 506 var returnStatement = new ReturnStatement(new NullLiteral()); | |
| 507 return new Block([resultVar, completeCompleter, returnStatement]); | |
| 508 } | 796 } |
| 509 } | 797 } |
| 510 | 798 |
| 511 class HelperNodes { | 799 class HelperNodes { |
| 512 final Library asyncLibrary; | 800 final Library asyncLibrary; |
| 513 final Library coreLibrary; | 801 final Library coreLibrary; |
| 514 final Procedure printProcedure; | 802 final Procedure printProcedure; |
| 515 final Procedure completerConstructor; | 803 final Procedure completerConstructor; |
| 516 final Procedure futureMicrotaskConstructor; | 804 final Procedure futureMicrotaskConstructor; |
| 517 final Constructor streamControllerConstructor; | 805 final Constructor streamControllerConstructor; |
| 518 final Constructor syncIterableConstructor; | 806 final Constructor syncIterableConstructor; |
| 519 final Constructor streamIteratorConstructor; | 807 final Constructor streamIteratorConstructor; |
| 520 final Procedure asyncThenWrapper; | 808 final Procedure asyncThenWrapper; |
| 521 final Procedure asyncErrorWrapper; | 809 final Procedure asyncErrorWrapper; |
| 522 final Procedure awaitHelper; | 810 final Procedure awaitHelper; |
| 523 | 811 |
| 524 HelperNodes( | 812 HelperNodes( |
| 525 this.asyncLibrary, | 813 this.asyncLibrary, |
| 526 this.coreLibrary, | 814 this.coreLibrary, |
| 527 this.printProcedure, | 815 this.printProcedure, |
| 528 this.completerConstructor, | 816 this.completerConstructor, |
| 529 this.syncIterableConstructor, | 817 this.syncIterableConstructor, |
| 530 this.streamIteratorConstructor, | 818 this.streamIteratorConstructor, |
| 531 this.futureMicrotaskConstructor, | 819 this.futureMicrotaskConstructor, |
| 532 this.streamControllerConstructor, | 820 this.streamControllerConstructor, |
| 533 this.asyncThenWrapper, | 821 this.asyncThenWrapper, |
| 534 this.asyncErrorWrapper, | 822 this.asyncErrorWrapper, |
| 535 this.awaitHelper | 823 this.awaitHelper); |
| 536 ); | |
| 537 | 824 |
| 538 factory HelperNodes.fromProgram(Program program) { | 825 factory HelperNodes.fromProgram(Program program) { |
| 539 Library findLibrary(String name) { | 826 Library findLibrary(String name) { |
| 540 Uri uri = Uri.parse(name); | 827 Uri uri = Uri.parse(name); |
| 541 for (var library in program.libraries) { | 828 for (var library in program.libraries) { |
| 542 if (library.importUri == uri) return library; | 829 if (library.importUri == uri) return library; |
| 543 } | 830 } |
| 544 throw 'Library "$name" not found'; | 831 throw 'Library "$name" not found'; |
| 545 } | 832 } |
| 546 Class findClass(Library library, String name) { | 833 Class findClass(Library library, String name) { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 571 throw 'Procedure "$name" not found'; | 858 throw 'Procedure "$name" not found'; |
| 572 } | 859 } |
| 573 | 860 |
| 574 var asyncLibrary = findLibrary('dart:async'); | 861 var asyncLibrary = findLibrary('dart:async'); |
| 575 var coreLibrary = findLibrary('dart:core'); | 862 var coreLibrary = findLibrary('dart:core'); |
| 576 | 863 |
| 577 var completerClass = findClass(asyncLibrary, 'Completer'); | 864 var completerClass = findClass(asyncLibrary, 'Completer'); |
| 578 var futureClass = findClass(asyncLibrary, 'Future'); | 865 var futureClass = findClass(asyncLibrary, 'Future'); |
| 579 var streamIteratorClass = findClass(asyncLibrary, '_StreamIterator'); | 866 var streamIteratorClass = findClass(asyncLibrary, '_StreamIterator'); |
| 580 var syncIterableClass = findClass(coreLibrary, '_SyncIterable'); | 867 var syncIterableClass = findClass(coreLibrary, '_SyncIterable'); |
| 581 var streamControllerClass = findClass( | 868 var streamControllerClass = |
| 582 asyncLibrary, '_AsyncStarStreamController'); | 869 findClass(asyncLibrary, '_AsyncStarStreamController'); |
| 583 | 870 |
| 584 return new HelperNodes( | 871 return new HelperNodes( |
| 585 asyncLibrary, | 872 asyncLibrary, |
| 586 coreLibrary, | 873 coreLibrary, |
| 587 findProcedure(coreLibrary, 'print'), | 874 findProcedure(coreLibrary, 'print'), |
| 588 findFactoryConstructor(completerClass, 'sync'), | 875 findFactoryConstructor(completerClass, 'sync'), |
| 589 findConstructor(syncIterableClass, ''), | 876 findConstructor(syncIterableClass, ''), |
| 590 findConstructor(streamIteratorClass , ''), | 877 findConstructor(streamIteratorClass, ''), |
| 591 findFactoryConstructor(futureClass, 'microtask'), | 878 findFactoryConstructor(futureClass, 'microtask'), |
| 592 findConstructor(streamControllerClass, ''), | 879 findConstructor(streamControllerClass, ''), |
| 593 findProcedure(asyncLibrary, '_asyncThenWrapperHelper'), | 880 findProcedure(asyncLibrary, '_asyncThenWrapperHelper'), |
| 594 findProcedure(asyncLibrary, '_asyncErrorWrapperHelper'), | 881 findProcedure(asyncLibrary, '_asyncErrorWrapperHelper'), |
| 595 findProcedure(asyncLibrary, '_awaitHelper')); | 882 findProcedure(asyncLibrary, '_awaitHelper')); |
| 596 } | 883 } |
| 597 } | 884 } |
| OLD | NEW |