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; | 5 library closureToClassMapper; |
| 6 | 6 |
| 7 import 'common/names.dart' show Identifiers; | 7 import 'common/names.dart' show Identifiers; |
| 8 import 'common/resolution.dart' show ParsingContext, Resolution; | 8 import 'common/resolution.dart' show ParsingContext, Resolution; |
| 9 import 'common/tasks.dart' show CompilerTask; | 9 import 'common/tasks.dart' show CompilerTask; |
| 10 import 'common.dart'; | 10 import 'common.dart'; |
| 11 import 'compiler.dart' show Compiler; | 11 import 'compiler.dart' show Compiler; |
| 12 import 'constants/expressions.dart'; | 12 import 'constants/expressions.dart'; |
| 13 import 'elements/resolution_types.dart'; | 13 import 'elements/resolution_types.dart'; |
| 14 import 'elements/elements.dart'; | 14 import 'elements/elements.dart'; |
| 15 import 'elements/entities.dart'; | 15 import 'elements/entities.dart'; |
| 16 import 'elements/modelx.dart' | 16 import 'elements/modelx.dart' |
| 17 show BaseFunctionElementX, ClassElementX, ElementX; | 17 show BaseFunctionElementX, ClassElementX, ElementX; |
| 18 import 'elements/visitor.dart' show ElementVisitor; | 18 import 'elements/visitor.dart' show ElementVisitor; |
| 19 import 'js_backend/js_backend.dart' show JavaScriptBackend; | 19 import 'js_backend/js_backend.dart' show JavaScriptBackend; |
| 20 import 'resolution/tree_elements.dart' show TreeElements; | 20 import 'resolution/tree_elements.dart' show TreeElements; |
| 21 import 'package:front_end/src/fasta/scanner.dart' show Token; | 21 import 'package:front_end/src/fasta/scanner.dart' show Token; |
| 22 import 'tree/tree.dart'; | 22 import 'tree/tree.dart'; |
| 23 import 'util/util.dart'; | 23 import 'util/util.dart'; |
| 24 import 'world.dart' show ClosedWorldRefiner; | 24 import 'world.dart' show ClosedWorldRefiner; |
| 25 | 25 |
| 26 abstract class ClosureClassMaps { | 26 /// Where T is ir.Node or Node. |
| 27 ClosureClassMap getMemberMap(MemberEntity member); | 27 // TODO(efortuna): Rename this class. |
| 28 ClosureClassMap getLocalFunctionMap(Local localFunction); | 28 abstract class ClosureClassMaps<T> { |
|
Johnni Winther
2017/05/31 12:39:51
I'd prefer if these new methods (eventually) are o
| |
| 29 /// Currently, closures are rewritten in the form of classes that | |
|
Siggi Cherem (dart-lang)
2017/05/31 20:44:38
Small suggestion: to make the docs a bit more comp
| |
| 30 /// have fields to control the redirection and editing of variables that are | |
| 31 /// "captured" inside a scope (declared in an outer scope but used in an | |
| 32 /// inside scope). | |
| 33 ClassEntity getClosureClassEntity(Local member); | |
|
Siggi Cherem (dart-lang)
2017/05/31 20:44:39
minor naming ideas: these are totally optional, so
| |
| 34 | |
| 35 // TODO(efortuna): This should probably be renamed to something like | |
| 36 // forEachCapturedVariable or something that's less implementation specific. | |
|
Siggi Cherem (dart-lang)
2017/05/31 20:44:39
+1 I like that, also possible `forEachCapturedVari
| |
| 37 void forEachClosureClassFieldEntity(Local entity, void f(FieldEntity field)); | |
| 38 | |
| 39 /// Closures are rewritten as classes with fields representing the local | |
|
Siggi Cherem (dart-lang)
2017/05/31 20:44:39
Another though - we can explore moving the docs up
| |
| 40 /// variables that the closure has "captured. Return the actual local | |
|
Johnni Winther
2017/05/31 12:39:51
"captured -> captured
| |
| 41 /// variable that a particular closure field is emulating. | |
| 42 Local getLocalVarForClosureField(Local member, FieldEntity field); | |
| 43 | |
| 44 /// The method representing calling the closure to execute, provided the | |
|
Johnni Winther
2017/05/31 12:39:51
Maybe rephrase as:
The function that implements t
| |
| 45 /// original local entity. | |
| 46 FunctionEntity getCallEntity(Local member); | |
| 47 | |
| 48 /// Accessor to the local environment in which a particular closure node is | |
| 49 /// executed. This will encapsulate the value of any variables that have been | |
| 50 /// scoped into this context from outside. | |
|
Siggi Cherem (dart-lang)
2017/05/31 20:44:39
is "scoped" the same as captured here? or somethin
| |
| 51 Local getExecutableContext(T node); | |
|
Siggi Cherem (dart-lang)
2017/05/31 20:44:38
Open question: I wonder if we should introduce thi
| |
| 52 | |
| 53 // TODO(efortuna): Finish removing this method by exposing remining info in | |
|
Siggi Cherem (dart-lang)
2017/05/31 20:44:39
remining => remaining
| |
| 54 // this interface. | |
| 55 ClosureClassMap getMemberMap(MemberElement member); | |
| 56 | |
| 57 /// Look up information about what variables are have been mutated that are | |
|
Johnni Winther
2017/05/31 12:39:51
are have ... inside the -> have been mutated insid
| |
| 58 /// used inside the scope of [node]. | |
| 59 CapturedVariableInfo getCapturedVariableInfo(T node); | |
| 29 } | 60 } |
| 30 | 61 |
| 31 class ClosureTask extends CompilerTask implements ClosureClassMaps { | 62 class ClosureTask extends CompilerTask implements ClosureClassMaps<Node> { |
| 32 Map<Element, ClosureClassMap> _closureMappingCache = | 63 Map<Element, ClosureClassMap> _closureMappingCache = |
| 33 <Element, ClosureClassMap>{}; | 64 <Element, ClosureClassMap>{}; |
| 65 Map<Node, Local> _executableContextCache = <Node, Local>{}; | |
| 66 Map<Node, ClosureScope> _closureScopeCache = <Node, ClosureScope>{}; | |
| 34 Compiler compiler; | 67 Compiler compiler; |
| 35 ClosureTask(Compiler compiler) | 68 ClosureTask(Compiler compiler) |
| 36 : compiler = compiler, | 69 : compiler = compiler, |
| 37 super(compiler.measurer); | 70 super(compiler.measurer); |
| 38 | 71 |
| 39 String get name => "Closure Simplifier"; | 72 String get name => "Closure Simplifier"; |
| 40 | 73 |
| 41 DiagnosticReporter get reporter => compiler.reporter; | 74 DiagnosticReporter get reporter => compiler.reporter; |
| 42 | 75 |
| 76 CapturedVariableInfo getCapturedVariableInfo(Node node) { | |
| 77 ClosureScope scopeData = _closureScopeCache[node]; | |
| 78 if (scopeData == null) return new CapturedVariableInfo(); | |
|
Siggi Cherem (dart-lang)
2017/06/01 15:40:30
consider adding a const constructor to CapturedVar
| |
| 79 return scopeData; | |
| 80 } | |
| 81 | |
| 43 ClosureClassMap getMemberMap(MemberElement member) { | 82 ClosureClassMap getMemberMap(MemberElement member) { |
| 44 return getClosureToClassMapping(member.resolvedAst); | 83 return getClosureToClassMapping(member.resolvedAst); |
| 45 } | 84 } |
| 46 | 85 |
| 47 ClosureClassMap getLocalFunctionMap(LocalFunctionElement localFunction) { | 86 ClosureClassMap _getLocalFunctionMap(LocalFunctionElement localFunction) { |
| 48 return getClosureToClassMapping(localFunction.resolvedAst); | 87 return getClosureToClassMapping(localFunction.resolvedAst); |
| 49 } | 88 } |
| 50 | 89 |
| 90 void forEachClosureClassFieldEntity( | |
| 91 LocalFunctionElement entity, void f(FieldEntity field)) { | |
| 92 ClosureClassElement classEntity = getClosureClassEntity(entity); | |
| 93 classEntity.closureFields.forEach(f); | |
| 94 } | |
| 95 | |
| 96 Local getExecutableContext(Node node) { | |
| 97 return _executableContextCache[node]; | |
| 98 } | |
| 99 | |
| 100 Local getLocalVarForClosureField( | |
| 101 LocalFunctionElement member, FieldEntity field) { | |
| 102 ClosureClassMap memberMap = _getLocalFunctionMap(member); | |
| 103 assert(memberMap != null); | |
| 104 assert(memberMap.closureClassElement != null); | |
| 105 return memberMap.getLocalVariableForClosureField(field); | |
| 106 } | |
| 107 | |
| 108 ClassEntity getClosureClassEntity(LocalFunctionElement member) { | |
| 109 ClosureClassMap memberMap = _getLocalFunctionMap(member); | |
| 110 assert(memberMap != null); | |
| 111 assert(memberMap.closureClassElement != null); | |
| 112 return memberMap.closureClassElement; | |
| 113 } | |
| 114 | |
| 115 FunctionEntity getCallEntity(LocalFunctionElement member) { | |
| 116 ClosureClassMap memberMap = _getLocalFunctionMap(member); | |
| 117 assert(memberMap != null); | |
| 118 assert(memberMap.closureClassElement != null); | |
| 119 return memberMap.callElement; | |
| 120 } | |
| 121 | |
| 51 /// Returns the [ClosureClassMap] computed for [resolvedAst]. | 122 /// Returns the [ClosureClassMap] computed for [resolvedAst]. |
| 52 ClosureClassMap getClosureToClassMapping(ResolvedAst resolvedAst) { | 123 ClosureClassMap getClosureToClassMapping(ResolvedAst resolvedAst) { |
| 53 return measure(() { | 124 return measure(() { |
| 54 Element element = resolvedAst.element; | 125 Element element = resolvedAst.element; |
| 55 if (element.isGenerativeConstructorBody) { | 126 if (element.isGenerativeConstructorBody) { |
| 56 ConstructorBodyElement constructorBody = element; | 127 ConstructorBodyElement constructorBody = element; |
| 57 element = constructorBody.constructor; | 128 element = constructorBody.constructor; |
| 58 } | 129 } |
| 59 ClosureClassMap closureClassMap = _closureMappingCache[element]; | 130 ClosureClassMap closureClassMap = _closureMappingCache[element]; |
| 60 assert(invariant(resolvedAst.element, closureClassMap != null, | 131 assert(invariant(resolvedAst.element, closureClassMap != null, |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 87 if (cached != null) return cached; | 158 if (cached != null) return cached; |
| 88 if (resolvedAst.kind != ResolvedAstKind.PARSED) { | 159 if (resolvedAst.kind != ResolvedAstKind.PARSED) { |
| 89 return _closureMappingCache[element] = | 160 return _closureMappingCache[element] = |
| 90 new ClosureClassMap(null, null, null, new ThisLocal(element)); | 161 new ClosureClassMap(null, null, null, new ThisLocal(element)); |
| 91 } | 162 } |
| 92 return reporter.withCurrentElement(element.implementation, () { | 163 return reporter.withCurrentElement(element.implementation, () { |
| 93 Node node = resolvedAst.node; | 164 Node node = resolvedAst.node; |
| 94 TreeElements elements = resolvedAst.elements; | 165 TreeElements elements = resolvedAst.elements; |
| 95 | 166 |
| 96 ClosureTranslator translator = new ClosureTranslator( | 167 ClosureTranslator translator = new ClosureTranslator( |
| 97 compiler, closedWorldRefiner, elements, _closureMappingCache); | 168 compiler, |
| 169 closedWorldRefiner, | |
| 170 elements, | |
| 171 _closureMappingCache, | |
| 172 _executableContextCache, | |
| 173 _closureScopeCache); | |
| 98 | 174 |
| 99 // The translator will store the computed closure-mappings inside the | 175 // The translator will store the computed closure-mappings inside the |
| 100 // cache. One for given node and one for each nested closure. | 176 // cache. One for given node and one for each nested closure. |
| 101 if (node is FunctionExpression) { | 177 if (node is FunctionExpression) { |
| 102 translator.translateFunction(element, node); | 178 translator.translateFunction(element, node); |
| 103 } else if (element.isSynthesized) { | 179 } else if (element.isSynthesized) { |
| 104 reporter.internalError( | 180 reporter.internalError( |
| 105 element, "Unexpected synthesized element: $element"); | 181 element, "Unexpected synthesized element: $element"); |
| 106 _closureMappingCache[element] = | 182 _closureMappingCache[element] = |
| 107 new ClosureClassMap(null, null, null, new ThisLocal(element)); | 183 new ClosureClassMap(null, null, null, new ThisLocal(element)); |
| (...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 404 ResolvedAst get resolvedAst { | 480 ResolvedAst get resolvedAst { |
| 405 return new ParsedResolvedAst(this, node, node.body, treeElements, | 481 return new ParsedResolvedAst(this, node, node.body, treeElements, |
| 406 expression.compilationUnit.script.resourceUri); | 482 expression.compilationUnit.script.resourceUri); |
| 407 } | 483 } |
| 408 | 484 |
| 409 accept(ElementVisitor visitor, arg) { | 485 accept(ElementVisitor visitor, arg) { |
| 410 return visitor.visitMethodElement(this, arg); | 486 return visitor.visitMethodElement(this, arg); |
| 411 } | 487 } |
| 412 } | 488 } |
| 413 | 489 |
| 490 /// Interface external classes can use to query information about what variables | |
| 491 /// are mutated inside a scope. | |
| 492 class CapturedVariableInfo { | |
| 493 bool hasCapturedVariables() => false; | |
|
Siggi Cherem (dart-lang)
2017/05/31 20:44:39
nit: turn these two into getters:
bool get hasCap
| |
| 494 bool hasBoxedLoopVariables() => false; | |
| 495 | |
| 496 /// True if the specified variable has been mutated inside the scope of this | |
| 497 /// closure. | |
| 498 bool isCaptured(Local variable) => false; | |
| 499 void forEachCapturedVariable(f(Entity from, Entity to)) {} | |
| 500 } | |
| 501 | |
| 414 // The box-element for a scope, and the captured variables that need to be | 502 // The box-element for a scope, and the captured variables that need to be |
| 415 // stored in the box. | 503 // stored in the box. |
| 416 class ClosureScope { | 504 class ClosureScope implements CapturedVariableInfo { |
| 417 final BoxLocal boxElement; | 505 final BoxLocal boxElement; |
| 418 final Map<Local, BoxFieldElement> capturedVariables; | 506 final Map<Local, BoxFieldElement> capturedVariables; |
| 419 | 507 |
| 420 // If the scope is attached to a [For] contains the variables that are | 508 // If the scope is attached to a [For] contains the variables that are |
| 421 // declared in the initializer of the [For] and that need to be boxed. | 509 // declared in the initializer of the [For] and that need to be boxed. |
| 422 // Otherwise contains the empty List. | 510 // Otherwise contains the empty List. |
| 423 List<VariableElement> boxedLoopVariables = const <VariableElement>[]; | 511 List<VariableElement> boxedLoopVariables = const <VariableElement>[]; |
| 424 | 512 |
| 425 ClosureScope(this.boxElement, this.capturedVariables); | 513 ClosureScope(this.boxElement, this.capturedVariables); |
| 426 | 514 |
| 515 bool hasCapturedVariables() => capturedVariables.keys.isNotEmpty; | |
| 516 | |
| 427 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty; | 517 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty; |
| 428 | 518 |
| 429 bool isCapturedVariable(VariableElement variable) { | 519 bool isCaptured(VariableElement variable) { |
|
Johnni Winther
2017/05/31 12:39:51
[VariableElement] -> [LocalVariableElement] to mak
| |
| 430 return capturedVariables.containsKey(variable); | 520 return capturedVariables.containsKey(variable); |
| 431 } | 521 } |
| 432 | 522 |
| 433 void forEachCapturedVariable( | 523 void forEachCapturedVariable( |
| 434 f(LocalVariableElement variable, BoxFieldElement boxField)) { | 524 f(LocalVariableElement variable, BoxFieldElement boxField)) { |
| 435 capturedVariables.forEach(f); | 525 capturedVariables.forEach(f); |
| 436 } | 526 } |
| 437 | 527 |
| 438 String toString() { | 528 String toString() { |
| 439 String separator = ''; | 529 String separator = ''; |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 584 Set<LocalVariableElement> mutatedVariables = new Set<LocalVariableElement>(); | 674 Set<LocalVariableElement> mutatedVariables = new Set<LocalVariableElement>(); |
| 585 | 675 |
| 586 MemberElement outermostElement; | 676 MemberElement outermostElement; |
| 587 ExecutableElement executableContext; | 677 ExecutableElement executableContext; |
| 588 | 678 |
| 589 // The closureData of the currentFunctionElement. | 679 // The closureData of the currentFunctionElement. |
| 590 ClosureClassMap closureData; | 680 ClosureClassMap closureData; |
| 591 | 681 |
| 592 bool insideClosure = false; | 682 bool insideClosure = false; |
| 593 | 683 |
| 594 ClosureTranslator(this.compiler, this.closedWorldRefiner, this.elements, | 684 Map<Node, Local> executableContextCache; |
| 595 this.closureMappingCache); | 685 Map<Node, ClosureScopeCache> closureScopeCache; |
| 686 | |
| 687 ClosureTranslator( | |
| 688 this.compiler, | |
| 689 this.closedWorldRefiner, | |
| 690 this.elements, | |
| 691 this.closureMappingCache, | |
| 692 this.executableContextCache, | |
| 693 this.closureScopeCache); | |
| 596 | 694 |
| 597 DiagnosticReporter get reporter => compiler.reporter; | 695 DiagnosticReporter get reporter => compiler.reporter; |
| 598 | 696 |
| 599 /// Generate a unique name for the [id]th closure field, with proposed name | 697 /// Generate a unique name for the [id]th closure field, with proposed name |
| 600 /// [name]. | 698 /// [name]. |
| 601 /// | 699 /// |
| 602 /// The result is used as the name of [ClosureFieldElement]s, and must | 700 /// The result is used as the name of [ClosureFieldElement]s, and must |
| 603 /// therefore be unique to avoid breaking an invariant in the element model | 701 /// therefore be unique to avoid breaking an invariant in the element model |
| 604 /// (classes cannot declare multiple fields with the same name). | 702 /// (classes cannot declare multiple fields with the same name). |
| 605 /// | 703 /// |
| (...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 969 | 1067 |
| 970 for (LocalVariableElement variable in scopeVariables) { | 1068 for (LocalVariableElement variable in scopeVariables) { |
| 971 // No need to box non-assignable elements. | 1069 // No need to box non-assignable elements. |
| 972 if (!variable.isAssignable) continue; | 1070 if (!variable.isAssignable) continue; |
| 973 if (!mutatedVariables.contains(variable)) continue; | 1071 if (!mutatedVariables.contains(variable)) continue; |
| 974 boxCapturedVariable(variable); | 1072 boxCapturedVariable(variable); |
| 975 } | 1073 } |
| 976 if (!scopeMapping.isEmpty) { | 1074 if (!scopeMapping.isEmpty) { |
| 977 ClosureScope scope = new ClosureScope(box, scopeMapping); | 1075 ClosureScope scope = new ClosureScope(box, scopeMapping); |
| 978 closureData.capturingScopes[node] = scope; | 1076 closureData.capturingScopes[node] = scope; |
| 1077 assert(executableContextCache[node] == null); | |
| 1078 executableContextCache[node] = box; | |
| 1079 assert(closureScopeCache[node] == null); | |
| 1080 closureScopeCache[node] = scope; | |
| 979 } | 1081 } |
| 980 } | 1082 } |
| 981 | 1083 |
| 982 void inNewScope(Node node, Function action) { | 1084 void inNewScope(Node node, Function action) { |
| 983 List<LocalVariableElement> oldScopeVariables = scopeVariables; | 1085 List<LocalVariableElement> oldScopeVariables = scopeVariables; |
| 984 scopeVariables = <LocalVariableElement>[]; | 1086 scopeVariables = <LocalVariableElement>[]; |
| 985 action(); | 1087 action(); |
| 986 attachCapturedScopeVariables(node); | 1088 attachCapturedScopeVariables(node); |
| 987 mutatedVariables.removeAll(scopeVariables); | 1089 mutatedVariables.removeAll(scopeVariables); |
| 988 scopeVariables = oldScopeVariables; | 1090 scopeVariables = oldScopeVariables; |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1235 /// | 1337 /// |
| 1236 /// Move the below classes to a JS model eventually. | 1338 /// Move the below classes to a JS model eventually. |
| 1237 /// | 1339 /// |
| 1238 abstract class JSEntity implements Entity { | 1340 abstract class JSEntity implements Entity { |
| 1239 Entity get declaredEntity; | 1341 Entity get declaredEntity; |
| 1240 } | 1342 } |
| 1241 | 1343 |
| 1242 abstract class PrivatelyNamedJSEntity implements JSEntity { | 1344 abstract class PrivatelyNamedJSEntity implements JSEntity { |
| 1243 Entity get rootOfScope; | 1345 Entity get rootOfScope; |
| 1244 } | 1346 } |
| OLD | NEW |