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 |