Index: pkg/compiler/lib/src/closure.dart |
diff --git a/pkg/compiler/lib/src/closure.dart b/pkg/compiler/lib/src/closure.dart |
index 9ad557d071efcab925b5ef5e0a520d4ef226092a..94eb03da53100403fe44cad8437f5caa854c3430 100644 |
--- a/pkg/compiler/lib/src/closure.dart |
+++ b/pkg/compiler/lib/src/closure.dart |
@@ -52,10 +52,10 @@ abstract class ClosureDataLookup<T> { |
/// Look up information about a loop, in case any variables it declares need |
/// to be boxed/snapshotted. |
- LoopClosureRepresentationInfo getClosureRepresentationInfoForLoop(T loopNode); |
+ LoopClosureScope getLoopClosureScope(T loopNode); |
/// Accessor to the information about closures that the SSA builder will use. |
- ClosureAnalysisInfo getClosureAnalysisInfo(T node); |
+ ClosureScope getClosureScope(T node); |
} |
/// Class that represents one level of scoping information, whether this scope |
@@ -70,6 +70,10 @@ abstract class ClosureDataLookup<T> { |
class ScopeInfo { |
const ScopeInfo(); |
+ /// Convenience reference pointer to the element representing `this`. |
+ /// If this scope is not in an instance member, it will be null. |
+ Local get thisLocal => null; |
+ |
/// Returns true if this [variable] is used inside a `try` block or a `sync*` |
/// generator (this is important to know because boxing/redirection needs to |
/// happen for those local variables). |
@@ -82,16 +86,29 @@ class ScopeInfo { |
/// [ClosureClassMap.useLocal]. |
bool variableIsUsedInTryOrSync(Local variable) => false; |
- /// Convenience reference pointer to the element representing `this`. |
- /// If this scope is not in an instance member, it will be null. |
- Local get thisLocal => null; |
+ /// Loop through each variable that has been defined in this scope, modified |
+ /// anywhere (this scope or another scope) and used in another scope. Because |
+ /// it is used in another scope, these variables need to be "boxed", creating |
+ /// a thin wrapper around accesses to these variables so that accesses get |
+ /// the correct updated value. The variables in variablesUsedInTryOrSync may |
+ /// be included in this set. |
+ /// |
+ /// In the case of loops, this is the set of iteration variables (or any |
+ /// variables declared in the for loop expression (`for (...here...)`) that |
+ /// need to be boxed to snapshot their value. |
+ void forEachBoxedVariable(f(Local local, FieldEntity field)) {} |
+ |
+ /// True if [variable] has been mutated and is also used in another scope. |
+ bool isBoxed(Local variable) => false; |
+ |
+ /// True if this scope declares any variables that need to be boxed. |
+ bool get hasBoxedVariables => false; |
} |
-/// Class that provides a black-box interface to information gleaned from |
-/// analyzing a closure's characteristics, most commonly used to influence how |
-/// code should be generated in SSA builder stage. |
-class ClosureAnalysisInfo { |
- const ClosureAnalysisInfo(); |
+/// Class representing the usage of a scope that has been captured in the |
+/// context of a closure. |
+class ClosureScope extends ScopeInfo { |
+ const ClosureScope(); |
/// If true, this closure accesses a variable that was defined in an outside |
/// scope and this variable gets modified at some point (sometimes we say that |
@@ -105,23 +122,14 @@ class ClosureAnalysisInfo { |
/// scoped into this context from outside. This is an accessor to the |
/// contextBox that [requiresContextBox] is testing is required. |
Local get context => null; |
- |
- /// True if the specified variable has been mutated inside the scope of this |
- /// closure. |
- bool isCaptured(Local variable) => false; |
- |
- /// Loop through every variable that has been captured in this closure. This |
- /// consists of all the free variables (variables captured *just* in this |
- /// closure) and all variables captured in nested scopes that we may be |
- /// capturing as well. |
- void forEachCapturedVariable(f(Local from, FieldEntity to)) {} |
} |
-/// Class that describes the actual mechanics of how a loop is |
-/// converted/rewritten without closures. Unlike JS, the value of a declared |
-/// loop iteration variable in any closure is captured/snapshotted inside at |
-/// each iteration point, as if we created a new local variable for that value |
-/// inside the loop. For example, for the following loop: |
+/// Class that describes the actual mechanics of how values of variables |
+/// instantiated in a loop are captured inside closures in the loop body. |
+/// Unlike JS, the value of a declared loop iteration variable in any closure |
+/// is captured/snapshotted inside at each iteration point, as if we created a |
+/// new local variable for that value inside the loop. For example, for the |
+/// following loop: |
/// |
/// var lst = []; |
/// for (int i = 0; i < 5; i++) lst.add(()=>i); |
@@ -131,16 +139,8 @@ class ClosureAnalysisInfo { |
/// the result would be [5, 5, 5, 5, 5]. Because of this difference we need to |
/// create a closure for these sorts of loops to capture the variable's value at |
/// each iteration, by boxing the iteration variable[s]. |
-class LoopClosureRepresentationInfo extends ClosureAnalysisInfo { |
- const LoopClosureRepresentationInfo(); |
- |
- /// True if this loop declares any variables that need to be boxed. |
- bool get hasBoxedVariables => false; |
- |
- /// The set of iteration variables (or variables declared in the for loop |
- /// expression (`for (...here...)`) that need to be boxed to snapshot their |
- /// value. |
- List<Local> get boxedVariables => const <Local>[]; |
+class LoopClosureScope extends ClosureScope { |
+ const LoopClosureScope(); |
} |
/// Class that describes the actual mechanics of how the converted, rewritten |
@@ -209,7 +209,11 @@ class ClosureRepresentationInfo extends ScopeInfo { |
/// Loop through each variable that has been boxed in this closure class. Only |
/// captured variables that are mutated need to be "boxed" (which basically |
/// puts a thin layer between updates and reads to this variable to ensure |
- /// that every place that accesses it gets the correct updated value). |
+ /// that every place that accesses it gets the correct updated value). This |
+ /// includes looping over variables that were boxed from other scopes, not |
+ /// strictly variables defined in this closure, unlike the behavior in |
+ /// the superclass ScopeInfo. |
+ @override |
void forEachBoxedVariable(f(Local local, FieldEntity field)) {} |
/// Loop through each free variable in this closure. Free variables are the |
@@ -228,7 +232,7 @@ class ClosureRepresentationInfo extends ScopeInfo { |
} |
class ClosureTask extends ClosureConversionTask<Node> { |
- Map<Node, ClosureScope> _closureInfoMap = <Node, ClosureScope>{}; |
+ Map<Node, ClosureScopeImpl> _closureInfoMap = <Node, ClosureScopeImpl>{}; |
Map<Element, ClosureClassMap> _closureMappingCache = |
<Element, ClosureClassMap>{}; |
Compiler compiler; |
@@ -245,9 +249,9 @@ class ClosureTask extends ClosureConversionTask<Node> { |
createClosureClasses(closedWorldRefiner); |
} |
- ClosureAnalysisInfo getClosureAnalysisInfo(Node node) { |
+ ClosureScope getClosureScope(Node node) { |
var value = _closureInfoMap[node]; |
- return value == null ? const ClosureAnalysisInfo() : value; |
+ return value == null ? const ClosureScope() : value; |
} |
ScopeInfo getScopeInfo(Element member) { |
@@ -258,10 +262,9 @@ class ClosureTask extends ClosureConversionTask<Node> { |
return getClosureToClassMapping(member); |
} |
- LoopClosureRepresentationInfo getClosureRepresentationInfoForLoop( |
- Node loopNode) { |
+ LoopClosureScope getLoopClosureScope(Node loopNode) { |
var value = _closureInfoMap[loopNode]; |
- return value == null ? const LoopClosureRepresentationInfo() : value; |
+ return value == null ? const LoopClosureScope() : value; |
} |
/// Returns the [ClosureClassMap] computed for [resolvedAst]. |
@@ -629,8 +632,7 @@ class SynthesizedCallMethodElementX extends BaseFunctionElementX |
// The box-element for a scope, and the captured variables that need to be |
// stored in the box. |
-class ClosureScope |
- implements ClosureAnalysisInfo, LoopClosureRepresentationInfo { |
+class ClosureScopeImpl implements ClosureScope, LoopClosureScope { |
final BoxLocal boxElement; |
final Map<Local, BoxFieldElement> capturedVariables; |
@@ -639,17 +641,27 @@ class ClosureScope |
// Otherwise contains the empty List. |
List<Local> boxedLoopVariables = const <Local>[]; |
- ClosureScope(this.boxElement, this.capturedVariables); |
+ ClosureScopeImpl(this.boxElement, this.capturedVariables); |
Local get context => boxElement; |
bool get requiresContextBox => capturedVariables.keys.isNotEmpty; |
- List<Local> get boxedVariables => boxedLoopVariables; |
+ void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
+ if (capturedVariables.isNotEmpty) { |
+ capturedVariables.forEach(f); |
+ } else { |
+ for (Local l in boxedLoopVariables) { |
+ // The boxes for loop variables are constructed on-demand per-iteration |
+ // in the locals handler. |
+ f(l, null); |
+ } |
+ } |
+ } |
- bool get hasBoxedVariables => !boxedLoopVariables.isEmpty; |
+ bool get hasBoxedVariables => !capturedVariables.isEmpty; |
- bool isCaptured(Local variable) { |
+ bool isBoxed(Local variable) { |
return capturedVariables.containsKey(variable); |
} |
@@ -658,10 +670,18 @@ class ClosureScope |
capturedVariables.forEach(f); |
} |
+ // Should not be called. Added to make the new interface happy. |
+ bool variableIsUsedInTryOrSync(Local variable) => |
+ throw new UnsupportedError("ClosureScopeImpl.variableIsUsedInTryOrSync"); |
+ |
+ // Should not be called. Added to make the new interface happy. |
+ Local get thisLocal => |
+ throw new UnsupportedError("ClosureScopeImpl.thisLocal"); |
+ |
String toString() { |
String separator = ''; |
StringBuffer sb = new StringBuffer(); |
- sb.write('ClosureScope('); |
+ sb.write('ClosureScopeImpl('); |
if (boxElement != null) { |
sb.write('box=$boxElement'); |
separator = ','; |
@@ -705,11 +725,12 @@ class ClosureClassMap implements ClosureRepresentationInfo { |
/// their locations. |
final Map<Local, FieldEntity> freeVariableMap = new Map<Local, FieldEntity>(); |
- /// Maps [Loop] and [FunctionExpression] nodes to their [ClosureScope] which |
+ /// Maps [Loop] and [FunctionExpression] nodes to their [ClosureScopeImpl] which |
/// contains their box and the captured variables that are stored in the box. |
/// This map will be empty if the method/closure of this [ClosureData] does |
/// not contain any nested closure. |
- final Map<Node, ClosureScope> capturingScopes = new Map<Node, ClosureScope>(); |
+ final Map<Node, ClosureScopeImpl> capturingScopes = |
+ new Map<Node, ClosureScopeImpl>(); |
/// Set of [variable]s referenced in this scope that are used inside a |
/// `try` block or a `sync*` generator (this is important to know because |
@@ -726,6 +747,9 @@ class ClosureClassMap implements ClosureRepresentationInfo { |
ClosureClassMap(this.closureEntity, this.closureClassEntity, this.callMethod, |
this.thisLocal); |
+ bool get hasBoxedVariables => |
+ throw new UnsupportedError("ClosureClassMap.hasBoxedVariables"); |
+ |
List<Local> get createdFieldEntities { |
List<Local> fields = <Local>[]; |
if (closureClassEntity == null) return const <Local>[]; |
@@ -780,7 +804,7 @@ class ClosureClassMap implements ClosureRepresentationInfo { |
if (variable is BoxLocal) return; |
f(variable, copy); |
}); |
- capturingScopes.values.forEach((ClosureScope scope) { |
+ capturingScopes.values.forEach((ClosureScopeImpl scope) { |
scope.forEachCapturedVariable(f); |
}); |
} |
@@ -791,10 +815,18 @@ class ClosureClassMap implements ClosureRepresentationInfo { |
if (!isVariableBoxed(variable)) return; |
f(variable, copy); |
}); |
- capturingScopes.values.forEach((ClosureScope scope) { |
+ capturingScopes.values.forEach((ClosureScopeImpl scope) { |
scope.forEachCapturedVariable(f); |
}); |
} |
+ |
+ bool isBoxed(Local local) { |
+ bool variableIsBoxed = false; |
+ forEachBoxedVariable((LocalVariableElement element, BoxFieldElement field) { |
+ if (element == local) variableIsBoxed = true; |
+ }); |
+ return variableIsBoxed; |
+ } |
} |
class ClosureTranslator extends Visitor { |
@@ -806,7 +838,7 @@ class ClosureTranslator extends Visitor { |
bool inTryStatement = false; |
final Map<Element, ClosureClassMap> closureMappingCache; |
- final Map<Node, ClosureScope> closureInfo; |
+ final Map<Node, ClosureScopeImpl> closureInfo; |
// Map of captured variables. Initially they will map to `null`. If |
// a variable needs to be boxed then the scope declaring the variable |
@@ -1215,7 +1247,7 @@ class ClosureTranslator extends Visitor { |
boxCapturedVariable(variable); |
} |
if (!scopeMapping.isEmpty) { |
- ClosureScope scope = new ClosureScope(box, scopeMapping); |
+ ClosureScopeImpl scope = new ClosureScopeImpl(box, scopeMapping); |
closureData.capturingScopes[node] = scope; |
assert(closureInfo[node] == null); |
closureInfo[node] = scope; |
@@ -1280,7 +1312,7 @@ class ClosureTranslator extends Visitor { |
} |
} |
}); |
- ClosureScope scopeData = closureData.capturingScopes[node]; |
+ ClosureScopeImpl scopeData = closureData.capturingScopes[node]; |
if (scopeData == null) return; |
scopeData.boxedLoopVariables = boxedLoopVariables; |
} |