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

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

Powered by Google App Engine
This is Rietveld 408576698