| 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, () {
|
|
|