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

Side by Side Diff: lib/compiler/implementation/closure.dart

Issue 10913133: Allow closures inside lazy initializers. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add test. Created 8 years, 2 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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("closureToClassMapper"); 5 #library("closureToClassMapper");
6 6
7 #import("elements/elements.dart"); 7 #import("elements/elements.dart");
8 #import("leg.dart"); 8 #import("leg.dart");
9 #import("scanner/scannerlib.dart"); 9 #import("scanner/scannerlib.dart");
10 #import("tree/tree.dart"); 10 #import("tree/tree.dart");
11 #import("util/util.dart"); 11 #import("util/util.dart");
12 12
13 class ClosureTask extends CompilerTask { 13 class ClosureTask extends CompilerTask {
14 Map<Node, ClosureClassMap> closureMappingCache; 14 Map<Node, ClosureClassMap> closureMappingCache;
15 ClosureTask(Compiler compiler) 15 ClosureTask(Compiler compiler)
16 : closureMappingCache = new Map<Node, ClosureClassMap>(), 16 : closureMappingCache = new Map<Node, ClosureClassMap>(),
17 super(compiler); 17 super(compiler);
18 18
19 String get name => "Closure Simplifier"; 19 String get name => "Closure Simplifier";
20 20
21 ClosureClassMap computeClosureToClassMapping(FunctionExpression node, 21 ClosureClassMap computeClosureToClassMapping(Element element,
22 Expression node,
22 TreeElements elements) { 23 TreeElements elements) {
23 return measure(() { 24 return measure(() {
24 ClosureClassMap cached = closureMappingCache[node]; 25 ClosureClassMap cached = closureMappingCache[node];
25 if (cached !== null) return cached; 26 if (cached !== null) return cached;
26 27
27 ClosureTranslator translator = 28 ClosureTranslator translator =
28 new ClosureTranslator(compiler, elements, closureMappingCache); 29 new ClosureTranslator(compiler, elements, closureMappingCache);
30
29 // The translator will store the computed closure-mappings inside the 31 // The translator will store the computed closure-mappings inside the
30 // cache. One for given method and one for each nested closure. 32 // cache. One for given node and one for each nested closure.
31 translator.translate(node); 33 if (node is FunctionExpression) {
34 translator.translateFunction(element, node);
35 } else {
36 // Must be the lazy initializer of a static.
37 assert(node is SendSet);
38 translator.translateLazyInitializer(element, node);
39 }
32 assert(closureMappingCache[node] != null); 40 assert(closureMappingCache[node] != null);
33 return closureMappingCache[node]; 41 return closureMappingCache[node];
34 }); 42 });
35 } 43 }
36 44
37 ClosureClassMap getMappingForNestedFunction(FunctionExpression node) { 45 ClosureClassMap getMappingForNestedFunction(FunctionExpression node) {
38 return measure(() { 46 return measure(() {
39 ClosureClassMap nestedClosureData = closureMappingCache[node]; 47 ClosureClassMap nestedClosureData = closureMappingCache[node];
40 if (nestedClosureData === null) { 48 if (nestedClosureData === null) {
41 // TODO(floitsch): we can only assume that the reason for not having a 49 // TODO(floitsch): we can only assume that the reason for not having a
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
106 List<Element> boxedLoopVariables; 114 List<Element> boxedLoopVariables;
107 115
108 ClosureScope(this.boxElement, this.capturedVariableMapping) 116 ClosureScope(this.boxElement, this.capturedVariableMapping)
109 : boxedLoopVariables = const <Element>[]; 117 : boxedLoopVariables = const <Element>[];
110 118
111 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty(); 119 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty();
112 } 120 }
113 121
114 class ClosureClassMap { 122 class ClosureClassMap {
115 // The closure's element before any translation. Will be null for methods. 123 // The closure's element before any translation. Will be null for methods.
116 final FunctionElement closureElement; 124 final Element closureElement;
117 // The closureClassElement will be null for methods that are not local 125 // The closureClassElement will be null for methods that are not local
118 // closures. 126 // closures.
119 final ClassElement closureClassElement; 127 final ClassElement closureClassElement;
120 // The callElement will be null for methods that are not local closures. 128 // The callElement will be null for methods that are not local closures.
121 final FunctionElement callElement; 129 final FunctionElement callElement;
122 // The [thisElement] makes handling 'this' easier by treating it like any 130 // The [thisElement] makes handling 'this' easier by treating it like any
123 // other argument. It is only set for instance-members. 131 // other argument. It is only set for instance-members.
124 final ThisElement thisElement; 132 final ThisElement thisElement;
125 133
126 // Maps free locals, arguments and function elements to their captured 134 // Maps free locals, arguments and function elements to their captured
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 final TreeElements elements; 171 final TreeElements elements;
164 int closureFieldCounter = 0; 172 int closureFieldCounter = 0;
165 bool inTryStatement = false; 173 bool inTryStatement = false;
166 final Map<Node, ClosureClassMap> closureMappingCache; 174 final Map<Node, ClosureClassMap> closureMappingCache;
167 175
168 // Map of captured variables. Initially they will map to themselves. If 176 // Map of captured variables. Initially they will map to themselves. If
169 // a variable needs to be boxed then the scope declaring the variable 177 // a variable needs to be boxed then the scope declaring the variable
170 // will update this mapping. 178 // will update this mapping.
171 Map<Element, Element> capturedVariableMapping; 179 Map<Element, Element> capturedVariableMapping;
172 // List of encountered closures. 180 // List of encountered closures.
173 List<FunctionExpression> closures; 181 List<Expression> closures;
174 182
175 // The variables that have been declared in the current scope. 183 // The variables that have been declared in the current scope.
176 List<Element> scopeVariables; 184 List<Element> scopeVariables;
177 185
178 // Keep track of the mutated variables so that we don't need to box 186 // Keep track of the mutated variables so that we don't need to box
179 // non-mutated variables. 187 // non-mutated variables.
180 Set<Element> mutatedVariables; 188 Set<Element> mutatedVariables;
181 189
182 FunctionElement outermostFunctionElement; 190 Element outermostElement;
183 FunctionElement currentFunctionElement; 191 Element currentElement;
184 192
185 // The closureData of the currentFunctionElement. 193 // The closureData of the currentFunctionElement.
186 ClosureClassMap closureData; 194 ClosureClassMap closureData;
187 195
188 bool insideClosure = false; 196 bool insideClosure = false;
189 197
190 ClosureTranslator(this.compiler, this.elements, this.closureMappingCache) 198 ClosureTranslator(this.compiler, this.elements, this.closureMappingCache)
191 : capturedVariableMapping = new Map<Element, Element>(), 199 : capturedVariableMapping = new Map<Element, Element>(),
192 closures = <FunctionExpression>[], 200 closures = <Expression>[],
193 mutatedVariables = new Set<Element>(); 201 mutatedVariables = new Set<Element>();
194 202
195 void translate(Node node) { 203 void translateFunction(Element element, FunctionExpression node) {
196 visit(node); 204 // For constructors the [element] and the [:elements[node]:] may differ.
205 // The [:elements[node]:] always points to the generative-constructor
206 // element, whereas the [element] might be the constructor-body element.
207 visit(node); // [visitFunctionExpression] will call [visitInvokable].
197 // When variables need to be boxed their [capturedVariableMapping] is 208 // When variables need to be boxed their [capturedVariableMapping] is
198 // updated, but we delay updating the similar freeVariableMapping in the 209 // updated, but we delay updating the similar freeVariableMapping in the
199 // closure datas that capture these variables. 210 // closure datas that capture these variables.
200 // The closures don't have their fields (in the closure class) set, either. 211 // The closures don't have their fields (in the closure class) set, either.
201 updateClosures(); 212 updateClosures();
202 } 213 }
203 214
215 void translateLazyInitializer(Element element, SendSet node) {
216 assert(node.assignmentOperator.source == const SourceString("="));
217 Expression initialValue = node.argumentsNode.nodes.head;
218 visitInvokable(element, node, () { visit(initialValue); });
219 updateClosures();
220 }
221
204 // This function runs through all of the existing closures and updates their 222 // This function runs through all of the existing closures and updates their
205 // free variables to the boxed value. It also adds the field-elements to the 223 // free variables to the boxed value. It also adds the field-elements to the
206 // class representing the closure. At the same time it fills the 224 // class representing the closure. At the same time it fills the
207 // [capturedFieldMapping]. 225 // [capturedFieldMapping].
208 void updateClosures() { 226 void updateClosures() {
209 for (FunctionExpression closure in closures) { 227 for (Expression closure in closures) {
210 // The captured variables that need to be stored in a field of the closure 228 // The captured variables that need to be stored in a field of the closure
211 // class. 229 // class.
212 Set<Element> fieldCaptures = new Set<Element>(); 230 Set<Element> fieldCaptures = new Set<Element>();
213 ClosureClassMap data = closureMappingCache[closure]; 231 ClosureClassMap data = closureMappingCache[closure];
214 Map<Element, Element> freeVariableMapping = data.freeVariableMapping; 232 Map<Element, Element> freeVariableMapping = data.freeVariableMapping;
215 // We get a copy of the keys and iterate over it, to avoid modifications 233 // We get a copy of the keys and iterate over it, to avoid modifications
216 // to the map while iterating over it. 234 // to the map while iterating over it.
217 freeVariableMapping.getKeys().forEach((Element fromElement) { 235 freeVariableMapping.getKeys().forEach((Element fromElement) {
218 assert(fromElement == freeVariableMapping[fromElement]); 236 assert(fromElement == freeVariableMapping[fromElement]);
219 Element updatedElement = capturedVariableMapping[fromElement]; 237 Element updatedElement = capturedVariableMapping[fromElement];
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
253 } 271 }
254 272
255 void useLocal(Element element) { 273 void useLocal(Element element) {
256 // If the element is not declared in the current function and the element 274 // If the element is not declared in the current function and the element
257 // is not the closure itself we need to mark the element as free variable. 275 // is not the closure itself we need to mark the element as free variable.
258 // Note that the check on [insideClosure] is not just an 276 // Note that the check on [insideClosure] is not just an
259 // optimization: factories have type parameters as function 277 // optimization: factories have type parameters as function
260 // parameters, and type parameters are declared in the class, not 278 // parameters, and type parameters are declared in the class, not
261 // the factory. 279 // the factory.
262 if (insideClosure && 280 if (insideClosure &&
263 element.enclosingElement != currentFunctionElement && 281 element.enclosingElement != currentElement &&
264 element != currentFunctionElement) { 282 element != currentElement) {
265 assert(closureData.freeVariableMapping[element] == null || 283 assert(closureData.freeVariableMapping[element] == null ||
266 closureData.freeVariableMapping[element] == element); 284 closureData.freeVariableMapping[element] == element);
267 closureData.freeVariableMapping[element] = element; 285 closureData.freeVariableMapping[element] = element;
268 } else if (inTryStatement) { 286 } else if (inTryStatement) {
269 // Don't mark the this-element. This would complicate things in the 287 // Don't mark the this-element. This would complicate things in the
270 // builder. 288 // builder.
271 if (element != closureData.thisElement) { 289 if (element != closureData.thisElement) {
272 // TODO(ngeoffray): only do this if the variable is mutated. 290 // TODO(ngeoffray): only do this if the variable is mutated.
273 closureData.usedVariablesInTry.add(element); 291 closureData.usedVariablesInTry.add(element);
274 } 292 }
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 void analyzeTypeVariables(DartType type) { 384 void analyzeTypeVariables(DartType type) {
367 if (type is TypeVariableType) { 385 if (type is TypeVariableType) {
368 useLocal(type.element); 386 useLocal(type.element);
369 } else if (type is InterfaceType) { 387 } else if (type is InterfaceType) {
370 InterfaceType ifcType = type; 388 InterfaceType ifcType = type;
371 for (DartType argument in ifcType.arguments) { 389 for (DartType argument in ifcType.arguments) {
372 analyzeTypeVariables(argument); 390 analyzeTypeVariables(argument);
373 } 391 }
374 } 392 }
375 } 393 }
376 if (outermostFunctionElement.isMember() && 394 if (outermostElement.isMember() &&
377 compiler.world.needsRti(outermostFunctionElement.getEnclosingClass())) { 395 compiler.world.needsRti(outermostElement.getEnclosingClass())) {
378 if (outermostFunctionElement.isInstanceMember() 396 if (outermostElement.isInstanceMember()
379 || outermostFunctionElement.isGenerativeConstructor()) { 397 || outermostElement.isGenerativeConstructor()) {
380 if (hasTypeVariable(type)) useLocal(closureData.thisElement); 398 if (hasTypeVariable(type)) useLocal(closureData.thisElement);
381 } else if (outermostFunctionElement.isFactoryConstructor()) { 399 } else if (outermostElement.isFactoryConstructor()) {
382 analyzeTypeVariables(type); 400 analyzeTypeVariables(type);
383 } 401 }
384 } 402 }
385 403
386 node.visitChildren(this); 404 node.visitChildren(this);
387 } 405 }
388 406
389 // If variables that are declared in the [node] scope are captured and need 407 // If variables that are declared in the [node] scope are captured and need
390 // to be boxed create a box-element and update the [capturingScopes] in the 408 // to be boxed create a box-element and update the [capturingScopes] in the
391 // current [closureData]. 409 // current [closureData].
392 // The boxed variables are updated in the [capturedVariableMapping]. 410 // The boxed variables are updated in the [capturedVariableMapping].
393 void attachCapturedScopeVariables(Node node) { 411 void attachCapturedScopeVariables(Node node) {
394 Element box = null; 412 Element box = null;
395 Map<Element, Element> scopeMapping = new Map<Element, Element>(); 413 Map<Element, Element> scopeMapping = new Map<Element, Element>();
396 for (Element element in scopeVariables) { 414 for (Element element in scopeVariables) {
397 // No need to box non-assignable elements. 415 // No need to box non-assignable elements.
398 if (!element.isAssignable()) continue; 416 if (!element.isAssignable()) continue;
399 if (!mutatedVariables.contains(element)) continue; 417 if (!mutatedVariables.contains(element)) continue;
400 if (capturedVariableMapping.containsKey(element)) { 418 if (capturedVariableMapping.containsKey(element)) {
401 if (box == null) { 419 if (box == null) {
402 // TODO(floitsch): construct better box names. 420 // TODO(floitsch): construct better box names.
403 SourceString boxName = 421 SourceString boxName =
404 new SourceString("box_${closureFieldCounter++}"); 422 new SourceString("box_${closureFieldCounter++}");
405 box = new BoxElement(boxName, currentFunctionElement); 423 box = new BoxElement(boxName, currentElement);
406 } 424 }
407 // TODO(floitsch): construct better boxed names. 425 // TODO(floitsch): construct better boxed names.
408 String elementName = element.name.slowToString(); 426 String elementName = element.name.slowToString();
409 // We are currently using the name in an HForeign which could replace 427 // We are currently using the name in an HForeign which could replace
410 // "$X" with something else. 428 // "$X" with something else.
411 String escaped = elementName.replaceAll("\$", "_"); 429 String escaped = elementName.replaceAll("\$", "_");
412 SourceString boxedName = 430 SourceString boxedName =
413 new SourceString("${escaped}_${closureFieldCounter++}"); 431 new SourceString("${escaped}_${closureFieldCounter++}");
414 Element boxed = new Element(boxedName, ElementKind.FIELD, box); 432 Element boxed = new Element(boxedName, ElementKind.FIELD, box);
415 scopeMapping[element] = boxed; 433 scopeMapping[element] = boxed;
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
502 globalizedElement); 520 globalizedElement);
503 globalizedElement.backendMembers = 521 globalizedElement.backendMembers =
504 const EmptyLink<Element>().prepend(callElement); 522 const EmptyLink<Element>().prepend(callElement);
505 // The nested function's 'this' is the same as the one for the outer 523 // The nested function's 'this' is the same as the one for the outer
506 // function. It could be [null] if we are inside a static method. 524 // function. It could be [null] if we are inside a static method.
507 Element thisElement = closureData.thisElement; 525 Element thisElement = closureData.thisElement;
508 return new ClosureClassMap(element, globalizedElement, 526 return new ClosureClassMap(element, globalizedElement,
509 callElement, thisElement); 527 callElement, thisElement);
510 } 528 }
511 529
512 visitFunctionExpression(FunctionExpression node) { 530 void visitInvokable(Element element, Expression node, void visitChildren()) {
513 Element element = elements[node];
514 if (element.isParameter()) {
515 // TODO(ahe): This is a hack. This method should *not* call
516 // visitChildren.
517 return node.name.accept(this);
518 }
519
520 bool oldInsideClosure = insideClosure; 531 bool oldInsideClosure = insideClosure;
521 FunctionElement oldFunctionElement = currentFunctionElement; 532 Element oldFunctionElement = currentElement;
522 ClosureClassMap oldClosureData = closureData; 533 ClosureClassMap oldClosureData = closureData;
523 534
524 insideClosure = outermostFunctionElement != null; 535 insideClosure = outermostElement != null;
525 currentFunctionElement = element; 536 currentElement = element;
526 if (insideClosure) { 537 if (insideClosure) {
527 closures.add(node); 538 closures.add(node);
528 closureData = globalizeClosure(node, element); 539 closureData = globalizeClosure(node, element);
529 } else { 540 } else {
530 outermostFunctionElement = element; 541 outermostElement = element;
531 Element thisElement = null; 542 Element thisElement = null;
532 if (element.isInstanceMember() || element.isGenerativeConstructor()) { 543 if (element.isInstanceMember() || element.isGenerativeConstructor()) {
533 thisElement = new ThisElement(element); 544 thisElement = new ThisElement(element);
534 } 545 }
535 closureData = new ClosureClassMap(null, null, null, thisElement); 546 closureData = new ClosureClassMap(null, null, null, thisElement);
536 } 547 }
537 closureMappingCache[node] = closureData; 548 closureMappingCache[node] = closureData;
538 549
539 inNewScope(node, () { 550 inNewScope(node, () {
540 // We have to declare the implicit 'this' parameter. 551 // We have to declare the implicit 'this' parameter.
541 if (!insideClosure && closureData.thisElement !== null) { 552 if (!insideClosure && closureData.thisElement !== null) {
542 declareLocal(closureData.thisElement); 553 declareLocal(closureData.thisElement);
543 } 554 }
544 // If we are inside a named closure we have to declare ourselve. For 555 // If we are inside a named closure we have to declare ourselve. For
545 // simplicity we declare the local even if the closure does not have a 556 // simplicity we declare the local even if the closure does not have a
546 // name. 557 // name.
547 // It will simply not be used. 558 // It will simply not be used.
548 if (insideClosure) { 559 if (insideClosure) {
549 declareLocal(element); 560 declareLocal(element);
550 } 561 }
551 562
552 if (currentFunctionElement.isFactoryConstructor() 563 if (currentElement.isFactoryConstructor()
553 && compiler.world.needsRti(currentFunctionElement.enclosingElement)) { 564 && compiler.world.needsRti(currentElement.enclosingElement)) {
554 // Declare the type parameters in the scope. Generative 565 // Declare the type parameters in the scope. Generative
555 // constructors just use 'this'. 566 // constructors just use 'this'.
556 ClassElement cls = currentFunctionElement.enclosingElement; 567 ClassElement cls = currentElement.enclosingElement;
557 cls.typeVariables.forEach((TypeVariableType typeVariable) { 568 cls.typeVariables.forEach((TypeVariableType typeVariable) {
558 declareLocal(typeVariable.element); 569 declareLocal(typeVariable.element);
559 }); 570 });
560 } 571 }
561 572
562 // TODO(ahe): This is problematic. The backend should not repeat 573 visitChildren();
563 // the work of the resolver. It is the resolver's job to create
564 // parameters, etc. Other phases should only visit statements.
565 // TODO(floitsch): we avoid visiting the initializers on purpose so that
566 // we get an error-message later in the builder.
567 if (node.parameters !== null) node.parameters.accept(this);
568 if (node.body !== null) node.body.accept(this);
569 }); 574 });
570 575
571 576
572 ClosureClassMap savedClosureData = closureData; 577 ClosureClassMap savedClosureData = closureData;
573 bool savedInsideClosure = insideClosure; 578 bool savedInsideClosure = insideClosure;
574 579
575 // Restore old values. 580 // Restore old values.
576 insideClosure = oldInsideClosure; 581 insideClosure = oldInsideClosure;
577 closureData = oldClosureData; 582 closureData = oldClosureData;
578 currentFunctionElement = oldFunctionElement; 583 currentElement = oldFunctionElement;
579 584
580 // Mark all free variables as captured and use them in the outer function. 585 // Mark all free variables as captured and use them in the outer function.
581 List<Element> freeVariables = 586 List<Element> freeVariables =
582 savedClosureData.freeVariableMapping.getKeys(); 587 savedClosureData.freeVariableMapping.getKeys();
583 assert(freeVariables.isEmpty() || savedInsideClosure); 588 assert(freeVariables.isEmpty() || savedInsideClosure);
584 for (Element freeElement in freeVariables) { 589 for (Element freeElement in freeVariables) {
585 if (capturedVariableMapping[freeElement] != null && 590 if (capturedVariableMapping[freeElement] != null &&
586 capturedVariableMapping[freeElement] != freeElement) { 591 capturedVariableMapping[freeElement] != freeElement) {
587 compiler.internalError('In closure analyzer', node: node); 592 compiler.internalError('In closure analyzer', node: node);
588 } 593 }
589 capturedVariableMapping[freeElement] = freeElement; 594 capturedVariableMapping[freeElement] = freeElement;
590 useLocal(freeElement); 595 useLocal(freeElement);
591 } 596 }
592 } 597 }
593 598
599 visitFunctionExpression(FunctionExpression node) {
600 Element element = elements[node];
601
602 if (element.isParameter()) {
603 // TODO(ahe): This is a hack. This method should *not* call
604 // visitChildren.
605 return node.name.accept(this);
606 }
607
608 visitInvokable(element, node, () {
609 // TODO(ahe): This is problematic. The backend should not repeat
610 // the work of the resolver. It is the resolver's job to create
611 // parameters, etc. Other phases should only visit statements.
612 // TODO(floitsch): we avoid visiting the initializers on purpose so that
613 // we get an error-message later in the builder.
614 if (node.parameters !== null) node.parameters.accept(this);
615 if (node.body !== null) node.body.accept(this);
616 });
617 }
618
594 visitFunctionDeclaration(FunctionDeclaration node) { 619 visitFunctionDeclaration(FunctionDeclaration node) {
595 node.visitChildren(this); 620 node.visitChildren(this);
596 declareLocal(elements[node]); 621 declareLocal(elements[node]);
597 } 622 }
598 623
599 visitTryStatement(TryStatement node) { 624 visitTryStatement(TryStatement node) {
600 // TODO(ngeoffray): implement finer grain state. 625 // TODO(ngeoffray): implement finer grain state.
601 bool oldInTryStatement = inTryStatement; 626 bool oldInTryStatement = inTryStatement;
602 inTryStatement = true; 627 inTryStatement = true;
603 node.visitChildren(this); 628 node.visitChildren(this);
604 inTryStatement = oldInTryStatement; 629 inTryStatement = oldInTryStatement;
605 } 630 }
606 } 631 }
OLDNEW
« no previous file with comments | « no previous file | lib/compiler/implementation/elements/elements.dart » ('j') | lib/compiler/implementation/elements/elements.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698