Index: lib/src/codegen/side_effect_analysis.dart |
diff --git a/lib/src/codegen/side_effect_analysis.dart b/lib/src/codegen/side_effect_analysis.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4cccd421fccccdeddabf066261ee7d729f32a1cf |
--- /dev/null |
+++ b/lib/src/codegen/side_effect_analysis.dart |
@@ -0,0 +1,86 @@ |
+// Copyright (c) 2015, 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 dev_compiler.src.codegen.side_effect_analysis; |
+ |
+import 'package:analyzer/src/generated/ast.dart'; |
+import 'package:analyzer/src/generated/element.dart'; |
+ |
+/// True is the expression can be evaluated multiple times without causing |
+/// code execution. This is true for final fields. This can be true for local |
+/// variables, if: |
+/// * they are not assigned within the [context]. |
+/// * they are not assigned in a function closure anywhere. |
+/// True is the expression can be evaluated multiple times without causing |
+/// code execution. This is true for final fields. This can be true for local |
+/// variables, if: |
+/// |
+/// * they are not assigned within the [context] scope. |
+/// * they are not assigned in a function closure anywhere. |
+/// |
+/// This method is used to avoid creating temporaries in cases where we know |
+/// we can safely re-evaluate [node] multiple times in [context]. This lets |
+/// us generate prettier code. |
+/// |
+/// This method is conservative: it should never return `true` unless it is |
+/// certain the [node] is stateless, because generated code may rely on the |
+/// correctness of a `true` value. However it may return `false` for things |
+/// that are in fact, stateless. |
+bool isStateless(Expression node, [AstNode context]) { |
+ if (node is SimpleIdentifier) { |
+ var e = node.staticElement; |
+ if (e is PropertyAccessorElement) e = e.variable; |
+ if (e is VariableElement && !e.isSynthetic) { |
+ if (e.isFinal) return true; |
+ if (e is LocalVariableElement || e is ParameterElement) { |
+ // make sure the local isn't mutated in the context. |
+ return !_isPotentiallyMutated(e, context); |
+ } |
+ } |
+ } |
+ return false; |
+} |
+ |
+/// Returns true if the local variable is potentially mutated within [context]. |
+/// This accounts for closures that may have been created outside of [context]. |
+bool _isPotentiallyMutated(VariableElement e, [AstNode context]) { |
+ if (e.isPotentiallyMutatedInClosure) return true; |
+ if (e.isPotentiallyMutatedInScope) { |
+ // Need to visit the context looking for assignment to this local. |
+ if (context != null) { |
+ var visitor = new _AssignmentFinder(e); |
+ context.accept(visitor); |
+ return visitor._potentiallyMutated; |
+ } |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+/// Adapted from VariableResolverVisitor. Finds an assignment to a given |
+/// local variable. |
+class _AssignmentFinder extends RecursiveAstVisitor { |
+ final VariableElement _variable; |
+ bool _potentiallyMutated = false; |
+ |
+ _AssignmentFinder(this._variable); |
+ |
+ @override |
+ visitSimpleIdentifier(SimpleIdentifier node) { |
+ // Ignore if qualified. |
+ AstNode parent = node.parent; |
+ if (parent is PrefixedIdentifier && |
+ identical(parent.identifier, node)) return; |
+ if (parent is PropertyAccess && |
+ identical(parent.propertyName, node)) return; |
+ if (parent is MethodInvocation && |
+ identical(parent.methodName, node)) return; |
+ if (parent is ConstructorName) return; |
+ if (parent is Label) return; |
+ |
+ if (node.inSetterContext() && node.staticElement == _variable) { |
+ _potentiallyMutated = true; |
+ } |
+ } |
+} |