| 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 |