Index: pkg/kernel/lib/transformations/closure/info.dart |
diff --git a/pkg/kernel/lib/transformations/closure/info.dart b/pkg/kernel/lib/transformations/closure/info.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..acbb3d21535b93c87b12223e41267d102f153623 |
--- /dev/null |
+++ b/pkg/kernel/lib/transformations/closure/info.dart |
@@ -0,0 +1,206 @@ |
+// 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. |
+ |
+library kernel.transformations.closure.info; |
+ |
+import '../../ast.dart' |
+ show |
+ Class, |
+ Constructor, |
+ Field, |
+ FunctionDeclaration, |
+ FunctionNode, |
+ Member, |
+ Name, |
+ Procedure, |
+ ProcedureKind, |
+ PropertyGet, |
+ ThisExpression, |
+ TypeParameter, |
+ TypeParameterType, |
+ VariableDeclaration, |
+ VariableGet, |
+ VariableSet; |
+ |
+import '../../visitor.dart' show RecursiveVisitor; |
+ |
+class ClosureInfo extends RecursiveVisitor { |
+ FunctionNode currentFunction; |
+ final Map<VariableDeclaration, FunctionNode> function = |
+ <VariableDeclaration, FunctionNode>{}; |
+ |
+ final Set<VariableDeclaration> variables = new Set<VariableDeclaration>(); |
+ |
+ final Map<FunctionNode, Set<TypeParameter>> typeVariables = |
+ <FunctionNode, Set<TypeParameter>>{}; |
+ |
+ /// Map from members to synthetic variables for accessing `this` in a local |
+ /// function. |
+ final Map<FunctionNode, VariableDeclaration> thisAccess = |
+ <FunctionNode, VariableDeclaration>{}; |
+ |
+ final Set<String> currentMemberLocalNames = new Set<String>(); |
+ |
+ final Map<FunctionNode, String> localNames = <FunctionNode, String>{}; |
+ |
+ /// Contains all names used as getter through a [PropertyGet]. |
+ final Set<Name> invokedGetters = new Set<Name>(); |
+ |
+ /// Contains all names of declared regular instance methods (not including |
+ /// accessors and operators). |
+ final Set<Name> declaredInstanceMethodNames = new Set<Name>(); |
+ |
+ Class currentClass; |
+ |
+ Member currentMember; |
+ |
+ FunctionNode currentMemberFunction; |
+ |
+ bool get isOuterMostContext { |
+ return currentFunction == null || currentMemberFunction == currentFunction; |
+ } |
+ |
+ /// Maps the names of all instance methods that may be torn off (aka |
+ /// implicitly closurized) to `${name.name}#get`. |
+ Map<Name, Name> get tearOffGetterNames { |
+ Map<Name, Name> result = <Name, Name>{}; |
+ for (Name name in declaredInstanceMethodNames) { |
+ if (invokedGetters.contains(name)) { |
+ result[name] = new Name("${name.name}#get", name.library); |
+ } |
+ } |
+ return result; |
+ } |
+ |
+ void beginMember(Member member, [FunctionNode function]) { |
+ currentMemberLocalNames.clear(); |
+ if (function != null) { |
+ localNames[function] = computeUniqueLocalName(member.name.name); |
+ } |
+ currentMember = member; |
+ currentMemberFunction = function; |
+ } |
+ |
+ void endMember() { |
+ currentMember = null; |
+ currentMemberFunction = null; |
+ } |
+ |
+ visitClass(Class node) { |
+ currentClass = node; |
+ super.visitClass(node); |
+ currentClass = null; |
+ } |
+ |
+ visitConstructor(Constructor node) { |
+ beginMember(node, node.function); |
+ super.visitConstructor(node); |
+ endMember(); |
+ } |
+ |
+ visitProcedure(Procedure node) { |
+ beginMember(node, node.function); |
+ if (node.isInstanceMember && node.kind == ProcedureKind.Method) { |
+ // Ignore the `length` method of [File] subclasses for now, as they |
+ // will force us to rename the `length` getter (kernel issue #43). |
+ // TODO(ahe): remove this condition. |
+ Class parent = node.parent; |
+ if (node.name.name != "length" || |
+ parent.enclosingLibrary.importUri.toString() != "dart:io") { |
+ declaredInstanceMethodNames.add(node.name); |
+ } |
+ } |
+ super.visitProcedure(node); |
+ endMember(); |
+ } |
+ |
+ visitField(Field node) { |
+ beginMember(node); |
+ super.visitField(node); |
+ endMember(); |
+ } |
+ |
+ String computeUniqueLocalName([String name]) { |
+ if (name == null || name.isEmpty) { |
+ name = "function"; |
+ } |
+ if (currentFunction == null) { |
+ if (currentMember != null) { |
+ name = "${currentMember.name.name}#$name"; |
+ } |
+ if (currentClass != null) { |
+ name = "${currentClass.name}#$name"; |
+ } |
+ } else { |
+ name = "${localNames[currentFunction]}#$name"; |
+ } |
+ int count = 1; |
+ String candidate = name; |
+ while (currentMemberLocalNames.contains(candidate)) { |
+ candidate = "$name#${count++}"; |
+ } |
+ currentMemberLocalNames.add(candidate); |
+ return candidate; |
+ } |
+ |
+ visitFunctionDeclaration(FunctionDeclaration node) { |
+ assert(!localNames.containsKey(node)); |
+ localNames[node.function] = computeUniqueLocalName(node.variable.name); |
+ return super.visitFunctionDeclaration(node); |
+ } |
+ |
+ visitFunctionNode(FunctionNode node) { |
+ localNames.putIfAbsent(node, computeUniqueLocalName); |
+ var saved = currentFunction; |
+ currentFunction = node; |
+ node.visitChildren(this); |
+ currentFunction = saved; |
+ Set<TypeParameter> capturedTypeVariables = typeVariables[node]; |
+ if (capturedTypeVariables != null && !isOuterMostContext) { |
+ // Propagate captured type variables to enclosing function. |
+ typeVariables |
+ .putIfAbsent(currentFunction, () => new Set<TypeParameter>()) |
+ .addAll(capturedTypeVariables); |
+ } |
+ } |
+ |
+ visitVariableDeclaration(VariableDeclaration node) { |
+ function[node] = currentFunction; |
+ node.visitChildren(this); |
+ } |
+ |
+ visitVariableGet(VariableGet node) { |
+ if (function[node.variable] != currentFunction) { |
+ variables.add(node.variable); |
+ } |
+ node.visitChildren(this); |
+ } |
+ |
+ visitVariableSet(VariableSet node) { |
+ if (function[node.variable] != currentFunction) { |
+ variables.add(node.variable); |
+ } |
+ node.visitChildren(this); |
+ } |
+ |
+ visitTypeParameterType(TypeParameterType node) { |
+ if (!isOuterMostContext) { |
+ typeVariables |
+ .putIfAbsent(currentFunction, () => new Set<TypeParameter>()) |
+ .add(node.parameter); |
+ } |
+ } |
+ |
+ visitThisExpression(ThisExpression node) { |
+ if (!isOuterMostContext) { |
+ thisAccess.putIfAbsent( |
+ currentMemberFunction, () => new VariableDeclaration("#self")); |
+ } |
+ } |
+ |
+ visitPropertyGet(PropertyGet node) { |
+ invokedGetters.add(node.name); |
+ super.visitPropertyGet(node); |
+ } |
+} |