Index: sdk/lib/_internal/compiler/implementation/resolution/members.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart |
index f09879ab20914fcddc018b49ab112a750cde9c09..e6a1c2096bc227c0884a25b2ac2b97ba8a582614 100644 |
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart |
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart |
@@ -39,6 +39,19 @@ abstract class TreeElements { |
/// Register additional dependencies required by [currentElement]. |
/// For example, elements that are used by a backend. |
void registerDependency(Element element); |
+ |
+ /// Returns [:true:] if [element] is potentially mutated anywhere in its |
+ /// scope. |
+ bool isPotentiallyMutated(VariableElement element); |
+ |
+ /// Returns [:true:] if [element] is potentially mutated in [node]. |
+ bool isPotentiallyMutatedIn(Node node, VariableElement element); |
+ |
+ /// Returns [:true:] if [element] is potentially mutated in a closure. |
+ bool isPotentiallyMutatedInClosure(VariableElement element); |
+ |
+ /// Returns [:true:] if [element] is accessed by a closure in [node]. |
+ bool isAccessedByClosureIn(Node node, VariableElement element); |
} |
class TreeElementMapping implements TreeElements { |
@@ -49,6 +62,13 @@ class TreeElementMapping implements TreeElements { |
final Set<Node> superUses = new LinkedHashSet<Node>(); |
final Set<Element> otherDependencies = new LinkedHashSet<Element>(); |
final Map<Node, Constant> constants = new Map<Node, Constant>(); |
+ final Set<VariableElement> potentiallyMutated = new Set<VariableElement>(); |
+ final Map<Node, Set<VariableElement>> potentiallyMutatedIn = |
+ new Map<Node, Set<VariableElement>>(); |
+ final Set<VariableElement> potentiallyMutatedInClosure = |
+ new Set<VariableElement>(); |
+ final Map<Node, Set<VariableElement>> accessedByClosureIn = |
+ new Map<Node, Set<VariableElement>>(); |
final int hashCode = ++hashCodeCounter; |
static int hashCodeCounter = 0; |
@@ -159,6 +179,42 @@ class TreeElementMapping implements TreeElements { |
otherDependencies.add(element.implementation); |
} |
+ bool isPotentiallyMutated(VariableElement element) { |
+ return potentiallyMutated.contains(element); |
+ } |
+ |
+ void setPotentiallyMutated(VariableElement element) { |
+ potentiallyMutated.add(element); |
+ } |
+ |
+ bool isPotentiallyMutatedIn(Node node, VariableElement element) { |
+ Set<Element> mutatedIn = potentiallyMutatedIn[node]; |
+ return mutatedIn != null && mutatedIn.contains(element); |
+ } |
+ |
+ void setPotentiallyMutatedIn(Node node, VariableElement element) { |
+ potentiallyMutatedIn.putIfAbsent( |
+ node, () => new Set<VariableElement>()).add(element); |
+ } |
+ |
+ bool isPotentiallyMutatedInClosure(VariableElement element) { |
+ return potentiallyMutatedInClosure.contains(element); |
+ } |
+ |
+ void setPotentiallyMutatedInClosure(VariableElement element) { |
+ potentiallyMutatedInClosure.add(element); |
+ } |
+ |
+ bool isAccessedByClosureIn(Node node, VariableElement element) { |
+ Set<Element> accessedIn = accessedByClosureIn[node]; |
+ return accessedIn != null && accessedIn.contains(element); |
+ } |
+ |
+ void setAccessedByClosureIn(Node node, VariableElement element) { |
+ accessedByClosureIn.putIfAbsent( |
+ node, () => new Set<VariableElement>()).add(element); |
+ } |
+ |
String toString() => 'TreeElementMapping($currentElement)'; |
} |
@@ -1846,6 +1902,16 @@ class ResolverVisitor extends MappingVisitor<Element> { |
/// error when verifying that all final variables are initialized. |
bool allowFinalWithoutInitializer = false; |
+ /// The nodes for which variable access and mutation must be registed in order |
karlklose
2013/10/15 11:56:17
'registed' -> 'registered'.
Johnni Winther
2013/10/15 15:57:00
Done.
|
+ /// to determine when variable types are promoted. |
karlklose
2013/10/15 11:56:17
'when the static type of variables is promoted.'
Johnni Winther
2013/10/15 15:57:00
Done.
|
+ Link<Node> promotionScope = const Link<Node>(); |
+ |
+ bool isPotentiallyMutableTarget(Element target) { |
+ if (target == null) return false; |
+ return (target.isVariable() || target.isParameter()) && |
+ !(target.modifiers.isFinal() || target.modifiers.isConst()); |
+ } |
+ |
// TODO(ahe): Find a way to share this with runtime implementation. |
static final RegExp symbolValidationPattern = |
new RegExp(r'^(?:[a-zA-Z$][a-zA-Z$0-9_]*\.)*(?:[a-zA-Z$][a-zA-Z$0-9_]*=?|' |
@@ -1944,6 +2010,13 @@ class ResolverVisitor extends MappingVisitor<Element> { |
return result; |
} |
+ doInPromotionScope(Node node, action()) { |
+ promotionScope = promotionScope.prepend(node); |
+ var result = action(); |
+ promotionScope = promotionScope.tail; |
+ return result; |
+ } |
+ |
visitInStaticContext(Node node) { |
inStaticContext(() => visit(node)); |
} |
@@ -2186,8 +2259,9 @@ class ResolverVisitor extends MappingVisitor<Element> { |
} |
visitIf(If node) { |
- visit(node.condition); |
- visitIn(node.thenPart, new BlockScope(scope)); |
+ doInPromotionScope(node.condition.expression, () => visit(node.condition)); |
+ doInPromotionScope(node.thenPart, () => |
karlklose
2013/10/15 11:56:17
Break line before closure.
Johnni Winther
2013/10/15 15:57:00
Done.
|
+ visitIn(node.thenPart, new BlockScope(scope))); |
visitIn(node.elsePart, new BlockScope(scope)); |
} |
@@ -2413,7 +2487,12 @@ class ResolverVisitor extends MappingVisitor<Element> { |
visitSend(Send node) { |
bool oldSendIsMemberAccess = sendIsMemberAccess; |
sendIsMemberAccess = node.isPropertyAccess || node.isCall; |
- Element target = resolveSend(node); |
+ Element target; |
+ if (node.isLogicalAnd) { |
+ target = doInPromotionScope(node.receiver, () => resolveSend(node)); |
+ } else { |
+ target = resolveSend(node); |
+ } |
sendIsMemberAccess = oldSendIsMemberAccess; |
if (target != null |
@@ -2453,6 +2532,13 @@ class ResolverVisitor extends MappingVisitor<Element> { |
// Don't try to make constants of calls to type literals. |
analyzeConstant(node, isConst: !node.isCall); |
} |
+ if (isPotentiallyMutableTarget(target)) { |
+ if (enclosingElement != target.enclosingElement) { |
+ for (Node scope in promotionScope) { |
+ mapping.setAccessedByClosureIn(scope, target); |
+ } |
+ } |
+ } |
} |
bool resolvedArguments = false; |
@@ -2471,6 +2557,10 @@ class ResolverVisitor extends MappingVisitor<Element> { |
compiler.enqueuer.resolution.registerAsCheck(type, mapping); |
} |
resolvedArguments = true; |
+ } else if (identical(operatorString, '&&')) { |
+ doInPromotionScope(node.arguments.head, |
+ () => resolveArguments(node.argumentsNode)); |
+ resolvedArguments = true; |
} |
} |
@@ -2573,6 +2663,15 @@ class ResolverVisitor extends MappingVisitor<Element> { |
node.selector, target.name, MessageKind.CANNOT_RESOLVE_SETTER); |
compiler.backend.registerThrowNoSuchMethod(mapping); |
} |
+ if (isPotentiallyMutableTarget(target)) { |
+ mapping.setPotentiallyMutated(target); |
+ if (enclosingElement != target.enclosingElement) { |
+ mapping.setPotentiallyMutatedInClosure(target); |
+ } |
+ for (Node scope in promotionScope) { |
+ mapping.setPotentiallyMutatedIn(scope, target); |
+ } |
+ } |
} |
visit(node.argumentsNode); |
@@ -2999,7 +3098,9 @@ class ResolverVisitor extends MappingVisitor<Element> { |
} |
visitConditional(Conditional node) { |
- node.visitChildren(this); |
+ doInPromotionScope(node.condition, () => visit(node.condition)); |
+ doInPromotionScope(node.thenExpression, () => visit(node.thenExpression)); |
+ visit(node.elseExpression); |
} |
visitStringInterpolation(StringInterpolation node) { |