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 import 'common/names.dart' show Identifiers; | 5 import 'common/names.dart' show Identifiers; |
6 import 'common/resolution.dart' show ParsingContext, Resolution; | 6 import 'common/resolution.dart' show ParsingContext, Resolution; |
7 import 'common/tasks.dart' show CompilerTask, Measurer; | 7 import 'common/tasks.dart' show CompilerTask, Measurer; |
8 import 'common.dart'; | 8 import 'common.dart'; |
9 import 'compiler.dart' show Compiler; | 9 import 'compiler.dart' show Compiler; |
10 import 'constants/expressions.dart'; | 10 import 'constants/expressions.dart'; |
(...skipping 25 matching lines...) Expand all Loading... |
36 | 36 |
37 /// Class that provides information for how closures are rewritten/represented | 37 /// Class that provides information for how closures are rewritten/represented |
38 /// to preserve Dart semantics when compiled to JavaScript. Given a particular | 38 /// to preserve Dart semantics when compiled to JavaScript. Given a particular |
39 /// node to look up, it returns a information about the internal representation | 39 /// node to look up, it returns a information about the internal representation |
40 /// of how closure conversion is implemented. T is an ir.Node or Node. | 40 /// of how closure conversion is implemented. T is an ir.Node or Node. |
41 abstract class ClosureDataLookup<T> { | 41 abstract class ClosureDataLookup<T> { |
42 /// Look up information about the variables that have been mutated and are | 42 /// Look up information about the variables that have been mutated and are |
43 /// used inside the scope of [node]. | 43 /// used inside the scope of [node]. |
44 // TODO(johnniwinther): Split this up into two functions, one for members and | 44 // TODO(johnniwinther): Split this up into two functions, one for members and |
45 // one for local functions. | 45 // one for local functions. |
| 46 ScopeInfo getScopeInfo(covariant Entity member); |
| 47 |
| 48 /// This returns the same information as ScopeInfo, but can be called in |
| 49 /// situations when you are sure you are dealing with a closure specifically. |
46 ClosureRepresentationInfo getClosureRepresentationInfo( | 50 ClosureRepresentationInfo getClosureRepresentationInfo( |
47 covariant Entity member); | 51 covariant Entity member); |
48 | 52 |
49 /// Look up information about a loop, in case any variables it declares need | 53 /// Look up information about a loop, in case any variables it declares need |
50 /// to be boxed/snapshotted. | 54 /// to be boxed/snapshotted. |
51 LoopClosureRepresentationInfo getClosureRepresentationInfoForLoop(T loopNode); | 55 LoopClosureRepresentationInfo getClosureRepresentationInfoForLoop(T loopNode); |
52 | 56 |
53 /// Accessor to the information about closures that the SSA builder will use. | 57 /// Accessor to the information about closures that the SSA builder will use. |
54 ClosureAnalysisInfo getClosureAnalysisInfo(T node); | 58 ClosureAnalysisInfo getClosureAnalysisInfo(T node); |
55 } | 59 } |
56 | 60 |
| 61 /// Class that represents one level of scoping information, whether this scope |
| 62 /// is a closure or not. This is specifically used to store information |
| 63 /// about the usage of variables in try or sync blocks, because they need to be |
| 64 /// boxed. |
| 65 /// |
| 66 /// Variables that are used in a try must be treated as boxed because the |
| 67 /// control flow can be non-linear. Also parameters to a `sync*` generator must |
| 68 /// be boxed, because of the way we rewrite sync* functions. See also comments |
| 69 /// in [ClosureClassMap.useLocal]. |
| 70 class ScopeInfo { |
| 71 const ScopeInfo(); |
| 72 |
| 73 /// Returns true if this [variable] is used inside a `try` block or a `sync*` |
| 74 /// generator (this is important to know because boxing/redirection needs to |
| 75 /// happen for those local variables). |
| 76 /// |
| 77 /// Variables that are used in a try must be treated as boxed because the |
| 78 /// control flow can be non-linear. |
| 79 /// |
| 80 /// Also parameters to a `sync*` generator must be boxed, because of the way |
| 81 /// we rewrite sync* functions. See also comments in |
| 82 /// [ClosureClassMap.useLocal]. |
| 83 bool variableIsUsedInTryOrSync(Local variable) => false; |
| 84 |
| 85 /// Convenience reference pointer to the element representing `this`. |
| 86 /// If this scope is not in an instance member, it will be null. |
| 87 Local get thisLocal => null; |
| 88 } |
| 89 |
57 /// Class that provides a black-box interface to information gleaned from | 90 /// Class that provides a black-box interface to information gleaned from |
58 /// analyzing a closure's characteristics, most commonly used to influence how | 91 /// analyzing a closure's characteristics, most commonly used to influence how |
59 /// code should be generated in SSA builder stage. | 92 /// code should be generated in SSA builder stage. |
60 class ClosureAnalysisInfo { | 93 class ClosureAnalysisInfo { |
61 const ClosureAnalysisInfo(); | 94 const ClosureAnalysisInfo(); |
62 | 95 |
63 /// If true, this closure accesses a variable that was defined in an outside | 96 /// If true, this closure accesses a variable that was defined in an outside |
64 /// scope and this variable gets modified at some point (sometimes we say that | 97 /// scope and this variable gets modified at some point (sometimes we say that |
65 /// variable has been "captured"). In this situation, access to this variable | 98 /// variable has been "captured"). In this situation, access to this variable |
66 /// is controlled via a wrapper (box) so that updates to this variable | 99 /// is controlled via a wrapper (box) so that updates to this variable |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 /// var foo = new FooClosure(1); | 164 /// var foo = new FooClosure(1); |
132 /// foo.call(2); | 165 /// foo.call(2); |
133 /// | 166 /// |
134 /// if `y` is modified elsewhere within its scope, accesses to y anywhere in the | 167 /// if `y` is modified elsewhere within its scope, accesses to y anywhere in the |
135 /// code will be controlled via a box object. | 168 /// code will be controlled via a box object. |
136 /// | 169 /// |
137 /// Because in these examples `y` was declared in some other, outer scope, but | 170 /// Because in these examples `y` was declared in some other, outer scope, but |
138 /// used in the inner scope of this closure, we say `y` is a "captured" | 171 /// used in the inner scope of this closure, we say `y` is a "captured" |
139 /// variable. | 172 /// variable. |
140 /// TODO(efortuna): Make interface simpler in subsequent refactorings. | 173 /// TODO(efortuna): Make interface simpler in subsequent refactorings. |
141 class ClosureRepresentationInfo { | 174 class ClosureRepresentationInfo extends ScopeInfo { |
142 const ClosureRepresentationInfo(); | 175 const ClosureRepresentationInfo(); |
143 | 176 |
144 /// The original local function before any translation. | 177 /// The original local function before any translation. |
145 /// | 178 /// |
146 /// Will be null for methods. | 179 /// Will be null for methods. |
147 Local get closureEntity => null; | 180 Local get closureEntity => null; |
148 | 181 |
149 /// The entity for the class used to represent the rewritten closure in the | 182 /// The entity for the class used to represent the rewritten closure in the |
150 /// emitted JavaScript. | 183 /// emitted JavaScript. |
151 /// | 184 /// |
152 /// Closures are rewritten in the form of classes that have fields to control | 185 /// Closures are rewritten in the form of classes that have fields to control |
153 /// the redirection and editing of captured variables. | 186 /// the redirection and editing of captured variables. |
154 ClassEntity get closureClassEntity => null; | 187 ClassEntity get closureClassEntity => null; |
155 | 188 |
156 /// The function that implements the [local] function as a `call` method on | 189 /// The function that implements the [local] function as a `call` method on |
157 /// the closure class. | 190 /// the closure class. |
158 FunctionEntity get callMethod => null; | 191 FunctionEntity get callMethod => null; |
159 | 192 |
160 /// As shown in the example in the comments at the top of this class, we | 193 /// As shown in the example in the comments at the top of this class, we |
161 /// create fields in the closure class for each captured variable. This is an | 194 /// create fields in the closure class for each captured variable. This is an |
162 /// accessor to that set of fields. | 195 /// accessor to that set of fields. |
163 List<Local> get createdFieldEntities => const <Local>[]; | 196 List<Local> get createdFieldEntities => const <Local>[]; |
164 | 197 |
165 /// Convenience reference pointer to the element representing `this`. | |
166 /// It is only set for instance-members. | |
167 Local get thisLocal => null; | |
168 | |
169 /// Convenience pointer to the field entity representation in the closure | 198 /// Convenience pointer to the field entity representation in the closure |
170 /// class of the element representing `this`. | 199 /// class of the element representing `this`. |
171 FieldEntity get thisFieldEntity => null; | 200 FieldEntity get thisFieldEntity => null; |
172 | 201 |
173 /// Returns true if this [variable] is used inside a `try` block or a `sync*` | |
174 /// generator (this is important to know because boxing/redirection needs to | |
175 /// happen for those local variables). | |
176 /// | |
177 /// Variables that are used in a try must be treated as boxed because the | |
178 /// control flow can be non-linear. | |
179 /// | |
180 /// Also parameters to a `sync*` generator must be boxed, because of the way | |
181 /// we rewrite sync* functions. See also comments in | |
182 /// [ClosureClassMap.useLocal]. | |
183 bool variableIsUsedInTryOrSync(Local variable) => false; | |
184 | |
185 /// Loop through every variable that has been captured in this closure. This | 202 /// Loop through every variable that has been captured in this closure. This |
186 /// consists of all the free variables (variables captured *just* in this | 203 /// consists of all the free variables (variables captured *just* in this |
187 /// closure) and all variables captured in nested scopes that we may be | 204 /// closure) and all variables captured in nested scopes that we may be |
188 /// capturing as well. These nested scopes hold "boxes" to hold the executable | 205 /// capturing as well. These nested scopes hold "boxes" to hold the executable |
189 /// context for that scope. | 206 /// context for that scope. |
190 void forEachCapturedVariable(f(Local from, FieldEntity to)) {} | 207 void forEachCapturedVariable(f(Local from, FieldEntity to)) {} |
191 | 208 |
192 /// Loop through each variable that has been boxed in this closure class. Only | 209 /// Loop through each variable that has been boxed in this closure class. Only |
193 /// captured variables that are mutated need to be "boxed" (which basically | 210 /// captured variables that are mutated need to be "boxed" (which basically |
194 /// puts a thin layer between updates and reads to this variable to ensure | 211 /// puts a thin layer between updates and reads to this variable to ensure |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
226 void convertClosures(Iterable<MemberEntity> processedEntities, | 243 void convertClosures(Iterable<MemberEntity> processedEntities, |
227 ClosedWorldRefiner closedWorldRefiner) { | 244 ClosedWorldRefiner closedWorldRefiner) { |
228 createClosureClasses(closedWorldRefiner); | 245 createClosureClasses(closedWorldRefiner); |
229 } | 246 } |
230 | 247 |
231 ClosureAnalysisInfo getClosureAnalysisInfo(Node node) { | 248 ClosureAnalysisInfo getClosureAnalysisInfo(Node node) { |
232 var value = _closureInfoMap[node]; | 249 var value = _closureInfoMap[node]; |
233 return value == null ? const ClosureAnalysisInfo() : value; | 250 return value == null ? const ClosureAnalysisInfo() : value; |
234 } | 251 } |
235 | 252 |
| 253 ScopeInfo getScopeInfo(Element member) { |
| 254 return getClosureToClassMapping(member); |
| 255 } |
| 256 |
236 ClosureRepresentationInfo getClosureRepresentationInfo(Element member) { | 257 ClosureRepresentationInfo getClosureRepresentationInfo(Element member) { |
237 return getClosureToClassMapping(member); | 258 return getClosureToClassMapping(member); |
238 } | 259 } |
239 | 260 |
240 LoopClosureRepresentationInfo getClosureRepresentationInfoForLoop( | 261 LoopClosureRepresentationInfo getClosureRepresentationInfoForLoop( |
241 Node loopNode) { | 262 Node loopNode) { |
242 var value = _closureInfoMap[loopNode]; | 263 var value = _closureInfoMap[loopNode]; |
243 return value == null ? const LoopClosureRepresentationInfo() : value; | 264 return value == null ? const LoopClosureRepresentationInfo() : value; |
244 } | 265 } |
245 | 266 |
(...skipping 437 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
683 /// Maps free locals, arguments, function elements, and box locals to | 704 /// Maps free locals, arguments, function elements, and box locals to |
684 /// their locations. | 705 /// their locations. |
685 final Map<Local, FieldEntity> freeVariableMap = new Map<Local, FieldEntity>(); | 706 final Map<Local, FieldEntity> freeVariableMap = new Map<Local, FieldEntity>(); |
686 | 707 |
687 /// Maps [Loop] and [FunctionExpression] nodes to their [ClosureScope] which | 708 /// Maps [Loop] and [FunctionExpression] nodes to their [ClosureScope] which |
688 /// contains their box and the captured variables that are stored in the box. | 709 /// contains their box and the captured variables that are stored in the box. |
689 /// This map will be empty if the method/closure of this [ClosureData] does | 710 /// This map will be empty if the method/closure of this [ClosureData] does |
690 /// not contain any nested closure. | 711 /// not contain any nested closure. |
691 final Map<Node, ClosureScope> capturingScopes = new Map<Node, ClosureScope>(); | 712 final Map<Node, ClosureScope> capturingScopes = new Map<Node, ClosureScope>(); |
692 | 713 |
| 714 /// Set of [variable]s referenced in this scope that are used inside a |
| 715 /// `try` block or a `sync*` generator (this is important to know because |
| 716 /// boxing/redirection needs to happen for those local variables). |
| 717 /// |
693 /// Variables that are used in a try must be treated as boxed because the | 718 /// Variables that are used in a try must be treated as boxed because the |
694 /// control flow can be non-linear. | 719 /// control flow can be non-linear. |
695 /// | 720 /// |
696 /// Also parameters to a `sync*` generator must be boxed, because of the way | 721 /// Also parameters to a `sync*` generator must be boxed, because of the way |
697 /// we rewrite sync* functions. See also comments in [useLocal]. | 722 /// we rewrite sync* functions. See also comments in [useLocal]. |
698 // TODO(johnniwinther): Add variables to this only if the variable is mutated. | 723 // TODO(johnniwinther): Add variables to this only if the variable is mutated. |
699 final Set<Local> variablesUsedInTryOrGenerator = new Set<Local>(); | 724 final Set<Local> variablesUsedInTryOrSync = new Set<Local>(); |
700 | 725 |
701 ClosureClassMap(this.closureEntity, this.closureClassEntity, this.callMethod, | 726 ClosureClassMap(this.closureEntity, this.closureClassEntity, this.callMethod, |
702 this.thisLocal); | 727 this.thisLocal); |
703 | 728 |
704 List<Local> get createdFieldEntities { | 729 List<Local> get createdFieldEntities { |
705 List<Local> fields = <Local>[]; | 730 List<Local> fields = <Local>[]; |
706 if (closureClassEntity == null) return const <Local>[]; | 731 if (closureClassEntity == null) return const <Local>[]; |
707 closureClassEntity.closureFields.forEach((field) { | 732 closureClassEntity.closureFields.forEach((field) { |
708 fields.add(field.local); | 733 fields.add(field.local); |
709 }); | 734 }); |
(...skipping 11 matching lines...) Expand all Loading... |
721 return freeVariableMap.containsKey(element); | 746 return freeVariableMap.containsKey(element); |
722 } | 747 } |
723 | 748 |
724 void forEachFreeVariable(f(Local variable, FieldEntity field)) { | 749 void forEachFreeVariable(f(Local variable, FieldEntity field)) { |
725 freeVariableMap.forEach(f); | 750 freeVariableMap.forEach(f); |
726 } | 751 } |
727 | 752 |
728 FieldEntity get thisFieldEntity => freeVariableMap[thisLocal]; | 753 FieldEntity get thisFieldEntity => freeVariableMap[thisLocal]; |
729 | 754 |
730 bool variableIsUsedInTryOrSync(Local variable) => | 755 bool variableIsUsedInTryOrSync(Local variable) => |
731 variablesUsedInTryOrGenerator.contains(variable); | 756 variablesUsedInTryOrSync.contains(variable); |
732 | 757 |
733 Local getLocalVariableForClosureField(ClosureFieldElement field) { | 758 Local getLocalVariableForClosureField(ClosureFieldElement field) { |
734 return field.local; | 759 return field.local; |
735 } | 760 } |
736 | 761 |
737 bool get isClosure => closureEntity != null; | 762 bool get isClosure => closureEntity != null; |
738 | 763 |
739 bool capturingScopesBox(Local variable) { | 764 bool capturingScopesBox(Local variable) { |
740 return capturingScopes.values.any((scope) { | 765 return capturingScopes.values.any((scope) { |
741 return scope.boxedLoopVariables.contains(variable); | 766 return scope.boxedLoopVariables.contains(variable); |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
962 | 987 |
963 if (insideClosure && !inCurrentContext(variable)) { | 988 if (insideClosure && !inCurrentContext(variable)) { |
964 closureData.addFreeVariable(variable); | 989 closureData.addFreeVariable(variable); |
965 } else if (inTryStatement) { | 990 } else if (inTryStatement) { |
966 // Don't mark the this-element or a self-reference. This would complicate | 991 // Don't mark the this-element or a self-reference. This would complicate |
967 // things in the builder. | 992 // things in the builder. |
968 // Note that nested (named) functions are immutable. | 993 // Note that nested (named) functions are immutable. |
969 if (variable != closureData.thisLocal && | 994 if (variable != closureData.thisLocal && |
970 variable != closureData.closureEntity && | 995 variable != closureData.closureEntity && |
971 variable is! TypeVariableLocal) { | 996 variable is! TypeVariableLocal) { |
972 closureData.variablesUsedInTryOrGenerator.add(variable); | 997 closureData.variablesUsedInTryOrSync.add(variable); |
973 } | 998 } |
974 } else if (variable is LocalParameterElement && | 999 } else if (variable is LocalParameterElement && |
975 variable.functionDeclaration.asyncMarker == AsyncMarker.SYNC_STAR) { | 1000 variable.functionDeclaration.asyncMarker == AsyncMarker.SYNC_STAR) { |
976 // Parameters in a sync* function are shared between each Iterator created | 1001 // Parameters in a sync* function are shared between each Iterator created |
977 // by the Iterable returned by the function, therefore they must be boxed. | 1002 // by the Iterable returned by the function, therefore they must be boxed. |
978 closureData.variablesUsedInTryOrGenerator.add(variable); | 1003 closureData.variablesUsedInTryOrSync.add(variable); |
979 } | 1004 } |
980 } | 1005 } |
981 | 1006 |
982 void useTypeVariableAsLocal(ResolutionTypeVariableType typeVariable) { | 1007 void useTypeVariableAsLocal(ResolutionTypeVariableType typeVariable) { |
983 useLocal(new TypeVariableLocal( | 1008 useLocal(new TypeVariableLocal( |
984 typeVariable, outermostElement, outermostElement.memberContext)); | 1009 typeVariable, outermostElement, outermostElement.memberContext)); |
985 } | 1010 } |
986 | 1011 |
987 void declareLocal(LocalVariableElement element) { | 1012 void declareLocal(LocalVariableElement element) { |
988 scopeVariables.add(element); | 1013 scopeVariables.add(element); |
(...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1468 /// | 1493 /// |
1469 /// Move the below classes to a JS model eventually. | 1494 /// Move the below classes to a JS model eventually. |
1470 /// | 1495 /// |
1471 abstract class JSEntity implements MemberEntity { | 1496 abstract class JSEntity implements MemberEntity { |
1472 Local get declaredEntity; | 1497 Local get declaredEntity; |
1473 } | 1498 } |
1474 | 1499 |
1475 abstract class PrivatelyNamedJSEntity implements JSEntity { | 1500 abstract class PrivatelyNamedJSEntity implements JSEntity { |
1476 Entity get rootOfScope; | 1501 Entity get rootOfScope; |
1477 } | 1502 } |
OLD | NEW |