Chromium Code Reviews| 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) { |