Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: pkg/kernel/lib/transformations/closure/converter.dart

Issue 2712473003: closure conversion: Support closures in initializers (Closed)
Patch Set: Address comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/kernel/lib/transformations/closure/context.dart ('k') | pkg/kernel/lib/transformations/closure/rewriter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698