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 |