| 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 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 77 /// Returns true if this [variable] is used inside a `try` block or a `sync*` | 77 /// Returns true if this [variable] is used inside a `try` block or a `sync*` |
| 78 /// generator (this is important to know because boxing/redirection needs to | 78 /// generator (this is important to know because boxing/redirection needs to |
| 79 /// happen for those local variables). | 79 /// happen for those local variables). |
| 80 /// | 80 /// |
| 81 /// Variables that are used in a try must be treated as boxed because the | 81 /// Variables that are used in a try must be treated as boxed because the |
| 82 /// control flow can be non-linear. | 82 /// control flow can be non-linear. |
| 83 /// | 83 /// |
| 84 /// Also parameters to a `sync*` generator must be boxed, because of the way | 84 /// Also parameters to a `sync*` generator must be boxed, because of the way |
| 85 /// we rewrite sync* functions. See also comments in | 85 /// we rewrite sync* functions. See also comments in |
| 86 /// [ClosureClassMap.useLocal]. | 86 /// [ClosureClassMap.useLocal]. |
| 87 bool variableIsUsedInTryOrSync(Local variable) => false; | 87 bool localIsUsedInTryOrSync(Local variable) => false; |
| 88 | 88 |
| 89 /// Loop through each variable that has been defined in this scope, modified | 89 /// Loop through each variable that has been defined in this scope, modified |
| 90 /// anywhere (this scope or another scope) and used in another scope. Because | 90 /// anywhere (this scope or another scope) and used in another scope. Because |
| 91 /// it is used in another scope, these variables need to be "boxed", creating | 91 /// it is used in another scope, these variables need to be "boxed", creating |
| 92 /// a thin wrapper around accesses to these variables so that accesses get | 92 /// a thin wrapper around accesses to these variables so that accesses get |
| 93 /// the correct updated value. The variables in variablesUsedInTryOrSync may | 93 /// the correct updated value. The variables in localsUsedInTryOrSync may |
| 94 /// be included in this set. | 94 /// be included in this set. |
| 95 /// | 95 /// |
| 96 /// In the case of loops, this is the set of iteration variables (or any | 96 /// In the case of loops, this is the set of iteration variables (or any |
| 97 /// variables declared in the for loop expression (`for (...here...)`) that | 97 /// variables declared in the for loop expression (`for (...here...)`) that |
| 98 /// need to be boxed to snapshot their value. | 98 /// need to be boxed to snapshot their value. |
| 99 void forEachBoxedVariable(f(Local local, FieldEntity field)) {} | 99 void forEachBoxedVariable(f(Local local, FieldEntity field)) {} |
| 100 | 100 |
| 101 /// True if [variable] has been mutated and is also used in another scope. | 101 /// True if [variable] has been mutated and is also used in another scope. |
| 102 bool isBoxed(Local variable) => false; | 102 bool isBoxed(Local variable) => false; |
| 103 | |
| 104 /// True if this scope declares any variables that need to be boxed. | |
| 105 bool get hasBoxedVariables => false; | |
| 106 } | 103 } |
| 107 | 104 |
| 108 /// Class representing the usage of a scope that has been captured in the | 105 /// Class representing the usage of a scope that has been captured in the |
| 109 /// context of a closure. | 106 /// context of a closure. |
| 110 class ClosureScope extends ScopeInfo { | 107 class ClosureScope extends ScopeInfo { |
| 111 const ClosureScope(); | 108 const ClosureScope(); |
| 112 | 109 |
| 113 /// If true, this closure accesses a variable that was defined in an outside | 110 /// If true, this closure accesses a variable that was defined in an outside |
| 114 /// scope and this variable gets modified at some point (sometimes we say that | 111 /// scope and this variable gets modified at some point (sometimes we say that |
| 115 /// variable has been "captured"). In this situation, access to this variable | 112 /// variable has been "captured"). In this situation, access to this variable |
| (...skipping 18 matching lines...) Expand all Loading... |
| 134 /// var lst = []; | 131 /// var lst = []; |
| 135 /// for (int i = 0; i < 5; i++) lst.add(()=>i); | 132 /// for (int i = 0; i < 5; i++) lst.add(()=>i); |
| 136 /// var result = list.map((f) => f()).toList(); | 133 /// var result = list.map((f) => f()).toList(); |
| 137 /// | 134 /// |
| 138 /// `result` will be [0, 1, 2, 3, 4], whereas were this JS code | 135 /// `result` will be [0, 1, 2, 3, 4], whereas were this JS code |
| 139 /// the result would be [5, 5, 5, 5, 5]. Because of this difference we need to | 136 /// the result would be [5, 5, 5, 5, 5]. Because of this difference we need to |
| 140 /// create a closure for these sorts of loops to capture the variable's value at | 137 /// create a closure for these sorts of loops to capture the variable's value at |
| 141 /// each iteration, by boxing the iteration variable[s]. | 138 /// each iteration, by boxing the iteration variable[s]. |
| 142 class LoopClosureScope extends ClosureScope { | 139 class LoopClosureScope extends ClosureScope { |
| 143 const LoopClosureScope(); | 140 const LoopClosureScope(); |
| 141 |
| 142 /// True if this loop scope declares in the first part of the loop |
| 143 /// `for (<here>;...;...)` any variables that need to be boxed. |
| 144 bool get hasBoxedLoopVariables => false; |
| 145 |
| 146 /// The set of iteration variables (or variables declared in the for loop |
| 147 /// expression (`for (<here>; ... ; ...)`) that need to be boxed to snapshot |
| 148 /// their value. These variables are also included in the set of |
| 149 /// `forEachBoxedVariable` method. The distinction between these two sets is |
| 150 /// in this example: |
| 151 /// |
| 152 /// run(f) => f(); |
| 153 /// var a; |
| 154 /// for (int i = 0; i < 3; i++) { |
| 155 /// var b = 3; |
| 156 /// a = () => b = i; |
| 157 /// } |
| 158 /// |
| 159 /// `i` would be a part of the boxedLoopVariables AND boxedVariables, but b |
| 160 /// would only be a part of boxedVariables. |
| 161 List<Local> get boxedLoopVariables => const <Local>[]; |
| 144 } | 162 } |
| 145 | 163 |
| 146 /// Class that describes the actual mechanics of how the converted, rewritten | 164 /// Class that describes the actual mechanics of how the converted, rewritten |
| 147 /// closure is implemented. For example, for the following closure (named foo | 165 /// closure is implemented. For example, for the following closure (named foo |
| 148 /// for convenience): | 166 /// for convenience): |
| 149 /// | 167 /// |
| 150 /// var foo = (x) => y + x; | 168 /// var foo = (x) => y + x; |
| 151 /// | 169 /// |
| 152 /// We would produce the following class to control access to these variables in | 170 /// We would produce the following class to control access to these variables in |
| 153 /// the following way (modulo naming of variables, assuming that y is modified | 171 /// the following way (modulo naming of variables, assuming that y is modified |
| (...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 640 // Otherwise contains the empty List. | 658 // Otherwise contains the empty List. |
| 641 List<Local> boxedLoopVariables = const <Local>[]; | 659 List<Local> boxedLoopVariables = const <Local>[]; |
| 642 | 660 |
| 643 ClosureScopeImpl(this.boxElement, this.capturedVariables); | 661 ClosureScopeImpl(this.boxElement, this.capturedVariables); |
| 644 | 662 |
| 645 Local get context => boxElement; | 663 Local get context => boxElement; |
| 646 | 664 |
| 647 bool get requiresContextBox => capturedVariables.keys.isNotEmpty; | 665 bool get requiresContextBox => capturedVariables.keys.isNotEmpty; |
| 648 | 666 |
| 649 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | 667 void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
| 650 if (capturedVariables.isNotEmpty) { | 668 capturedVariables.forEach(f); |
| 651 capturedVariables.forEach(f); | |
| 652 } else { | |
| 653 for (Local l in boxedLoopVariables) { | |
| 654 // The boxes for loop variables are constructed on-demand per-iteration | |
| 655 // in the locals handler. | |
| 656 f(l, null); | |
| 657 } | |
| 658 } | |
| 659 } | 669 } |
| 660 | 670 |
| 661 bool get hasBoxedVariables => !capturedVariables.isEmpty; | 671 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 662 | 672 |
| 663 bool isBoxed(Local variable) { | 673 bool isBoxed(Local variable) { |
| 664 return capturedVariables.containsKey(variable); | 674 return capturedVariables.containsKey(variable); |
| 665 } | 675 } |
| 666 | 676 |
| 667 void forEachCapturedVariable( | 677 void forEachCapturedVariable( |
| 668 f(LocalVariableElement variable, BoxFieldElement boxField)) { | 678 f(LocalVariableElement variable, BoxFieldElement boxField)) { |
| 669 capturedVariables.forEach(f); | 679 capturedVariables.forEach(f); |
| 670 } | 680 } |
| 671 | 681 |
| 672 // Should not be called. Added to make the new interface happy. | 682 // Should not be called. Added to make the new interface happy. |
| 673 bool variableIsUsedInTryOrSync(Local variable) => | 683 bool localIsUsedInTryOrSync(Local variable) => |
| 674 throw new UnsupportedError("ClosureScopeImpl.variableIsUsedInTryOrSync"); | 684 throw new UnsupportedError("ClosureScopeImpl.localIsUsedInTryOrSync"); |
| 675 | 685 |
| 676 // Should not be called. Added to make the new interface happy. | 686 // Should not be called. Added to make the new interface happy. |
| 677 Local get thisLocal => | 687 Local get thisLocal => |
| 678 throw new UnsupportedError("ClosureScopeImpl.thisLocal"); | 688 throw new UnsupportedError("ClosureScopeImpl.thisLocal"); |
| 679 | 689 |
| 680 String toString() { | 690 String toString() { |
| 681 String separator = ''; | 691 String separator = ''; |
| 682 StringBuffer sb = new StringBuffer(); | 692 StringBuffer sb = new StringBuffer(); |
| 683 sb.write('ClosureScopeImpl('); | 693 sb.write('ClosureScopeImpl('); |
| 684 if (boxElement != null) { | 694 if (boxElement != null) { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 734 /// Set of [variable]s referenced in this scope that are used inside a | 744 /// Set of [variable]s referenced in this scope that are used inside a |
| 735 /// `try` block or a `sync*` generator (this is important to know because | 745 /// `try` block or a `sync*` generator (this is important to know because |
| 736 /// boxing/redirection needs to happen for those local variables). | 746 /// boxing/redirection needs to happen for those local variables). |
| 737 /// | 747 /// |
| 738 /// Variables that are used in a try must be treated as boxed because the | 748 /// Variables that are used in a try must be treated as boxed because the |
| 739 /// control flow can be non-linear. | 749 /// control flow can be non-linear. |
| 740 /// | 750 /// |
| 741 /// Also parameters to a `sync*` generator must be boxed, because of the way | 751 /// Also parameters to a `sync*` generator must be boxed, because of the way |
| 742 /// we rewrite sync* functions. See also comments in [useLocal]. | 752 /// we rewrite sync* functions. See also comments in [useLocal]. |
| 743 // TODO(johnniwinther): Add variables to this only if the variable is mutated. | 753 // TODO(johnniwinther): Add variables to this only if the variable is mutated. |
| 744 final Set<Local> variablesUsedInTryOrSync = new Set<Local>(); | 754 final Set<Local> localsUsedInTryOrSync = new Set<Local>(); |
| 745 | 755 |
| 746 ClosureClassMap(this.closureEntity, this.closureClassEntity, this.callMethod, | 756 ClosureClassMap(this.closureEntity, this.closureClassEntity, this.callMethod, |
| 747 this.thisLocal); | 757 this.thisLocal); |
| 748 | 758 |
| 749 bool get hasBoxedVariables => | |
| 750 throw new UnsupportedError("ClosureClassMap.hasBoxedVariables"); | |
| 751 | |
| 752 List<Local> get createdFieldEntities { | 759 List<Local> get createdFieldEntities { |
| 753 List<Local> fields = <Local>[]; | 760 List<Local> fields = <Local>[]; |
| 754 if (closureClassEntity == null) return const <Local>[]; | 761 if (closureClassEntity == null) return const <Local>[]; |
| 755 closureClassEntity.closureFields.forEach((field) { | 762 closureClassEntity.closureFields.forEach((field) { |
| 756 fields.add(field.local); | 763 fields.add(field.local); |
| 757 }); | 764 }); |
| 758 return fields; | 765 return fields; |
| 759 } | 766 } |
| 760 | 767 |
| 761 void addFreeVariable(Local element) { | 768 void addFreeVariable(Local element) { |
| 762 assert(freeVariableMap[element] == null); | 769 assert(freeVariableMap[element] == null); |
| 763 freeVariableMap[element] = null; | 770 freeVariableMap[element] = null; |
| 764 } | 771 } |
| 765 | 772 |
| 766 Iterable<Local> get freeVariables => freeVariableMap.keys; | 773 Iterable<Local> get freeVariables => freeVariableMap.keys; |
| 767 | 774 |
| 768 bool isFreeVariable(Local element) { | 775 bool isFreeVariable(Local element) { |
| 769 return freeVariableMap.containsKey(element); | 776 return freeVariableMap.containsKey(element); |
| 770 } | 777 } |
| 771 | 778 |
| 772 void forEachFreeVariable(f(Local variable, FieldEntity field)) { | 779 void forEachFreeVariable(f(Local variable, FieldEntity field)) { |
| 773 freeVariableMap.forEach(f); | 780 freeVariableMap.forEach(f); |
| 774 } | 781 } |
| 775 | 782 |
| 776 FieldEntity get thisFieldEntity => freeVariableMap[thisLocal]; | 783 FieldEntity get thisFieldEntity => freeVariableMap[thisLocal]; |
| 777 | 784 |
| 778 bool variableIsUsedInTryOrSync(Local variable) => | 785 bool localIsUsedInTryOrSync(Local variable) => |
| 779 variablesUsedInTryOrSync.contains(variable); | 786 localsUsedInTryOrSync.contains(variable); |
| 780 | 787 |
| 781 Local getLocalVariableForClosureField(ClosureFieldElement field) { | 788 Local getLocalVariableForClosureField(ClosureFieldElement field) { |
| 782 return field.local; | 789 return field.local; |
| 783 } | 790 } |
| 784 | 791 |
| 785 bool get isClosure => closureEntity != null; | 792 bool get isClosure => closureEntity != null; |
| 786 | 793 |
| 787 bool capturingScopesBox(Local variable) { | 794 bool capturingScopesBox(Local variable) { |
| 788 return capturingScopes.values.any((scope) { | 795 return capturingScopes.values.any((scope) { |
| 789 return scope.boxedLoopVariables.contains(variable); | 796 return scope.boxedLoopVariables.contains(variable); |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1018 | 1025 |
| 1019 if (insideClosure && !inCurrentContext(variable)) { | 1026 if (insideClosure && !inCurrentContext(variable)) { |
| 1020 closureData.addFreeVariable(variable); | 1027 closureData.addFreeVariable(variable); |
| 1021 } else if (inTryStatement) { | 1028 } else if (inTryStatement) { |
| 1022 // Don't mark the this-element or a self-reference. This would complicate | 1029 // Don't mark the this-element or a self-reference. This would complicate |
| 1023 // things in the builder. | 1030 // things in the builder. |
| 1024 // Note that nested (named) functions are immutable. | 1031 // Note that nested (named) functions are immutable. |
| 1025 if (variable != closureData.thisLocal && | 1032 if (variable != closureData.thisLocal && |
| 1026 variable != closureData.closureEntity && | 1033 variable != closureData.closureEntity && |
| 1027 variable is! TypeVariableLocal) { | 1034 variable is! TypeVariableLocal) { |
| 1028 closureData.variablesUsedInTryOrSync.add(variable); | 1035 closureData.localsUsedInTryOrSync.add(variable); |
| 1029 } | 1036 } |
| 1030 } else if (variable is LocalParameterElement && | 1037 } else if (variable is LocalParameterElement && |
| 1031 variable.functionDeclaration.asyncMarker == AsyncMarker.SYNC_STAR) { | 1038 variable.functionDeclaration.asyncMarker == AsyncMarker.SYNC_STAR) { |
| 1032 // Parameters in a sync* function are shared between each Iterator created | 1039 // Parameters in a sync* function are shared between each Iterator created |
| 1033 // by the Iterable returned by the function, therefore they must be boxed. | 1040 // by the Iterable returned by the function, therefore they must be boxed. |
| 1034 closureData.variablesUsedInTryOrSync.add(variable); | 1041 closureData.localsUsedInTryOrSync.add(variable); |
| 1035 } | 1042 } |
| 1036 } | 1043 } |
| 1037 | 1044 |
| 1038 void useTypeVariableAsLocal(ResolutionTypeVariableType typeVariable) { | 1045 void useTypeVariableAsLocal(ResolutionTypeVariableType typeVariable) { |
| 1039 useLocal(new TypeVariableLocal( | 1046 useLocal(new TypeVariableLocal( |
| 1040 typeVariable, outermostElement, outermostElement.memberContext)); | 1047 typeVariable, outermostElement, outermostElement.memberContext)); |
| 1041 } | 1048 } |
| 1042 | 1049 |
| 1043 void declareLocal(LocalVariableElement element) { | 1050 void declareLocal(LocalVariableElement element) { |
| 1044 scopeVariables.add(element); | 1051 scopeVariables.add(element); |
| (...skipping 480 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1525 /// | 1532 /// |
| 1526 /// Move the below classes to a JS model eventually. | 1533 /// Move the below classes to a JS model eventually. |
| 1527 /// | 1534 /// |
| 1528 abstract class JSEntity implements MemberEntity { | 1535 abstract class JSEntity implements MemberEntity { |
| 1529 Local get declaredEntity; | 1536 Local get declaredEntity; |
| 1530 } | 1537 } |
| 1531 | 1538 |
| 1532 abstract class PrivatelyNamedJSEntity implements JSEntity { | 1539 abstract class PrivatelyNamedJSEntity implements JSEntity { |
| 1533 Entity get rootOfScope; | 1540 Entity get rootOfScope; |
| 1534 } | 1541 } |
| OLD | NEW |