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

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 another 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 assert(elements[node] == element);
205 visit(node); // [visitFunctionExpression] will call [visitInvokable].
197 // When variables need to be boxed their [capturedVariableMapping] is 206 // When variables need to be boxed their [capturedVariableMapping] is
198 // updated, but we delay updating the similar freeVariableMapping in the 207 // updated, but we delay updating the similar freeVariableMapping in the
199 // closure datas that capture these variables. 208 // closure datas that capture these variables.
200 // The closures don't have their fields (in the closure class) set, either. 209 // The closures don't have their fields (in the closure class) set, either.
201 updateClosures(); 210 updateClosures();
202 } 211 }
203 212
213 void translateLazyInitializer(Element element, SendSet node) {
214 assert(node.assignmentOperator.source == const SourceString("="));
215 Expression initialValue = node.argumentsNode.nodes.head;
216 visitInvokable(element, node, () { visit(initialValue); });
217 updateClosures();
218 }
219
204 // This function runs through all of the existing closures and updates their 220 // 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 221 // 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 222 // class representing the closure. At the same time it fills the
207 // [capturedFieldMapping]. 223 // [capturedFieldMapping].
208 void updateClosures() { 224 void updateClosures() {
209 for (FunctionExpression closure in closures) { 225 for (Expression closure in closures) {
210 // The captured variables that need to be stored in a field of the closure 226 // The captured variables that need to be stored in a field of the closure
211 // class. 227 // class.
212 Set<Element> fieldCaptures = new Set<Element>(); 228 Set<Element> fieldCaptures = new Set<Element>();
213 ClosureClassMap data = closureMappingCache[closure]; 229 ClosureClassMap data = closureMappingCache[closure];
214 Map<Element, Element> freeVariableMapping = data.freeVariableMapping; 230 Map<Element, Element> freeVariableMapping = data.freeVariableMapping;
215 // We get a copy of the keys and iterate over it, to avoid modifications 231 // We get a copy of the keys and iterate over it, to avoid modifications
216 // to the map while iterating over it. 232 // to the map while iterating over it.
217 freeVariableMapping.getKeys().forEach((Element fromElement) { 233 freeVariableMapping.getKeys().forEach((Element fromElement) {
218 assert(fromElement == freeVariableMapping[fromElement]); 234 assert(fromElement == freeVariableMapping[fromElement]);
219 Element updatedElement = capturedVariableMapping[fromElement]; 235 Element updatedElement = capturedVariableMapping[fromElement];
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
253 } 269 }
254 270
255 void useLocal(Element element) { 271 void useLocal(Element element) {
256 // If the element is not declared in the current function and the element 272 // 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. 273 // 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 274 // Note that the check on [insideClosure] is not just an
259 // optimization: factories have type parameters as function 275 // optimization: factories have type parameters as function
260 // parameters, and type parameters are declared in the class, not 276 // parameters, and type parameters are declared in the class, not
261 // the factory. 277 // the factory.
262 if (insideClosure && 278 if (insideClosure &&
263 element.enclosingElement != currentFunctionElement && 279 element.enclosingElement != currentElement &&
264 element != currentFunctionElement) { 280 element != currentElement) {
265 assert(closureData.freeVariableMapping[element] == null || 281 assert(closureData.freeVariableMapping[element] == null ||
266 closureData.freeVariableMapping[element] == element); 282 closureData.freeVariableMapping[element] == element);
267 closureData.freeVariableMapping[element] = element; 283 closureData.freeVariableMapping[element] = element;
268 } else if (inTryStatement) { 284 } else if (inTryStatement) {
269 // Don't mark the this-element. This would complicate things in the 285 // Don't mark the this-element. This would complicate things in the
270 // builder. 286 // builder.
271 if (element != closureData.thisElement) { 287 if (element != closureData.thisElement) {
272 // TODO(ngeoffray): only do this if the variable is mutated. 288 // TODO(ngeoffray): only do this if the variable is mutated.
273 closureData.usedVariablesInTry.add(element); 289 closureData.usedVariablesInTry.add(element);
274 } 290 }
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 void analyzeTypeVariables(DartType type) { 382 void analyzeTypeVariables(DartType type) {
367 if (type is TypeVariableType) { 383 if (type is TypeVariableType) {
368 useLocal(type.element); 384 useLocal(type.element);
369 } else if (type is InterfaceType) { 385 } else if (type is InterfaceType) {
370 InterfaceType ifcType = type; 386 InterfaceType ifcType = type;
371 for (DartType argument in ifcType.arguments) { 387 for (DartType argument in ifcType.arguments) {
372 analyzeTypeVariables(argument); 388 analyzeTypeVariables(argument);
373 } 389 }
374 } 390 }
375 } 391 }
376 if (outermostFunctionElement.isMember() && 392 if (outermostElement.isMember() &&
377 compiler.world.needsRti(outermostFunctionElement.getEnclosingClass())) { 393 compiler.world.needsRti(outermostElement.getEnclosingClass())) {
378 if (outermostFunctionElement.isInstanceMember() 394 if (outermostElement.isInstanceMember()
379 || outermostFunctionElement.isGenerativeConstructor()) { 395 || outermostElement.isGenerativeConstructor()) {
380 if (hasTypeVariable(type)) useLocal(closureData.thisElement); 396 if (hasTypeVariable(type)) useLocal(closureData.thisElement);
381 } else if (outermostFunctionElement.isFactoryConstructor()) { 397 } else if (outermostElement.isFactoryConstructor()) {
382 analyzeTypeVariables(type); 398 analyzeTypeVariables(type);
383 } 399 }
384 } 400 }
385 401
386 node.visitChildren(this); 402 node.visitChildren(this);
387 } 403 }
388 404
389 // If variables that are declared in the [node] scope are captured and need 405 // 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 406 // to be boxed create a box-element and update the [capturingScopes] in the
391 // current [closureData]. 407 // current [closureData].
392 // The boxed variables are updated in the [capturedVariableMapping]. 408 // The boxed variables are updated in the [capturedVariableMapping].
393 void attachCapturedScopeVariables(Node node) { 409 void attachCapturedScopeVariables(Node node) {
394 Element box = null; 410 Element box = null;
395 Map<Element, Element> scopeMapping = new Map<Element, Element>(); 411 Map<Element, Element> scopeMapping = new Map<Element, Element>();
396 for (Element element in scopeVariables) { 412 for (Element element in scopeVariables) {
397 // No need to box non-assignable elements. 413 // No need to box non-assignable elements.
398 if (!element.isAssignable()) continue; 414 if (!element.isAssignable()) continue;
399 if (!mutatedVariables.contains(element)) continue; 415 if (!mutatedVariables.contains(element)) continue;
400 if (capturedVariableMapping.containsKey(element)) { 416 if (capturedVariableMapping.containsKey(element)) {
401 if (box == null) { 417 if (box == null) {
402 // TODO(floitsch): construct better box names. 418 // TODO(floitsch): construct better box names.
403 SourceString boxName = 419 SourceString boxName =
404 new SourceString("box_${closureFieldCounter++}"); 420 new SourceString("box_${closureFieldCounter++}");
405 box = new BoxElement(boxName, currentFunctionElement); 421 box = new BoxElement(boxName, currentElement);
406 } 422 }
407 // TODO(floitsch): construct better boxed names. 423 // TODO(floitsch): construct better boxed names.
408 String elementName = element.name.slowToString(); 424 String elementName = element.name.slowToString();
409 // We are currently using the name in an HForeign which could replace 425 // We are currently using the name in an HForeign which could replace
410 // "$X" with something else. 426 // "$X" with something else.
411 String escaped = elementName.replaceAll("\$", "_"); 427 String escaped = elementName.replaceAll("\$", "_");
412 SourceString boxedName = 428 SourceString boxedName =
413 new SourceString("${escaped}_${closureFieldCounter++}"); 429 new SourceString("${escaped}_${closureFieldCounter++}");
414 Element boxed = new Element(boxedName, ElementKind.FIELD, box); 430 Element boxed = new Element(boxedName, ElementKind.FIELD, box);
415 scopeMapping[element] = boxed; 431 scopeMapping[element] = boxed;
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
502 globalizedElement); 518 globalizedElement);
503 globalizedElement.backendMembers = 519 globalizedElement.backendMembers =
504 const EmptyLink<Element>().prepend(callElement); 520 const EmptyLink<Element>().prepend(callElement);
505 // The nested function's 'this' is the same as the one for the outer 521 // 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. 522 // function. It could be [null] if we are inside a static method.
507 Element thisElement = closureData.thisElement; 523 Element thisElement = closureData.thisElement;
508 return new ClosureClassMap(element, globalizedElement, 524 return new ClosureClassMap(element, globalizedElement,
509 callElement, thisElement); 525 callElement, thisElement);
510 } 526 }
511 527
512 visitFunctionExpression(FunctionExpression node) { 528 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; 529 bool oldInsideClosure = insideClosure;
521 FunctionElement oldFunctionElement = currentFunctionElement; 530 Element oldFunctionElement = currentElement;
522 ClosureClassMap oldClosureData = closureData; 531 ClosureClassMap oldClosureData = closureData;
523 532
524 insideClosure = outermostFunctionElement != null; 533 insideClosure = outermostElement != null;
525 currentFunctionElement = element; 534 currentElement = element;
526 if (insideClosure) { 535 if (insideClosure) {
527 closures.add(node); 536 closures.add(node);
528 closureData = globalizeClosure(node, element); 537 closureData = globalizeClosure(node, element);
529 } else { 538 } else {
530 outermostFunctionElement = element; 539 outermostElement = element;
531 Element thisElement = null; 540 Element thisElement = null;
532 if (element.isInstanceMember() || element.isGenerativeConstructor()) { 541 if (element.isInstanceMember() || element.isGenerativeConstructor()) {
533 thisElement = new ThisElement(element); 542 thisElement = new ThisElement(element);
534 } 543 }
535 closureData = new ClosureClassMap(null, null, null, thisElement); 544 closureData = new ClosureClassMap(null, null, null, thisElement);
536 } 545 }
537 closureMappingCache[node] = closureData; 546 closureMappingCache[node] = closureData;
538 547
539 inNewScope(node, () { 548 inNewScope(node, () {
540 // We have to declare the implicit 'this' parameter. 549 // We have to declare the implicit 'this' parameter.
541 if (!insideClosure && closureData.thisElement !== null) { 550 if (!insideClosure && closureData.thisElement !== null) {
542 declareLocal(closureData.thisElement); 551 declareLocal(closureData.thisElement);
543 } 552 }
544 // If we are inside a named closure we have to declare ourselve. For 553 // 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 554 // simplicity we declare the local even if the closure does not have a
546 // name. 555 // name.
547 // It will simply not be used. 556 // It will simply not be used.
548 if (insideClosure) { 557 if (insideClosure) {
549 declareLocal(element); 558 declareLocal(element);
550 } 559 }
551 560
552 if (currentFunctionElement.isFactoryConstructor() 561 if (currentElement.isFactoryConstructor()
553 && compiler.world.needsRti(currentFunctionElement.enclosingElement)) { 562 && compiler.world.needsRti(currentElement.enclosingElement)) {
554 // Declare the type parameters in the scope. Generative 563 // Declare the type parameters in the scope. Generative
555 // constructors just use 'this'. 564 // constructors just use 'this'.
556 ClassElement cls = currentFunctionElement.enclosingElement; 565 ClassElement cls = currentElement.enclosingElement;
557 cls.typeVariables.forEach((TypeVariableType typeVariable) { 566 cls.typeVariables.forEach((TypeVariableType typeVariable) {
558 declareLocal(typeVariable.element); 567 declareLocal(typeVariable.element);
559 }); 568 });
560 } 569 }
561 570
562 // TODO(ahe): This is problematic. The backend should not repeat 571 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 }); 572 });
570 573
571 574
572 ClosureClassMap savedClosureData = closureData; 575 ClosureClassMap savedClosureData = closureData;
573 bool savedInsideClosure = insideClosure; 576 bool savedInsideClosure = insideClosure;
574 577
575 // Restore old values. 578 // Restore old values.
576 insideClosure = oldInsideClosure; 579 insideClosure = oldInsideClosure;
577 closureData = oldClosureData; 580 closureData = oldClosureData;
578 currentFunctionElement = oldFunctionElement; 581 currentElement = oldFunctionElement;
579 582
580 // Mark all free variables as captured and use them in the outer function. 583 // Mark all free variables as captured and use them in the outer function.
581 List<Element> freeVariables = 584 List<Element> freeVariables =
582 savedClosureData.freeVariableMapping.getKeys(); 585 savedClosureData.freeVariableMapping.getKeys();
583 assert(freeVariables.isEmpty() || savedInsideClosure); 586 assert(freeVariables.isEmpty() || savedInsideClosure);
584 for (Element freeElement in freeVariables) { 587 for (Element freeElement in freeVariables) {
585 if (capturedVariableMapping[freeElement] != null && 588 if (capturedVariableMapping[freeElement] != null &&
586 capturedVariableMapping[freeElement] != freeElement) { 589 capturedVariableMapping[freeElement] != freeElement) {
587 compiler.internalError('In closure analyzer', node: node); 590 compiler.internalError('In closure analyzer', node: node);
588 } 591 }
589 capturedVariableMapping[freeElement] = freeElement; 592 capturedVariableMapping[freeElement] = freeElement;
590 useLocal(freeElement); 593 useLocal(freeElement);
591 } 594 }
592 } 595 }
593 596
597 visitFunctionExpression(FunctionExpression node) {
598 Element element = elements[node];
599
600 if (element.isParameter()) {
601 // TODO(ahe): This is a hack. This method should *not* call
602 // visitChildren.
603 return node.name.accept(this);
604 }
605
606 visitInvokable(element, node, () {
607 // TODO(ahe): This is problematic. The backend should not repeat
608 // the work of the resolver. It is the resolver's job to create
609 // parameters, etc. Other phases should only visit statements.
610 // TODO(floitsch): we avoid visiting the initializers on purpose so that
611 // we get an error-message later in the builder.
612 if (node.parameters !== null) node.parameters.accept(this);
613 if (node.body !== null) node.body.accept(this);
614 });
615 }
616
594 visitFunctionDeclaration(FunctionDeclaration node) { 617 visitFunctionDeclaration(FunctionDeclaration node) {
595 node.visitChildren(this); 618 node.visitChildren(this);
596 declareLocal(elements[node]); 619 declareLocal(elements[node]);
597 } 620 }
598 621
599 visitTryStatement(TryStatement node) { 622 visitTryStatement(TryStatement node) {
600 // TODO(ngeoffray): implement finer grain state. 623 // TODO(ngeoffray): implement finer grain state.
601 bool oldInTryStatement = inTryStatement; 624 bool oldInTryStatement = inTryStatement;
602 inTryStatement = true; 625 inTryStatement = true;
603 node.visitChildren(this); 626 node.visitChildren(this);
604 inTryStatement = oldInTryStatement; 627 inTryStatement = oldInTryStatement;
605 } 628 }
606 } 629 }
OLDNEW
« no previous file with comments | « no previous file | lib/compiler/implementation/resolver.dart » ('j') | lib/compiler/implementation/ssa/builder.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698