Index: pkg/compiler/lib/src/closure.dart |
diff --git a/pkg/compiler/lib/src/closure.dart b/pkg/compiler/lib/src/closure.dart |
index 6320e1f7bbaea75450fccf13e2594ab2825f031e..9da7b00ffd9245dc047203d612a3be887605596f 100644 |
--- a/pkg/compiler/lib/src/closure.dart |
+++ b/pkg/compiler/lib/src/closure.dart |
@@ -26,10 +26,16 @@ import 'util/util.dart'; |
import 'world.dart' show ClosedWorldRefiner; |
/// Where T is ir.Node or Node. |
+// TODO(efortuna): Rename this class. |
abstract class ClosureClassMaps<T> { |
ClosureClassMap getMemberMap(MemberEntity member); |
ClosureClassMap getLocalFunctionMap(Local localFunction); |
+ /// Look up information about the variables that have been mutated and are |
+ /// used inside the scope of [node]. |
+ // TODO(efortuna): Node for consistency with other interface. |
+ ClosureRepresentationInfo getClosureRepresentationInfo(Entity member); |
+ |
/// Look up information about a loop, in case any variables it declares need |
/// to be boxed/snapshotted. |
LoopClosureRepresentationInfo getClosureRepresentationInfoForLoop(T loopNode); |
@@ -38,6 +44,100 @@ abstract class ClosureClassMaps<T> { |
ClosureAnalysisInfo getClosureAnalysisInfo(T node); |
} |
+/// Class that describes the actual mechanics of how the converted, rewritten |
+/// closure is implemented. For example, for the following closure (named foo |
+/// for convenience): |
+/// |
+/// var foo = (x) => y + x; |
+/// |
+/// We would produce the following class to control access to these variables in |
+/// the following way (modulo naming of variables, assuming that y is modified |
+/// elsewhere in its scope): |
+/// |
+/// class FooClosure { |
+/// int y; |
+/// FooClosure(this.y); |
+/// call(x) => this.y + x; |
+/// } |
+/// |
+/// and then to execute this closure, for example: |
+/// |
+/// var foo = new FooClosure(1); |
+/// foo.call(2); |
+/// |
+/// if y is modified elsewhere within its scope, accesses to y anywhere in the |
+/// code will be controlled via a box object. |
+/// I expect this interface to get simpler in subsequent refactorings. |
+class ClosureRepresentationInfo { |
+ const ClosureRepresentationInfo(); |
+ |
+ /// Accessor to the local environment in which a particular closure node is |
+ /// executed. This will encapsulate the value of any variables that have been |
+ /// scoped into this context from outside. |
+ Local get context => null; |
+ |
+ /// The original local function before any translation. |
+ /// |
+ /// Will be null for methods. // TODO(efortuna): last part -- what? |
+ Local get closureElement => null; |
+ |
+ /// Closures are rewritten in the form of classes that |
+ /// have fields to control the redirection and editing of variables that are |
+ /// "captured" inside a scope (declared in an outer scope but used in an |
+ /// inside scope). So this returns the class entity that represents this |
+ /// particular rewritten closure. |
+ ClassEntity get closureClassEntity => null; |
+ |
+ /// The function that implements the [local] function as a `call` method on |
+ /// the closure class. |
+ FunctionEntity get callEntity => null; |
+ |
+ /// As shown in the example in the comments at the top of this class, we |
+ /// create fields in the closure class for each captured variable. This is an |
+ /// accessor to that set of fields. |
+ List<Local> get createdFieldEntities => const <Local>[]; |
+ |
+ /// Convenience reference pointer to the element representing `this`. |
+ /// It is only set for instance-members. |
+ Local get thisLocal => null; |
+ |
+ /// Convenience pointer to the field entity representation in the closure |
+ /// class of the element representing `this`. |
+ FieldEntity get thisFieldEntity => 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). |
+ /// |
+ /// Variables that are used in a try must be treated as boxed because the |
+ /// control flow can be non-linear. |
+ /// |
+ /// Also parameters to a `sync*` generator must be boxed, because of the way |
+ /// we rewrite sync* functions. See also comments in |
+ /// [ClosureClassMap.useLocal]. |
+ // TODO(efortuna): this feels like it should be in the ClosureAnalysisData |
+ // section. |
+ bool isVariableUsedInTryOrSync(Local variable) => false; |
+ |
+ /// Loop through every variable that has been captured exclusively in this |
+ /// current closure. Each [variable] has been "converted" to a [field] member |
+ /// in the closure class. |
+ void forEachCreatedField(f(Local variable, FieldEntity field)) {} |
+ |
+ /// 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. These nested scopes hold "boxes" to hold the executable |
+ /// context for that scope. |
+ void forEachCapturedVariable(f(Local from, Local to)) {} |
+ |
+ /// 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). |
+ void forEachBoxedVariable(f(Local local, FieldEntity field)) {} |
+} |
+ |
/// 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. |
@@ -117,11 +217,15 @@ class ClosureTask extends CompilerTask implements ClosureClassMaps<Node> { |
return value == null ? const LoopClosureRepresentationInfo() : value; |
} |
+ ClosureRepresentationInfo getClosureRepresentationInfo(Element member) { |
+ return getClosureToClassMapping(member); |
+ } |
+ |
ClosureClassMap getMemberMap(MemberElement member) { |
return getClosureToClassMapping(member); |
} |
- ClosureClassMap getLocalFunctionMap(LocalFunctionElement localFunction) { |
+ ClosureClassMap _getLocalFunctionMap(LocalFunctionElement localFunction) { |
return getClosureToClassMapping(localFunction); |
} |
@@ -352,7 +456,8 @@ class ClosureClassElement extends ClassElementX { |
} |
/// A local variable that contains the box object holding the [BoxFieldElement] |
-/// fields. |
+/// fields. // this is a "local variable" corresponding to the executable |
+//context (for a particular BoxFieldElement). |
class BoxLocal extends Local { |
final String name; |
final ExecutableElement executableContext; |
@@ -537,7 +642,7 @@ class ClosureScope |
} |
} |
-class ClosureClassMap { |
+class ClosureClassMap implements ClosureRepresentationInfo { |
/// The local function element before any translation. |
/// |
/// Will be null for methods. |
@@ -545,14 +650,14 @@ class ClosureClassMap { |
/// The synthesized closure class for [closureElement]. |
/// |
- /// The closureClassElement will be null for methods that are not local |
+ /// The closureClassEntity will be null for methods that are not local |
/// closures. |
- final ClosureClassElement closureClassElement; |
+ final ClosureClassElement closureClassEntity; |
- /// The synthesized `call` method of the [ closureClassElement]. |
+ /// The synthesized `call` method of the [ closureClassEntity]. |
/// |
- /// The callElement will be null for methods that are not local closures. |
- final MethodElement callElement; |
+ /// The callEntity will be null for methods that are not local closures. |
+ final MethodElement callEntity; |
/// The [thisElement] makes handling 'this' easier by treating it like any |
/// other argument. It is only set for instance-members. |
@@ -576,8 +681,16 @@ class ClosureClassMap { |
// TODO(johnniwinther): Add variables to this only if the variable is mutated. |
final Set<Local> variablesUsedInTryOrGenerator = new Set<Local>(); |
- ClosureClassMap(this.closureElement, this.closureClassElement, |
- this.callElement, this.thisLocal); |
+ ClosureClassMap(this.closureElement, this.closureClassEntity, this.callEntity, |
+ this.thisLocal); |
+ |
+ List<Local> get createdFieldEntities { |
+ List<Local> fields = <Local>[]; |
+ closureClassEntity.closureFields.forEach((field) { |
+ fields.add(field.local); |
+ }); |
+ return fields; |
+ } |
void addFreeVariable(Local element) { |
assert(freeVariableMap[element] == null); |
@@ -590,10 +703,15 @@ class ClosureClassMap { |
return freeVariableMap.containsKey(element); |
} |
+ bool isVariableUsedInTryOrSync(Local variable) => |
+ variablesUsedInTryOrGenerator.contains(variable); |
+ |
void forEachFreeVariable(f(Local variable, FieldEntity field)) { |
freeVariableMap.forEach(f); |
} |
+ FieldEntity get thisFieldEntity => freeVariableMap[thisLocal]; |
+ |
bool isVariableUsedInTryOrSync(Local variable) => |
variablesUsedInTryOrGenerator.contains(variable); |
@@ -674,6 +792,9 @@ class ClosureTranslator extends Visitor { |
bool insideClosure = false; |
+ Map<Node, Local> executableContextCache; |
+ Map<Node, ClosureScope> closureInfo; |
+ |
ClosureTranslator(this.compiler, this.closedWorldRefiner, this.elements, |
this.closureMappingCache, this.closureInfo); |
@@ -778,7 +899,7 @@ class ClosureTranslator extends Visitor { |
boxes.add(boxFieldElement.box); |
} |
}); |
- ClosureClassElement closureClass = data.closureClassElement; |
+ ClosureClassElement closureClass = data.closureClassEntity; |
assert(closureClass != null || (fieldCaptures.isEmpty && boxes.isEmpty)); |
void addClosureField(Local local, String name) { |
@@ -1221,8 +1342,8 @@ class ClosureTranslator extends Visitor { |
} |
closureMappingCache[element] = closureData; |
closureMappingCache[element.declaration] = closureData; |
- if (closureData.callElement != null) { |
- closureMappingCache[closureData.callElement] = closureData; |
+ if (closureData.callEntity != null) { |
+ closureMappingCache[closureData.callEntity] = closureData; |
} |
inNewScope(node, () { |