| 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.closure.converter; | 5 library kernel.transformations.closure.converter; |
| 6 | 6 |
| 7 import '../../ast.dart' | 7 import '../../ast.dart' |
| 8 show | 8 show |
| 9 Arguments, | 9 Arguments, |
| 10 Block, | 10 Block, |
| 11 Catch, | 11 Catch, |
| 12 Class, | 12 Class, |
| 13 Constructor, | 13 Constructor, |
| 14 ConstructorInvocation, | 14 ConstructorInvocation, |
| 15 DartType, | 15 DartType, |
| 16 EmptyStatement, | 16 EmptyStatement, |
| 17 Expression, | 17 Expression, |
| 18 ExpressionStatement, | 18 ExpressionStatement, |
| 19 Field, | 19 Field, |
| 20 FieldInitializer, | 20 FieldInitializer, |
| 21 ForInStatement, | 21 ForInStatement, |
| 22 ForStatement, | 22 ForStatement, |
| 23 FunctionDeclaration, | 23 FunctionDeclaration, |
| 24 FunctionExpression, | 24 FunctionExpression, |
| 25 FunctionNode, | 25 FunctionNode, |
| 26 InferredValue, | 26 InferredValue, |
| 27 Initializer, | 27 Initializer, |
| 28 InvalidExpression, | 28 InvalidExpression, |
| 29 InvocationExpression, | 29 InvocationExpression, |
| 30 Let, |
| 30 Library, | 31 Library, |
| 31 LocalInitializer, | 32 LocalInitializer, |
| 32 Member, | 33 Member, |
| 33 MethodInvocation, | 34 MethodInvocation, |
| 34 Name, | 35 Name, |
| 35 NamedExpression, | 36 NamedExpression, |
| 36 NullLiteral, | 37 NullLiteral, |
| 37 Procedure, | 38 Procedure, |
| 38 ProcedureKind, | 39 ProcedureKind, |
| 39 PropertyGet, | 40 PropertyGet, |
| (...skipping 20 matching lines...) Expand all Loading... |
| 60 import '../../core_types.dart' show CoreTypes; | 61 import '../../core_types.dart' show CoreTypes; |
| 61 | 62 |
| 62 import '../../type_algebra.dart' show substitute; | 63 import '../../type_algebra.dart' show substitute; |
| 63 | 64 |
| 64 import 'clone_without_body.dart' show CloneWithoutBody; | 65 import 'clone_without_body.dart' show CloneWithoutBody; |
| 65 | 66 |
| 66 import 'context.dart' show Context, NoContext; | 67 import 'context.dart' show Context, NoContext; |
| 67 | 68 |
| 68 import 'info.dart' show ClosureInfo; | 69 import 'info.dart' show ClosureInfo; |
| 69 | 70 |
| 71 import 'rewriter.dart' show AstRewriter, BlockRewriter, InitializerRewriter; |
| 72 |
| 70 class ClosureConverter extends Transformer { | 73 class ClosureConverter extends Transformer { |
| 71 final CoreTypes coreTypes; | 74 final CoreTypes coreTypes; |
| 72 final Class contextClass; | 75 final Class contextClass; |
| 73 final Set<VariableDeclaration> capturedVariables; | 76 final Set<VariableDeclaration> capturedVariables; |
| 74 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; | 77 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; |
| 75 final Map<FunctionNode, VariableDeclaration> thisAccess; | 78 final Map<FunctionNode, VariableDeclaration> thisAccess; |
| 76 final Map<FunctionNode, String> localNames; | 79 final Map<FunctionNode, String> localNames; |
| 77 | 80 |
| 78 /// Records place-holders for cloning contexts. See [visitForStatement]. | 81 /// Records place-holders for cloning contexts. See [visitForStatement]. |
| 79 final Set<InvalidExpression> contextClonePlaceHolders = | 82 final Set<InvalidExpression> contextClonePlaceHolders = |
| (...skipping 16 matching lines...) Expand all Loading... |
| 96 Library currentLibrary; | 99 Library currentLibrary; |
| 97 | 100 |
| 98 Class currentClass; | 101 Class currentClass; |
| 99 | 102 |
| 100 Member currentMember; | 103 Member currentMember; |
| 101 | 104 |
| 102 FunctionNode currentMemberFunction; | 105 FunctionNode currentMemberFunction; |
| 103 | 106 |
| 104 FunctionNode currentFunction; | 107 FunctionNode currentFunction; |
| 105 | 108 |
| 106 Block _currentBlock; | 109 Context context; |
| 107 | 110 |
| 108 int _insertionIndex = 0; | 111 AstRewriter rewriter; |
| 109 | |
| 110 Context context; | |
| 111 | 112 |
| 112 /// Maps original type variable (aka type parameter) to a hoisted type | 113 /// Maps original type variable (aka type parameter) to a hoisted type |
| 113 /// variable type. | 114 /// variable type. |
| 114 /// | 115 /// |
| 115 /// For example, consider: | 116 /// For example, consider: |
| 116 /// | 117 /// |
| 117 /// class C<T> { | 118 /// class C<T> { |
| 118 /// f() => (x) => x is T; | 119 /// f() => (x) => x is T; |
| 119 /// } | 120 /// } |
| 120 /// | 121 /// |
| (...skipping 22 matching lines...) Expand all Loading... |
| 143 return currentFunction == null || currentMemberFunction == currentFunction; | 144 return currentFunction == null || currentMemberFunction == currentFunction; |
| 144 } | 145 } |
| 145 | 146 |
| 146 String get currentFileUri { | 147 String get currentFileUri { |
| 147 if (currentMember is Constructor) return currentClass.fileUri; | 148 if (currentMember is Constructor) return currentClass.fileUri; |
| 148 if (currentMember is Field) return (currentMember as Field).fileUri; | 149 if (currentMember is Field) return (currentMember as Field).fileUri; |
| 149 if (currentMember is Procedure) return (currentMember as Procedure).fileUri; | 150 if (currentMember is Procedure) return (currentMember as Procedure).fileUri; |
| 150 throw "No file uri for ${currentMember.runtimeType}"; | 151 throw "No file uri for ${currentMember.runtimeType}"; |
| 151 } | 152 } |
| 152 | 153 |
| 153 void insert(Statement statement) { | |
| 154 _currentBlock.statements.insert(_insertionIndex++, statement); | |
| 155 statement.parent = _currentBlock; | |
| 156 } | |
| 157 | |
| 158 TreeNode saveContext(TreeNode f()) { | 154 TreeNode saveContext(TreeNode f()) { |
| 159 Block savedBlock = _currentBlock; | 155 AstRewriter old = rewriter; |
| 160 int savedIndex = _insertionIndex; | |
| 161 Context savedContext = context; | 156 Context savedContext = context; |
| 162 try { | 157 try { |
| 163 return f(); | 158 return f(); |
| 164 } finally { | 159 } finally { |
| 165 _currentBlock = savedBlock; | 160 rewriter = old; |
| 166 _insertionIndex = savedIndex; | |
| 167 context = savedContext; | 161 context = savedContext; |
| 168 } | 162 } |
| 169 } | 163 } |
| 170 | 164 |
| 171 TreeNode visitLibrary(Library node) { | 165 TreeNode visitLibrary(Library node) { |
| 172 assert(newLibraryMembers.isEmpty); | 166 assert(newLibraryMembers.isEmpty); |
| 173 if (node == contextClass.enclosingLibrary) return node; | 167 if (node == contextClass.enclosingLibrary) return node; |
| 174 | 168 |
| 175 currentLibrary = node; | 169 currentLibrary = node; |
| 176 node = super.visitLibrary(node); | 170 node = super.visitLibrary(node); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 189 TreeNode visitClass(Class node) { | 183 TreeNode visitClass(Class node) { |
| 190 assert(newClassMembers.isEmpty); | 184 assert(newClassMembers.isEmpty); |
| 191 currentClass = node; | 185 currentClass = node; |
| 192 node = super.visitClass(node); | 186 node = super.visitClass(node); |
| 193 newClassMembers.forEach(node.addMember); | 187 newClassMembers.forEach(node.addMember); |
| 194 newClassMembers.clear(); | 188 newClassMembers.clear(); |
| 195 currentClass = null; | 189 currentClass = null; |
| 196 return node; | 190 return node; |
| 197 } | 191 } |
| 198 | 192 |
| 193 void extendContextWith(VariableDeclaration parameter) { |
| 194 context.extend(parameter, new VariableGet(parameter)); |
| 195 } |
| 196 |
| 199 TreeNode visitConstructor(Constructor node) { | 197 TreeNode visitConstructor(Constructor node) { |
| 200 assert(isEmptyContext); | 198 assert(isEmptyContext); |
| 201 | |
| 202 currentMember = node; | 199 currentMember = node; |
| 203 | 200 // Transform initializers. |
| 201 for (Initializer initializer in node.initializers) { |
| 202 if (initializer is FieldInitializer) { |
| 203 // Create a rewriter and a context for the initializer expression. |
| 204 rewriter = new InitializerRewriter(initializer.value); |
| 205 context = new NoContext(this); |
| 206 // Save the expression to visit it in the extended context, since the |
| 207 // rewriter will modify `initializer.value`. |
| 208 Expression initializerExpression = initializer.value; |
| 209 // Extend the context with all captured parameters of the constructor. |
| 210 // TODO(karlklose): add a fine-grained analysis of captured parameters. |
| 211 node.function.positionalParameters |
| 212 .where(capturedVariables.contains) |
| 213 .forEach(extendContextWith); |
| 214 node.function.namedParameters |
| 215 .where(capturedVariables.contains) |
| 216 .forEach(extendContextWith); |
| 217 // Transform the initializer expression. |
| 218 var parent = initializerExpression.parent; |
| 219 initializerExpression = initializerExpression.accept(this); |
| 220 initializerExpression.parent = parent; |
| 221 if (parent is Let) { |
| 222 parent.body = initializerExpression; |
| 223 } else if (parent is FieldInitializer) { |
| 224 parent.value = initializerExpression; |
| 225 } else { |
| 226 throw "Found unexpected node '${node.runtimeType}, expected a 'Let' " |
| 227 "or a 'FieldInitializer'."; |
| 228 } |
| 229 } |
| 230 } |
| 231 rewriter = null; |
| 232 // Transform constructor body. |
| 204 FunctionNode function = node.function; | 233 FunctionNode function = node.function; |
| 205 if (function.body != null && function.body is! EmptyStatement) { | 234 if (function.body != null && function.body is! EmptyStatement) { |
| 206 setupContextForFunctionBody(function); | 235 setupContextForFunctionBody(function); |
| 207 VariableDeclaration self = thisAccess[currentMemberFunction]; | 236 VariableDeclaration self = thisAccess[currentMemberFunction]; |
| 208 // TODO(karlklose): transform initializers | |
| 209 if (self != null) { | 237 if (self != null) { |
| 210 context.extend(self, new ThisExpression()); | 238 context.extend(self, new ThisExpression()); |
| 211 } | 239 } |
| 212 node.function.accept(this); | 240 node.function.accept(this); |
| 213 resetContext(); | 241 resetContext(); |
| 214 } | 242 } |
| 243 return node; |
| 244 } |
| 215 | 245 |
| 216 return node; | 246 AstRewriter makeRewriterForBody(FunctionNode function) { |
| 247 Statement body = function.body; |
| 248 if (body is! Block) { |
| 249 body = new Block(<Statement>[body]); |
| 250 function.body = function.body.parent = body; |
| 251 } |
| 252 return new BlockRewriter(body); |
| 217 } | 253 } |
| 218 | 254 |
| 219 Expression handleLocalFunction(FunctionNode function) { | 255 Expression handleLocalFunction(FunctionNode function) { |
| 220 FunctionNode enclosingFunction = currentFunction; | 256 FunctionNode enclosingFunction = currentFunction; |
| 221 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution; | 257 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution; |
| 222 currentFunction = function; | 258 currentFunction = function; |
| 223 Statement body = function.body; | 259 Statement body = function.body; |
| 224 assert(body != null); | 260 assert(body != null); |
| 225 | 261 |
| 226 if (body is Block) { | 262 rewriter = makeRewriterForBody(function); |
| 227 _currentBlock = body; | |
| 228 } else { | |
| 229 _currentBlock = new Block(<Statement>[body]); | |
| 230 function.body = body.parent = _currentBlock; | |
| 231 } | |
| 232 _insertionIndex = 0; | |
| 233 | 263 |
| 234 VariableDeclaration contextVariable = new VariableDeclaration( | 264 VariableDeclaration contextVariable = new VariableDeclaration( |
| 235 "#contextParameter", | 265 "#contextParameter", |
| 236 type: contextClass.rawType, | 266 type: contextClass.rawType, |
| 237 isFinal: true); | 267 isFinal: true); |
| 238 Context parent = context; | 268 Context parent = context; |
| 239 context = context.toNestedContext(new VariableAccessor(contextVariable)); | 269 context = context.toNestedContext(new VariableAccessor(contextVariable)); |
| 240 | 270 |
| 241 Set<TypeParameter> captured = capturedTypeVariables[currentFunction]; | 271 Set<TypeParameter> captured = capturedTypeVariables[currentFunction]; |
| 242 if (captured != null) { | 272 if (captured != null) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 268 parent.update(node.variable, expression); | 298 parent.update(node.variable, expression); |
| 269 return null; | 299 return null; |
| 270 } else { | 300 } else { |
| 271 node.variable.initializer = expression; | 301 node.variable.initializer = expression; |
| 272 expression.parent = node.variable; | 302 expression.parent = node.variable; |
| 273 return node.variable; | 303 return node.variable; |
| 274 } | 304 } |
| 275 }); | 305 }); |
| 276 } | 306 } |
| 277 | 307 |
| 278 TreeNode visitFunctionExpression(FunctionExpression node) => saveContext(() { | 308 TreeNode visitFunctionExpression(FunctionExpression node) { |
| 279 return handleLocalFunction(node.function); | 309 return saveContext(() { |
| 280 }); | 310 return handleLocalFunction(node.function); |
| 311 }); |
| 312 } |
| 281 | 313 |
| 282 /// Add a new class to the current library that looks like this: | 314 /// Add a new class to the current library that looks like this: |
| 283 /// | 315 /// |
| 284 /// class Closure#0 extends core::Object implements core::Function { | 316 /// class Closure#0 extends core::Object implements core::Function { |
| 285 /// field _in::Context context; | 317 /// field _in::Context context; |
| 286 /// constructor •(final _in::Context #t1) → dynamic | 318 /// constructor •(final _in::Context #t1) → dynamic |
| 287 /// : self::Closure 0::context = #t1 | 319 /// : self::Closure 0::context = #t1 |
| 288 /// ; | 320 /// ; |
| 289 /// method call(/* The parameters of [function] */) → dynamic { | 321 /// method call(/* The parameters of [function] */) → dynamic { |
| 290 /// /// #t2 is [contextVariable]. | 322 /// /// #t2 is [contextVariable]. |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 387 } | 419 } |
| 388 | 420 |
| 389 return node; | 421 return node; |
| 390 } | 422 } |
| 391 | 423 |
| 392 void setupContextForFunctionBody(FunctionNode function) { | 424 void setupContextForFunctionBody(FunctionNode function) { |
| 393 Statement body = function.body; | 425 Statement body = function.body; |
| 394 assert(body != null); | 426 assert(body != null); |
| 395 currentMemberFunction = function; | 427 currentMemberFunction = function; |
| 396 // Ensure that the body is a block which becomes the current block. | 428 // Ensure that the body is a block which becomes the current block. |
| 397 if (body is Block) { | 429 rewriter = makeRewriterForBody(function); |
| 398 _currentBlock = body; | |
| 399 } else { | |
| 400 _currentBlock = new Block(<Statement>[body]); | |
| 401 function.body = body.parent = _currentBlock; | |
| 402 } | |
| 403 _insertionIndex = 0; | |
| 404 // Start with no context. This happens after setting up _currentBlock | 430 // Start with no context. This happens after setting up _currentBlock |
| 405 // so statements can be emitted into _currentBlock if necessary. | 431 // so statements can be emitted into _currentBlock if necessary. |
| 406 context = new NoContext(this); | 432 context = new NoContext(this); |
| 407 } | 433 } |
| 408 | 434 |
| 409 void resetContext() { | 435 void resetContext() { |
| 410 _currentBlock = null; | 436 rewriter = null; |
| 411 _insertionIndex = 0; | |
| 412 context = null; | 437 context = null; |
| 413 currentMemberFunction = null; | 438 currentMemberFunction = null; |
| 414 currentMember = null; | 439 currentMember = null; |
| 415 } | 440 } |
| 416 | 441 |
| 417 bool get isEmptyContext { | 442 bool get isEmptyContext { |
| 418 return _currentBlock == null && _insertionIndex == 0 && context == null; | 443 return rewriter == null && context == null; |
| 419 } | 444 } |
| 420 | 445 |
| 421 TreeNode visitLocalInitializer(LocalInitializer node) { | 446 TreeNode visitLocalInitializer(LocalInitializer node) { |
| 422 assert(!capturedVariables.contains(node.variable)); | 447 assert(!capturedVariables.contains(node.variable)); |
| 423 node.transformChildren(this); | 448 node.transformChildren(this); |
| 424 return node; | 449 return node; |
| 425 } | 450 } |
| 426 | 451 |
| 427 TreeNode visitFunctionNode(FunctionNode node) { | 452 TreeNode visitFunctionNode(FunctionNode node) { |
| 428 transformList(node.typeParameters, this, node); | 453 transformList(node.typeParameters, this, node); |
| 429 | |
| 430 void extend(VariableDeclaration parameter) { | |
| 431 context.extend(parameter, new VariableGet(parameter)); | |
| 432 } | |
| 433 | |
| 434 // TODO: Can parameters contain initializers (e.g., for optional ones) that | 454 // TODO: Can parameters contain initializers (e.g., for optional ones) that |
| 435 // need to be closure converted? | 455 // need to be closure converted? |
| 436 node.positionalParameters.where(capturedVariables.contains).forEach(extend); | 456 node.positionalParameters |
| 437 node.namedParameters.where(capturedVariables.contains).forEach(extend); | 457 .where(capturedVariables.contains) |
| 438 | 458 .forEach(extendContextWith); |
| 459 node.namedParameters |
| 460 .where(capturedVariables.contains) |
| 461 .forEach(extendContextWith); |
| 439 assert(node.body != null); | 462 assert(node.body != null); |
| 440 node.body = node.body.accept(this); | 463 node.body = node.body.accept(this); |
| 441 node.body.parent = node; | 464 node.body.parent = node; |
| 442 return node; | 465 return node; |
| 443 } | 466 } |
| 444 | 467 |
| 445 TreeNode visitBlock(Block node) { | 468 TreeNode visitBlock(Block node) { |
| 446 return saveContext(() { | 469 return saveContext(() { |
| 447 if (_currentBlock != node) { | 470 BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node); |
| 448 _currentBlock = node; | 471 blockRewriter.transformStatements(node, this); |
| 449 _insertionIndex = 0; | |
| 450 } | |
| 451 | |
| 452 while (_insertionIndex < _currentBlock.statements.length) { | |
| 453 assert(_currentBlock == node); | |
| 454 | |
| 455 var original = _currentBlock.statements[_insertionIndex]; | |
| 456 var transformed = original.accept(this); | |
| 457 assert(_currentBlock.statements[_insertionIndex] == original); | |
| 458 if (transformed == null) { | |
| 459 _currentBlock.statements.removeAt(_insertionIndex); | |
| 460 } else { | |
| 461 _currentBlock.statements[_insertionIndex++] = transformed; | |
| 462 transformed.parent = _currentBlock; | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 return node; | 472 return node; |
| 467 }); | 473 }); |
| 468 } | 474 } |
| 469 | 475 |
| 470 TreeNode visitVariableDeclaration(VariableDeclaration node) { | 476 TreeNode visitVariableDeclaration(VariableDeclaration node) { |
| 471 node.transformChildren(this); | 477 node.transformChildren(this); |
| 472 | 478 |
| 473 if (!capturedVariables.contains(node)) return node; | 479 if (!capturedVariables.contains(node)) return node; |
| 474 context.extend(node, node.initializer ?? new NullLiteral()); | 480 context.extend(node, node.initializer ?? new NullLiteral()); |
| 475 | 481 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 549 // `clone-context` is a place-holder that will later be replaced by an | 555 // `clone-context` is a place-holder that will later be replaced by an |
| 550 // expression that clones the current closure context (see | 556 // expression that clones the current closure context (see |
| 551 // [visitInvalidExpression]). | 557 // [visitInvalidExpression]). |
| 552 return saveContext(() { | 558 return saveContext(() { |
| 553 context = context.toNestedContext(); | 559 context = context.toNestedContext(); |
| 554 List<Statement> statements = <Statement>[]; | 560 List<Statement> statements = <Statement>[]; |
| 555 statements.addAll(node.variables); | 561 statements.addAll(node.variables); |
| 556 statements.add(node); | 562 statements.add(node); |
| 557 node.variables.clear(); | 563 node.variables.clear(); |
| 558 node.updates.insert(0, cloneContext()); | 564 node.updates.insert(0, cloneContext()); |
| 559 _currentBlock = new Block(statements); | 565 Block block = new Block(statements); |
| 560 _insertionIndex = 0; | 566 rewriter = new BlockRewriter(block); |
| 561 return _currentBlock.accept(this); | 567 return block.accept(this); |
| 562 }); | 568 }); |
| 563 } | 569 } |
| 564 return super.visitForStatement(node); | 570 return super.visitForStatement(node); |
| 565 } | 571 } |
| 566 | 572 |
| 567 TreeNode visitForInStatement(ForInStatement node) { | 573 TreeNode visitForInStatement(ForInStatement node) { |
| 568 if (capturedVariables.contains(node.variable)) { | 574 if (capturedVariables.contains(node.variable)) { |
| 569 // In Dart, loop variables are new variables on each iteration of the | 575 // In Dart, loop variables are new variables on each iteration of the |
| 570 // loop. This is only observable when the loop variable is captured by a | 576 // loop. This is only observable when the loop variable is captured by a |
| 571 // closure, so we need to transform the for-in loop when `node.variable` | 577 // closure, so we need to transform the for-in loop when `node.variable` |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 809 // TODO(ahe): Remove this method when we don't generate closure classes | 815 // TODO(ahe): Remove this method when we don't generate closure classes |
| 810 // anymore. | 816 // anymore. |
| 811 void addClosureClassNote(Class closureClass) { | 817 void addClosureClassNote(Class closureClass) { |
| 812 closureClass.addMember(new Field(new Name("note"), | 818 closureClass.addMember(new Field(new Name("note"), |
| 813 type: coreTypes.stringClass.rawType, | 819 type: coreTypes.stringClass.rawType, |
| 814 initializer: new StringLiteral( | 820 initializer: new StringLiteral( |
| 815 "This is temporary. The VM doesn't need closure classes."), | 821 "This is temporary. The VM doesn't need closure classes."), |
| 816 fileUri: currentFileUri)); | 822 fileUri: currentFileUri)); |
| 817 } | 823 } |
| 818 } | 824 } |
| OLD | NEW |