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

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

Issue 10855146: Make the closure-to-class translator more accessible. It can now be (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 4 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
« no previous file with comments | « no previous file | lib/compiler/implementation/compiler.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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");
ahe 2012/08/14 12:54:38 I like an empty line between #library and #import.
floitsch 2012/08/14 14:16:32 Done.
6 #import("elements/elements.dart");
7 #import("leg.dart");
8 #import("scanner/scannerlib.dart");
9 #import("tree/tree.dart");
10 #import("util/util.dart");
11
12 class ClosureTask extends CompilerTask {
13 Map<Node, ClosureToClassMapping> closureMappingCache;
14 ClosureTask(Compiler compiler)
15 : closureMappingCache = new Map<Node, ClosureToClassMapping>(),
16 super(compiler);
17
18 String get name() => "Closure Simplifier";
19
20 ClosureToClassMapping computeClosureToClassMapping(FunctionExpression node,
21 TreeElements elements) {
22 return measure(() {
23 ClosureToClassMapping cached = closureMappingCache[node];
24 if (cached !== null) return cached;
25
26 ClosureTranslator translator =
27 new ClosureTranslator(compiler, elements, closureMappingCache);
28 // The translator will store the computed closure-mappings inside the
29 // cache. One for given method and one for each nested closure.
30 translator.translate(node);
31 assert(closureMappingCache[node] != null);
32 return closureMappingCache[node];
33 });
34 }
35
36 ClosureToClassMapping getMappingForNestedFunction(FunctionExpression node) {
ahe 2012/08/14 12:54:38 Should this be wrapped in measure?
floitsch 2012/08/14 14:16:32 It is just looking up in a hashmap, so the cost sh
37 ClosureToClassMapping nestedClosureData = closureMappingCache[node];
38 if (nestedClosureData === null) {
39 // TODO(floitsch): we can only assume that the reason for not having a
40 // closure data here is, because the function is inside an initializer.
41 compiler.unimplemented("Closures inside initializers", node: node);
42 }
43 return nestedClosureData;
44 }
45 }
46
5 class ClosureFieldElement extends Element { 47 class ClosureFieldElement extends Element {
6 ClosureFieldElement(SourceString name, ClassElement enclosing) 48 ClosureFieldElement(SourceString name, ClassElement enclosing)
7 : super(name, ElementKind.FIELD, enclosing); 49 : super(name, ElementKind.FIELD, enclosing);
8 50
9 bool isInstanceMember() => true; 51 bool isInstanceMember() => true;
10 bool isAssignable() => false; 52 bool isAssignable() => false;
11 53
12 String toString() => "ClosureFieldElement($name)"; 54 String toString() => "ClosureFieldElement($name)";
13 } 55 }
14 56
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 // declared in the initializer of the [For] and that need to be boxed. 99 // declared in the initializer of the [For] and that need to be boxed.
58 // Otherwise contains the empty List. 100 // Otherwise contains the empty List.
59 List<Element> boxedLoopVariables; 101 List<Element> boxedLoopVariables;
60 102
61 ClosureScope(this.boxElement, this.capturedVariableMapping) 103 ClosureScope(this.boxElement, this.capturedVariableMapping)
62 : boxedLoopVariables = const <Element>[]; 104 : boxedLoopVariables = const <Element>[];
63 105
64 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty(); 106 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty();
65 } 107 }
66 108
67 class ClosureData { 109 class ClosureToClassMapping {
ahe 2012/08/14 12:54:38 If you don't like this name, consider "ClosureClas
floitsch 2012/08/14 14:16:32 As you can see I was struggling with the name. Hap
68 // The closure's element before any translation. Will be null for methods. 110 // The closure's element before any translation. Will be null for methods.
69 final FunctionElement closureElement; 111 final FunctionElement closureElement;
70 // The closureClassElement will be null for methods that are not local 112 // The closureClassElement will be null for methods that are not local
71 // closures. 113 // closures.
72 final ClassElement closureClassElement; 114 final ClassElement closureClassElement;
73 // The callElement will be null for methods that are not local closures. 115 // The callElement will be null for methods that are not local closures.
74 final FunctionElement callElement; 116 final FunctionElement callElement;
75 // The [thisElement] makes handling 'this' easier by treating it like any 117 // The [thisElement] makes handling 'this' easier by treating it like any
76 // other argument. It is only set for instance-members. 118 // other argument. It is only set for instance-members.
77 final ThisElement thisElement; 119 final ThisElement thisElement;
78 120
79 // Maps free locals, arguments and function elements to their captured 121 // Maps free locals, arguments and function elements to their captured
80 // copies. 122 // copies.
81 final Map<Element, Element> freeVariableMapping; 123 final Map<Element, Element> freeVariableMapping;
82 // Maps closure-fields to their captured elements. This is somehow the inverse 124 // Maps closure-fields to their captured elements. This is somehow the inverse
83 // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does 125 // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does
84 // not deal with boxes, here we map instance-fields (which might represent 126 // not deal with boxes, here we map instance-fields (which might represent
85 // boxes) to their boxElement. 127 // boxes) to their boxElement.
86 final Map<Element, Element> capturedFieldMapping; 128 final Map<Element, Element> capturedFieldMapping;
87 129
88 // Maps scopes ([Loop] and [FunctionExpression] nodes) to their 130 // Maps scopes ([Loop] and [FunctionExpression] nodes) to their
89 // [ClosureScope] which contains their box and the 131 // [ClosureScope] which contains their box and the
90 // captured variables that are stored in the box. 132 // captured variables that are stored in the box.
91 // This map will be empty if the method/closure of this [ClosureData] does not 133 // This map will be empty if the method/closure of this [ClosureData] does not
92 // contain any nested closure. 134 // contain any nested closure.
93 final Map<Node, ClosureScope> capturingScopes; 135 final Map<Node, ClosureScope> capturingScopes;
94 136
95 final Set<Element> usedVariablesInTry; 137 final Set<Element> usedVariablesInTry;
96 138
97 ClosureData(this.closureElement, 139 ClosureToClassMapping(this.closureElement,
98 this.closureClassElement, 140 this.closureClassElement,
99 this.callElement, 141 this.callElement,
100 this.thisElement) 142 this.thisElement)
101 : this.freeVariableMapping = new Map<Element, Element>(), 143 : this.freeVariableMapping = new Map<Element, Element>(),
102 this.capturedFieldMapping = new Map<Element, Element>(), 144 this.capturedFieldMapping = new Map<Element, Element>(),
103 this.capturingScopes = new Map<Node, ClosureScope>(), 145 this.capturingScopes = new Map<Node, ClosureScope>(),
104 this.usedVariablesInTry = new Set<Element>(); 146 this.usedVariablesInTry = new Set<Element>();
105 147
106 bool isClosure() => closureElement !== null; 148 bool isClosure() => closureElement !== null;
107 } 149 }
108 150
109 class ClosureTranslator extends AbstractVisitor { 151 class ClosureTranslator extends AbstractVisitor {
110 final SsaBuilder builder; 152 final Compiler compiler;
111 final TreeElements elements; 153 final TreeElements elements;
112 int closureFieldCounter = 0; 154 int closureFieldCounter = 0;
113 bool inTryStatement = false; 155 bool inTryStatement = false;
114 final Map<Node, ClosureData> closureDataCache; 156 final Map<Node, ClosureToClassMapping> closureMappingCache;
115 157
116 // Map of captured variables. Initially they will map to themselves. If 158 // Map of captured variables. Initially they will map to themselves. If
117 // a variable needs to be boxed then the scope declaring the variable 159 // a variable needs to be boxed then the scope declaring the variable
118 // will update this mapping. 160 // will update this mapping.
119 Map<Element, Element> capturedVariableMapping; 161 Map<Element, Element> capturedVariableMapping;
120 // List of encountered closures. 162 // List of encountered closures.
121 List<FunctionExpression> closures; 163 List<FunctionExpression> closures;
122 164
123 // The variables that have been declared in the current scope. 165 // The variables that have been declared in the current scope.
124 List<Element> scopeVariables; 166 List<Element> scopeVariables;
125 167
126 // Keep track of the mutated variables so that we don't need to box 168 // Keep track of the mutated variables so that we don't need to box
127 // non-mutated variables. 169 // non-mutated variables.
128 Set<Element> mutatedVariables; 170 Set<Element> mutatedVariables;
129 171
130 FunctionElement currentFunctionElement; 172 FunctionElement currentFunctionElement;
131 // The closureData of the currentFunctionElement. 173 // The closureData of the currentFunctionElement.
132 ClosureData closureData; 174 ClosureToClassMapping closureData;
133 175
134 bool insideClosure = false; 176 bool insideClosure = false;
135 177
136 Compiler get compiler() => builder.compiler; 178 ClosureTranslator(this.compiler, this.elements, this.closureMappingCache)
179 : capturedVariableMapping = new Map<Element, Element>(),
180 closures = <FunctionExpression>[],
181 mutatedVariables = new Set<Element>();
137 182
138 ClosureTranslator(SsaBuilder builder) 183 void translate(Node node) {
139 : this.builder = builder,
140 this.elements = builder.elements,
141 capturedVariableMapping = new Map<Element, Element>(),
142 closures = <FunctionExpression>[],
143 mutatedVariables = new Set<Element>(),
144 this.closureDataCache = builder.builder.closureDataCache;
145
146 ClosureData translate(Node node) {
147 // Closures have already been analyzed when visiting the surrounding
148 // method/function. This also shortcuts for bailout functions.
149 ClosureData cached = closureDataCache[node];
150 if (cached !== null) return cached;
151
152 visit(node); 184 visit(node);
153 // When variables need to be boxed their [capturedVariableMapping] is 185 // When variables need to be boxed their [capturedVariableMapping] is
154 // updated, but we delay updating the similar freeVariableMapping in the 186 // updated, but we delay updating the similar freeVariableMapping in the
155 // closure datas that capture these variables. 187 // closure datas that capture these variables.
156 // The closures don't have their fields (in the closure class) set, either. 188 // The closures don't have their fields (in the closure class) set, either.
157 updateClosures(); 189 updateClosures();
158
159 return closureDataCache[node];
160 } 190 }
161 191
162 // This function runs through all of the existing closures and updates their 192 // This function runs through all of the existing closures and updates their
163 // free variables to the boxed value. It also adds the field-elements to the 193 // free variables to the boxed value. It also adds the field-elements to the
164 // class representing the closure. At the same time it fills the 194 // class representing the closure. At the same time it fills the
165 // [capturedFieldMapping]. 195 // [capturedFieldMapping].
166 void updateClosures() { 196 void updateClosures() {
167 for (FunctionExpression closure in closures) { 197 for (FunctionExpression closure in closures) {
168 // The captured variables that need to be stored in a field of the closure 198 // The captured variables that need to be stored in a field of the closure
169 // class. 199 // class.
170 Set<Element> fieldCaptures = new Set<Element>(); 200 Set<Element> fieldCaptures = new Set<Element>();
171 ClosureData data = closureDataCache[closure]; 201 ClosureToClassMapping data = closureMappingCache[closure];
172 Map<Element, Element> freeVariableMapping = data.freeVariableMapping; 202 Map<Element, Element> freeVariableMapping = data.freeVariableMapping;
173 // We get a copy of the keys and iterate over it, to avoid modifications 203 // We get a copy of the keys and iterate over it, to avoid modifications
174 // to the map while iterating over it. 204 // to the map while iterating over it.
175 freeVariableMapping.getKeys().forEach((Element fromElement) { 205 freeVariableMapping.getKeys().forEach((Element fromElement) {
176 assert(fromElement == freeVariableMapping[fromElement]); 206 assert(fromElement == freeVariableMapping[fromElement]);
177 Element updatedElement = capturedVariableMapping[fromElement]; 207 Element updatedElement = capturedVariableMapping[fromElement];
178 assert(updatedElement !== null); 208 assert(updatedElement !== null);
179 if (fromElement == updatedElement) { 209 if (fromElement == updatedElement) {
180 assert(freeVariableMapping[fromElement] == updatedElement); 210 assert(freeVariableMapping[fromElement] == updatedElement);
181 assert(Elements.isLocal(updatedElement)); 211 assert(Elements.isLocal(updatedElement));
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 link = link.tail) { 386 link = link.tail) {
357 Node definition = link.head; 387 Node definition = link.head;
358 Element element = elements[definition]; 388 Element element = elements[definition];
359 if (capturedVariableMapping.containsKey(element)) { 389 if (capturedVariableMapping.containsKey(element)) {
360 result.add(element); 390 result.add(element);
361 }; 391 };
362 } 392 }
363 scopeData.boxedLoopVariables = result; 393 scopeData.boxedLoopVariables = result;
364 } 394 }
365 395
366 ClosureData globalizeClosure(FunctionExpression node, Element element) { 396 ClosureToClassMapping globalizeClosure(FunctionExpression node, Element elemen t) {
ahe 2012/08/14 12:54:38 Long line.
floitsch 2012/08/14 14:16:32 not anymore after the renaming.
367 SourceString closureName = 397 SourceString closureName =
368 new SourceString(compiler.namer.closureName(element)); 398 new SourceString(compiler.namer.closureName(element));
369 ClassElement globalizedElement = new ClosureClassElement( 399 ClassElement globalizedElement = new ClosureClassElement(
370 closureName, compiler, element.getCompilationUnit()); 400 closureName, compiler, element.getCompilationUnit());
371 FunctionElement callElement = 401 FunctionElement callElement =
372 new FunctionElement.from(compiler.namer.CLOSURE_INVOCATION_NAME, 402 new FunctionElement.from(compiler.namer.CLOSURE_INVOCATION_NAME,
373 element, 403 element,
374 globalizedElement); 404 globalizedElement);
375 globalizedElement.backendMembers = 405 globalizedElement.backendMembers =
376 const EmptyLink<Element>().prepend(callElement); 406 const EmptyLink<Element>().prepend(callElement);
377 // The nested function's 'this' is the same as the one for the outer 407 // The nested function's 'this' is the same as the one for the outer
378 // function. It could be [null] if we are inside a static method. 408 // function. It could be [null] if we are inside a static method.
379 Element thisElement = closureData.thisElement; 409 Element thisElement = closureData.thisElement;
380 return new ClosureData(element, globalizedElement, 410 return new ClosureToClassMapping(element, globalizedElement,
381 callElement, thisElement); 411 callElement, thisElement);
ahe 2012/08/14 12:54:38 Indentation.
floitsch 2012/08/14 14:16:32 Done.
382 } 412 }
383 413
384 visitFunctionExpression(FunctionExpression node) { 414 visitFunctionExpression(FunctionExpression node) {
385 Element element = elements[node]; 415 Element element = elements[node];
386 if (element.kind === ElementKind.PARAMETER) { 416 if (element.kind === ElementKind.PARAMETER) {
387 // TODO(ahe): This is a hack. This method should *not* call 417 // TODO(ahe): This is a hack. This method should *not* call
388 // visitChildren. 418 // visitChildren.
389 return node.name.accept(this); 419 return node.name.accept(this);
390 } 420 }
391 bool isClosure = (closureData !== null); 421 bool isClosure = (closureData !== null);
392 422
393 if (isClosure) closures.add(node); 423 if (isClosure) closures.add(node);
394 424
395 bool oldInsideClosure = insideClosure; 425 bool oldInsideClosure = insideClosure;
396 FunctionElement oldFunctionElement = currentFunctionElement; 426 FunctionElement oldFunctionElement = currentFunctionElement;
397 ClosureData oldClosureData = closureData; 427 ClosureToClassMapping oldClosureData = closureData;
398 428
399 insideClosure = isClosure; 429 insideClosure = isClosure;
400 currentFunctionElement = elements[node]; 430 currentFunctionElement = elements[node];
401 if (insideClosure) { 431 if (insideClosure) {
402 closureData = globalizeClosure(node, element); 432 closureData = globalizeClosure(node, element);
403 } else { 433 } else {
404 Element thisElement = null; 434 Element thisElement = null;
405 // TODO(floitsch): we should not need to look for generative constructors. 435 // TODO(floitsch): we should not need to look for generative constructors.
406 // At the moment we store only one ClosureData for both the factory and 436 // At the moment we store only one ClosureData for both the factory and
407 // the body. 437 // the body.
408 if (element.isInstanceMember() || 438 if (element.isInstanceMember() ||
409 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { 439 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
410 // TODO(floitsch): currently all variables are considered to be 440 // TODO(floitsch): currently all variables are considered to be
411 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'. 441 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'.
412 Element thisEnclosingElement = element; 442 Element thisEnclosingElement = element;
413 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { 443 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
414 ConstructorBodyElement body = element; 444 ConstructorBodyElement body = element;
415 thisEnclosingElement = body.constructor; 445 thisEnclosingElement = body.constructor;
416 } 446 }
417 thisElement = new ThisElement(thisEnclosingElement); 447 thisElement = new ThisElement(thisEnclosingElement);
418 } 448 }
419 closureData = new ClosureData(null, null, null, thisElement); 449 closureData = new ClosureToClassMapping(null, null, null, thisElement);
420 } 450 }
421 451
422 inNewScope(node, () { 452 inNewScope(node, () {
423 // We have to declare the implicit 'this' parameter. 453 // We have to declare the implicit 'this' parameter.
424 if (!insideClosure && closureData.thisElement !== null) { 454 if (!insideClosure && closureData.thisElement !== null) {
425 declareLocal(closureData.thisElement); 455 declareLocal(closureData.thisElement);
426 } 456 }
427 // If we are inside a named closure we have to declare ourselve. For 457 // If we are inside a named closure we have to declare ourselve. For
428 // simplicity we declare the local even if the closure does not have a 458 // simplicity we declare the local even if the closure does not have a
429 // name. 459 // name.
430 // It will simply not be used. 460 // It will simply not be used.
431 if (insideClosure) { 461 if (insideClosure) {
432 declareLocal(element); 462 declareLocal(element);
433 } 463 }
434 464
435 // TODO(ahe): This is problematic. The backend should not repeat 465 // TODO(ahe): This is problematic. The backend should not repeat
436 // the work of the resolver. It is the resolver's job to create 466 // the work of the resolver. It is the resolver's job to create
437 // parameters, etc. Other phases should only visit statements. 467 // parameters, etc. Other phases should only visit statements.
438 // TODO(floitsch): we avoid visiting the initializers on purpose so that 468 // TODO(floitsch): we avoid visiting the initializers on purpose so that
439 // we get an error-message later in the builder. 469 // we get an error-message later in the builder.
440 if (node.parameters !== null) node.parameters.accept(this); 470 if (node.parameters !== null) node.parameters.accept(this);
441 if (node.body !== null) node.body.accept(this); 471 if (node.body !== null) node.body.accept(this);
442 }); 472 });
443 473
444 closureDataCache[node] = closureData; 474 closureMappingCache[node] = closureData;
445 475
446 ClosureData savedClosureData = closureData; 476 ClosureToClassMapping savedClosureData = closureData;
447 bool savedInsideClosure = insideClosure; 477 bool savedInsideClosure = insideClosure;
448 478
449 // Restore old values. 479 // Restore old values.
450 insideClosure = oldInsideClosure; 480 insideClosure = oldInsideClosure;
451 closureData = oldClosureData; 481 closureData = oldClosureData;
452 currentFunctionElement = oldFunctionElement; 482 currentFunctionElement = oldFunctionElement;
453 483
454 // Mark all free variables as captured and use them in the outer function. 484 // Mark all free variables as captured and use them in the outer function.
455 List<Element> freeVariables = 485 List<Element> freeVariables =
456 savedClosureData.freeVariableMapping.getKeys(); 486 savedClosureData.freeVariableMapping.getKeys();
(...skipping 14 matching lines...) Expand all
471 } 501 }
472 502
473 visitTryStatement(TryStatement node) { 503 visitTryStatement(TryStatement node) {
474 // TODO(ngeoffray): implement finer grain state. 504 // TODO(ngeoffray): implement finer grain state.
475 bool oldInTryStatement = inTryStatement; 505 bool oldInTryStatement = inTryStatement;
476 inTryStatement = true; 506 inTryStatement = true;
477 node.visitChildren(this); 507 node.visitChildren(this);
478 inTryStatement = oldInTryStatement; 508 inTryStatement = oldInTryStatement;
479 } 509 }
480 } 510 }
OLDNEW
« no previous file with comments | « no previous file | lib/compiler/implementation/compiler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698