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