Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |