Index: pkg/compiler/lib/src/js_model/closure.dart |
diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart |
index 308219bce0cefe83553ea7be24ffe7560ae3c37d..3ba9ec542cdd60ada05161b50796b654f43bf572 100644 |
--- a/pkg/compiler/lib/src/js_model/closure.dart |
+++ b/pkg/compiler/lib/src/js_model/closure.dart |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
@@ -9,10 +9,54 @@ import '../common/tasks.dart'; |
import '../elements/entities.dart'; |
import '../kernel/element_map.dart'; |
import '../world.dart'; |
-import 'elements.dart'; |
-import 'closure_visitors.dart'; |
import 'locals.dart'; |
+class KernelClosureDataBuilder extends ir.Visitor { |
+ final KernelToLocalsMap _localsMap; |
+ final KernelClosureRepresentationInfo info; |
+ |
+ bool _inTry = false; |
+ |
+ KernelClosureDataBuilder(this._localsMap, ThisLocal thisLocal) |
+ : info = new KernelClosureRepresentationInfo(thisLocal); |
+ |
+ @override |
+ defaultNode(ir.Node node) { |
+ node.visitChildren(this); |
+ } |
+ |
+ @override |
+ visitTryCatch(ir.TryCatch node) { |
+ bool oldInTry = _inTry; |
+ _inTry = true; |
+ node.visitChildren(this); |
+ _inTry = oldInTry; |
+ } |
+ |
+ @override |
+ visitTryFinally(ir.TryFinally node) { |
+ bool oldInTry = _inTry; |
+ _inTry = true; |
+ node.visitChildren(this); |
+ _inTry = oldInTry; |
+ } |
+ |
+ @override |
+ visitVariableGet(ir.VariableGet node) { |
+ if (_inTry) { |
+ info.registerUsedInTryOrSync(_localsMap.getLocal(node.variable)); |
+ } |
+ } |
+ |
+ @override |
+ visitVariableSet(ir.VariableSet node) { |
+ if (_inTry) { |
+ info.registerUsedInTryOrSync(_localsMap.getLocal(node.variable)); |
+ } |
+ node.visitChildren(this); |
+ } |
+} |
+ |
/// Closure conversion code using our new Entity model. Closure conversion is |
/// necessary because the semantics of closures are slightly different in Dart |
/// than JavaScript. Closure conversion is separated out into two phases: |
@@ -25,24 +69,14 @@ import 'locals.dart'; |
/// check out: |
/// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or |
/// http://matt.might.net/articles/closure-conversion/. |
-// TODO(efortuna): Change inheritance hierarchy so that the |
-// ClosureConversionTask doesn't inherit from ClosureTask because it's just a |
-// glorified timer. |
class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
final KernelToElementMapForBuilding _elementMap; |
final GlobalLocalsMap _globalLocalsMap; |
- Map<ir.Node, ClosureScope> _closureScopeMap = <ir.Node, ClosureScope>{}; |
- |
- Map<Entity, ClosureRepresentationInfo> _closureRepresentationMap = |
+ Map<Entity, ClosureRepresentationInfo> _infoMap = |
<Entity, ClosureRepresentationInfo>{}; |
- /// Should only be used at the very beginning to ensure we are looking at the |
- /// right kind of elements. |
- // TODO(efortuna): Remove this map once we have one kernel backend strategy. |
- final JsToFrontendMap _kToJElementMap; |
- |
- KernelClosureConversionTask(Measurer measurer, this._elementMap, |
- this._kToJElementMap, this._globalLocalsMap) |
+ KernelClosureConversionTask( |
+ Measurer measurer, this._elementMap, this._globalLocalsMap) |
: super(measurer); |
/// The combined steps of generating our intermediate representation of |
@@ -54,192 +88,67 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
@override |
void convertClosures(Iterable<MemberEntity> processedEntities, |
ClosedWorldRefiner closedWorldRefiner) { |
- var closuresToGenerate = <ir.Node, ScopeInfo>{}; |
- processedEntities.forEach((MemberEntity kEntity) { |
- MemberEntity entity = kEntity; |
- if (_kToJElementMap != null) { |
- entity = _kToJElementMap.toBackendMember(kEntity); |
- } |
- if (entity.isAbstract) return; |
- if (entity.isField && !entity.isInstanceMember) { |
- ir.Field field = _elementMap.getMemberNode(entity); |
- // Skip top-level/static fields without an initializer. |
- if (field.initializer == null) return; |
- } |
- _buildClosureModel(entity, closuresToGenerate, closedWorldRefiner); |
- }); |
- |
- for (ir.Node node in closuresToGenerate.keys) { |
- _produceSyntheticElements( |
- node, closuresToGenerate[node], closedWorldRefiner); |
- } |
- } |
- |
- /// Inspect members and mark if those members capture any state that needs to |
- /// be marked as free variables. |
- void _buildClosureModel( |
- MemberEntity entity, |
- Map<ir.Node, ScopeInfo> closuresToGenerate, |
- ClosedWorldRefiner closedWorldRefiner) { |
- ir.Node node = _elementMap.getMemberNode(entity); |
- if (_closureScopeMap.keys.contains(node)) return; |
- ClosureScopeBuilder translator = new ClosureScopeBuilder(_closureScopeMap, |
- closuresToGenerate, _globalLocalsMap.getLocalsMap(entity), _elementMap); |
- if (entity.isField) { |
- if (node is ir.Field && node.initializer != null) { |
- translator.translateLazyInitializer(node); |
- } |
- } else { |
- assert(node is ir.Procedure || node is ir.Constructor); |
- translator.translateConstructorOrProcedure(node); |
- } |
+ // TODO(efortuna): implement. |
} |
- /// Given what variables are captured at each point, construct closure classes |
- /// with fields containing the captured variables to replicate the Dart |
- /// closure semantics in JS. |
- void _produceSyntheticElements( |
- ir.Node node, ScopeInfo info, ClosedWorldRefiner closedWorldRefiner) { |
- Entity entity; |
- KernelClosureClass closureClass = |
- new KernelClosureClass.fromScopeInfo(info); |
- if (node is ir.FunctionNode) { |
- // We want the original declaration where that function is used to point |
- // to the correct closure class. |
- // TODO(efortuna): entity equivalent of element.declaration? |
- node = (node as ir.FunctionNode).parent; |
- _closureRepresentationMap[closureClass.callMethod] = closureClass; |
- } |
- |
- if (node is ir.Member) { |
- entity = _elementMap.getMember(node); |
- } else { |
- entity = _elementMap.getLocalFunction(node); |
- } |
- assert(entity != null); |
- |
- _closureRepresentationMap[entity] = closureClass; |
+ /// TODO(johnniwinther,efortuna): Implement this. |
+ @override |
+ ClosureScope getClosureScope(ir.Node node) { |
+ return const ClosureScope(); |
} |
@override |
ScopeInfo getScopeInfo(Entity entity) { |
+ // TODO(efortuna): Specialize this function from the one below. |
return getClosureRepresentationInfo(entity); |
} |
+ /// TODO(johnniwinther,efortuna): Implement this. |
@override |
- // TODO(efortuna): Eventually closureScopeMap[node] should always be non-null, |
- // and we should just test that with an assert. |
- ClosureScope getClosureScope(ir.Node node) => |
- _closureScopeMap[node] ?? const ClosureScope(); |
+ LoopClosureScope getLoopClosureScope(ir.Node loopNode) { |
+ return const LoopClosureScope(); |
+ } |
@override |
- // TODO(efortuna): Eventually closureScopeMap[node] should always be non-null, |
- // and we should just test that with an assert. |
- LoopClosureScope getLoopClosureScope(ir.Node loopNode) => |
- _closureScopeMap[node] ?? const LoopClosureScope(); |
+ ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { |
+ return _infoMap.putIfAbsent(entity, () { |
+ if (entity is MemberEntity) { |
+ ir.Member node = _elementMap.getMemberNode(entity); |
+ ThisLocal thisLocal; |
+ if (entity.isInstanceMember) { |
+ thisLocal = new ThisLocal(entity); |
+ } |
+ KernelClosureDataBuilder builder = new KernelClosureDataBuilder( |
+ _globalLocalsMap.getLocalsMap(entity), thisLocal); |
+ node.accept(builder); |
+ return builder.info; |
+ } |
- @override |
- // TODO(efortuna): Eventually closureRepresentationMap[node] should always be |
- // non-null, and we should just test that with an assert. |
- ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) => |
- _closureRepresentationMap[entity] ?? const ClosureRepresentationInfo(); |
+ /// TODO(johnniwinther,efortuna): Implement this. |
+ return const ClosureRepresentationInfo(); |
+ }); |
+ } |
} |
-class KernelScopeInfo extends ScopeInfo { |
- final Set<Local> localsUsedInTryOrSync; |
- final Local thisLocal; |
- final Set<Local> boxedVariables = new Set<Local>(); |
- |
- /// The set of variables that were defined in another scope, but are used in |
- /// this scope. |
- Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); |
+// TODO(johnniwinther): Add unittest for the computed |
+// [ClosureRepresentationInfo]. |
+class KernelClosureRepresentationInfo extends ClosureRepresentationInfo { |
+ final ThisLocal thisLocal; |
+ final Set<Local> _localsUsedInTryOrSync = new Set<Local>(); |
- KernelScopeInfo(this.thisLocal) : localsUsedInTryOrSync = new Set<Local>(); |
+ KernelClosureRepresentationInfo(this.thisLocal); |
- KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info) |
- : localsUsedInTryOrSync = info.variablesUsedInTryOrSync, |
- boxedVariables = info.boxedVariables; |
- |
- KernelScopeInfo.withBoxedVariables(this.boxedVariables, this.thisLocal); |
- |
- void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
- boxedVariables.forEach((Local l) { |
- // TODO(efortuna): add FieldEntities as created. |
- f(l, null); |
- }); |
+ void registerUsedInTryOrSync(Local local) { |
+ _localsUsedInTryOrSync.add(local); |
} |
- bool localIsUsedInTryOrSync(Local variable) => |
- localsUsedInTryOrSync.contains(variable); |
+ bool variableIsUsedInTryOrSync(Local variable) => |
+ _localsUsedInTryOrSync.contains(variable); |
String toString() { |
StringBuffer sb = new StringBuffer(); |
sb.write('this=$thisLocal,'); |
- sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
+ sb.write('localsUsedInTryOrSync={${_localsUsedInTryOrSync.join(', ')}}'); |
return sb.toString(); |
} |
- |
- bool isBoxed(Local variable) => boxedVariables.contains(variable); |
-} |
- |
-class KernelClosureScope extends KernelScopeInfo implements ClosureScope { |
- final Local context; |
- |
- KernelClosureScope(Set<Local> boxedVariables, this.context, Local thisLocal) |
- : super.withBoxedVariables(boxedVariables, thisLocal); |
- |
- bool get requiresContextBox => boxedVariables.isNotEmpty; |
-} |
- |
-class KernelLoopClosureScope extends KernelClosureScope |
- implements LoopClosureScope { |
- final List<Local> boxedLoopVariables; |
- |
- KernelLoopClosureScope(Set<Local> boxedVariables, this.boxedLoopVariables, |
- Local context, Local thisLocal) |
- : super(boxedVariables, context, thisLocal); |
- |
- bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
-} |
- |
-// TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
-class KernelClosureClass extends KernelScopeInfo |
- implements ClosureRepresentationInfo { |
- KernelClosureClass.fromScopeInfo(ScopeInfo info) |
- : super.from(info.thisLocal, info); |
- |
- // TODO(efortuna): Implement. |
- Local get closureEntity => null; |
- |
- // TODO(efortuna): Implement. |
- ClassEntity get closureClassEntity => null; |
- |
- // TODO(efortuna): Implement. |
- FunctionEntity get callMethod => null; |
- |
- // TODO(efortuna): Implement. |
- List<Local> get createdFieldEntities => const <Local>[]; |
- |
- // TODO(efortuna): Implement. |
- FieldEntity get thisFieldEntity => null; |
- |
- // TODO(efortuna): Implement. |
- void forEachCapturedVariable(f(Local from, FieldEntity to)) {} |
- |
- // TODO(efortuna): Implement. |
- @override |
- void forEachBoxedVariable(f(Local local, FieldEntity field)) {} |
- |
- // TODO(efortuna): Implement. |
- void forEachFreeVariable(f(Local variable, FieldEntity field)) {} |
- |
- // TODO(efortuna): Implement. |
- bool isVariableBoxed(Local variable) => false; |
- |
- // TODO(efortuna): Implement. |
- // Why is this closure not actually a closure? Well, to properly call |
- // ourselves a closure, we need to register the new closure class with the |
- // ClosedWorldRefiner, which currently only takes elements. The change to |
- // that (and the subsequent adjustment here) will follow soon. |
- bool get isClosure => false; |
} |