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

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

Issue 2712473003: closure conversion: Support closures in initializers (Closed)
Patch Set: Remove parameter Created 3 years, 10 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);
ahe 2017/02/27 07:36:06 I'm curious, is this manual formatting or dartfmt?
karlklose 2017/02/28 11:45:15 I formatted it manually but the formatter produces
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 'unexpeced node $parent';
ahe 2017/02/27 07:36:06 Consider rewriting this message to be a complete s
karlklose 2017/02/28 11:45:15 Done.
227 }
228 }
229 }
230 rewriter = null;
231 // Transform constructor body.
204 FunctionNode function = node.function; 232 FunctionNode function = node.function;
205 if (function.body != null && function.body is! EmptyStatement) { 233 if (function.body != null && function.body is! EmptyStatement) {
206 setupContextForFunctionBody(function); 234 setupContextForFunctionBody(function);
207 VariableDeclaration self = thisAccess[currentMemberFunction]; 235 VariableDeclaration self = thisAccess[currentMemberFunction];
208 // TODO(karlklose): transform initializers
209 if (self != null) { 236 if (self != null) {
210 context.extend(self, new ThisExpression()); 237 context.extend(self, new ThisExpression());
211 } 238 }
212 node.function.accept(this); 239 node.function.accept(this);
213 resetContext(); 240 resetContext();
214 } 241 }
242 return node;
243 }
215 244
216 return node; 245 AstRewriter makeRewriterForBody(FunctionNode function) {
246 Statement body = function.body;
247 if (body is! Block) {
248 body = new Block(<Statement>[body]);
249 function.body = function.body.parent = body;
250 }
251 return new BlockRewriter(body);
217 } 252 }
218 253
219 Expression handleLocalFunction(FunctionNode function) { 254 Expression handleLocalFunction(FunctionNode function) {
220 FunctionNode enclosingFunction = currentFunction; 255 FunctionNode enclosingFunction = currentFunction;
221 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution; 256 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution;
222 currentFunction = function; 257 currentFunction = function;
223 Statement body = function.body; 258 Statement body = function.body;
224 assert(body != null); 259 assert(body != null);
225 260
226 if (body is Block) { 261 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 262
234 VariableDeclaration contextVariable = new VariableDeclaration( 263 VariableDeclaration contextVariable = new VariableDeclaration(
235 "#contextParameter", 264 "#contextParameter",
236 type: contextClass.rawType, 265 type: contextClass.rawType,
237 isFinal: true); 266 isFinal: true);
238 Context parent = context; 267 Context parent = context;
239 context = context.toNestedContext(new VariableAccessor(contextVariable)); 268 context = context.toNestedContext(new VariableAccessor(contextVariable));
240 269
241 Set<TypeParameter> captured = capturedTypeVariables[currentFunction]; 270 Set<TypeParameter> captured = capturedTypeVariables[currentFunction];
242 if (captured != null) { 271 if (captured != null) {
(...skipping 25 matching lines...) Expand all
268 parent.update(node.variable, expression); 297 parent.update(node.variable, expression);
269 return null; 298 return null;
270 } else { 299 } else {
271 node.variable.initializer = expression; 300 node.variable.initializer = expression;
272 expression.parent = node.variable; 301 expression.parent = node.variable;
273 return node.variable; 302 return node.variable;
274 } 303 }
275 }); 304 });
276 } 305 }
277 306
278 TreeNode visitFunctionExpression(FunctionExpression node) => saveContext(() { 307 TreeNode visitFunctionExpression(FunctionExpression node) {
279 return handleLocalFunction(node.function); 308 return saveContext(() {
280 }); 309 return handleLocalFunction(node.function);
310 });
311 }
281 312
282 /// Add a new class to the current library that looks like this: 313 /// Add a new class to the current library that looks like this:
283 /// 314 ///
284 /// class Closure#0 extends core::Object implements core::Function { 315 /// class Closure#0 extends core::Object implements core::Function {
285 /// field _in::Context context; 316 /// field _in::Context context;
286 /// constructor •(final _in::Context #t1) → dynamic 317 /// constructor •(final _in::Context #t1) → dynamic
287 /// : self::Closure 0::context = #t1 318 /// : self::Closure 0::context = #t1
288 /// ; 319 /// ;
289 /// method call(/* The parameters of [function] */) → dynamic { 320 /// method call(/* The parameters of [function] */) → dynamic {
290 /// /// #t2 is [contextVariable]. 321 /// /// #t2 is [contextVariable].
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 } 418 }
388 419
389 return node; 420 return node;
390 } 421 }
391 422
392 void setupContextForFunctionBody(FunctionNode function) { 423 void setupContextForFunctionBody(FunctionNode function) {
393 Statement body = function.body; 424 Statement body = function.body;
394 assert(body != null); 425 assert(body != null);
395 currentMemberFunction = function; 426 currentMemberFunction = function;
396 // Ensure that the body is a block which becomes the current block. 427 // Ensure that the body is a block which becomes the current block.
397 if (body is Block) { 428 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 429 // Start with no context. This happens after setting up _currentBlock
405 // so statements can be emitted into _currentBlock if necessary. 430 // so statements can be emitted into _currentBlock if necessary.
406 context = new NoContext(this); 431 context = new NoContext(this);
407 } 432 }
408 433
409 void resetContext() { 434 void resetContext() {
410 _currentBlock = null; 435 rewriter = null;
411 _insertionIndex = 0;
412 context = null; 436 context = null;
413 currentMemberFunction = null; 437 currentMemberFunction = null;
414 currentMember = null; 438 currentMember = null;
415 } 439 }
416 440
417 bool get isEmptyContext { 441 bool get isEmptyContext {
418 return _currentBlock == null && _insertionIndex == 0 && context == null; 442 return rewriter == null && context == null;
419 } 443 }
420 444
421 TreeNode visitLocalInitializer(LocalInitializer node) { 445 TreeNode visitLocalInitializer(LocalInitializer node) {
422 assert(!capturedVariables.contains(node.variable)); 446 assert(!capturedVariables.contains(node.variable));
423 node.transformChildren(this); 447 node.transformChildren(this);
424 return node; 448 return node;
425 } 449 }
426 450
427 TreeNode visitFunctionNode(FunctionNode node) { 451 TreeNode visitFunctionNode(FunctionNode node) {
428 transformList(node.typeParameters, this, node); 452 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 453 // TODO: Can parameters contain initializers (e.g., for optional ones) that
435 // need to be closure converted? 454 // need to be closure converted?
436 node.positionalParameters.where(capturedVariables.contains).forEach(extend); 455 node.positionalParameters
437 node.namedParameters.where(capturedVariables.contains).forEach(extend); 456 .where(capturedVariables.contains)
438 457 .forEach(extendContextWith);
458 node.namedParameters
459 .where(capturedVariables.contains)
460 .forEach(extendContextWith);
439 assert(node.body != null); 461 assert(node.body != null);
440 node.body = node.body.accept(this); 462 node.body = node.body.accept(this);
441 node.body.parent = node; 463 node.body.parent = node;
442 return node; 464 return node;
443 } 465 }
444 466
445 TreeNode visitBlock(Block node) { 467 TreeNode visitBlock(Block node) {
446 return saveContext(() { 468 return saveContext(() {
447 if (_currentBlock != node) { 469 BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node);
448 _currentBlock = node; 470 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; 471 return node;
467 }); 472 });
468 } 473 }
469 474
470 TreeNode visitVariableDeclaration(VariableDeclaration node) { 475 TreeNode visitVariableDeclaration(VariableDeclaration node) {
471 node.transformChildren(this); 476 node.transformChildren(this);
472 477
473 if (!capturedVariables.contains(node)) return node; 478 if (!capturedVariables.contains(node)) return node;
474 context.extend(node, node.initializer ?? new NullLiteral()); 479 context.extend(node, node.initializer ?? new NullLiteral());
475 480
(...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 554 // `clone-context` is a place-holder that will later be replaced by an
550 // expression that clones the current closure context (see 555 // expression that clones the current closure context (see
551 // [visitInvalidExpression]). 556 // [visitInvalidExpression]).
552 return saveContext(() { 557 return saveContext(() {
553 context = context.toNestedContext(); 558 context = context.toNestedContext();
554 List<Statement> statements = <Statement>[]; 559 List<Statement> statements = <Statement>[];
555 statements.addAll(node.variables); 560 statements.addAll(node.variables);
556 statements.add(node); 561 statements.add(node);
557 node.variables.clear(); 562 node.variables.clear();
558 node.updates.insert(0, cloneContext()); 563 node.updates.insert(0, cloneContext());
559 _currentBlock = new Block(statements); 564 Block block = new Block(statements);
560 _insertionIndex = 0; 565 rewriter = new BlockRewriter(block);
561 return _currentBlock.accept(this); 566 return block.accept(this);
562 }); 567 });
563 } 568 }
564 return super.visitForStatement(node); 569 return super.visitForStatement(node);
565 } 570 }
566 571
567 TreeNode visitForInStatement(ForInStatement node) { 572 TreeNode visitForInStatement(ForInStatement node) {
568 if (capturedVariables.contains(node.variable)) { 573 if (capturedVariables.contains(node.variable)) {
569 // In Dart, loop variables are new variables on each iteration of the 574 // 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 575 // 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` 576 // 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 814 // TODO(ahe): Remove this method when we don't generate closure classes
810 // anymore. 815 // anymore.
811 void addClosureClassNote(Class closureClass) { 816 void addClosureClassNote(Class closureClass) {
812 closureClass.addMember(new Field(new Name("note"), 817 closureClass.addMember(new Field(new Name("note"),
813 type: coreTypes.stringClass.rawType, 818 type: coreTypes.stringClass.rawType,
814 initializer: new StringLiteral( 819 initializer: new StringLiteral(
815 "This is temporary. The VM doesn't need closure classes."), 820 "This is temporary. The VM doesn't need closure classes."),
816 fileUri: currentFileUri)); 821 fileUri: currentFileUri));
817 } 822 }
818 } 823 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698