OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 import 'package:analyzer/dart/ast/ast.dart'; | |
6 import 'package:analyzer/dart/ast/visitor.dart'; | |
7 import 'package:analyzer/dart/element/element.dart'; | |
8 import 'package:analyzer/src/generated/constant.dart'; | |
9 import 'package:analyzer/src/generated/error.dart' | |
10 show AnalysisErrorListener, ErrorReporter; | |
11 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | |
12 import 'package:analyzer/src/generated/source.dart' show Source; | |
13 import 'package:analyzer/src/dart/ast/ast.dart'; | |
14 | |
15 /// True is the expression can be evaluated multiple times without causing | |
16 /// code execution. This is true for final fields. This can be true for local | |
17 /// variables, if: | |
18 /// * they are not assigned within the [context]. | |
19 /// * they are not assigned in a function closure anywhere. | |
20 /// True is the expression can be evaluated multiple times without causing | |
21 /// code execution. This is true for final fields. This can be true for local | |
22 /// variables, if: | |
23 /// | |
24 /// * they are not assigned within the [context] scope. | |
25 /// * they are not assigned in a function closure anywhere. | |
26 /// | |
27 /// This method is used to avoid creating temporaries in cases where we know | |
28 /// we can safely re-evaluate [node] multiple times in [context]. This lets | |
29 /// us generate prettier code. | |
30 /// | |
31 /// This method is conservative: it should never return `true` unless it is | |
32 /// certain the [node] is stateless, because generated code may rely on the | |
33 /// correctness of a `true` value. However it may return `false` for things | |
34 /// that are in fact, stateless. | |
35 bool isStateless(FunctionBody function, Expression node, [AstNode context]) { | |
36 // `this` and `super` cannot be reassigned. | |
37 if (node is ThisExpression || node is SuperExpression) return true; | |
38 if (node is Identifier) { | |
39 var e = node.staticElement; | |
40 if (e is PropertyAccessorElement) { | |
41 e = e.variable; | |
42 } | |
43 if (e is VariableElement && !e.isSynthetic) { | |
44 if (e.isFinal) return true; | |
45 if (e is LocalVariableElement || e is ParameterElement) { | |
46 // make sure the local isn't mutated in the context. | |
47 return !_isPotentiallyMutated(function, e, context); | |
48 } | |
49 } | |
50 } | |
51 return false; | |
52 } | |
53 | |
54 /// Returns true if the local variable is potentially mutated within [context]. | |
55 /// This accounts for closures that may have been created outside of [context]. | |
56 bool _isPotentiallyMutated(FunctionBody function, VariableElement e, | |
57 [AstNode context]) { | |
58 if (function is FunctionBodyImpl && function.localVariableInfo == null) { | |
59 // TODO(jmesserly): this is a caching bug in Analyzer. They don't restore | |
60 // this info in some cases. | |
61 return true; | |
62 } | |
63 | |
64 if (function.isPotentiallyMutatedInClosure(e)) return true; | |
65 if (function.isPotentiallyMutatedInScope(e)) { | |
66 // Need to visit the context looking for assignment to this local. | |
67 if (context != null) { | |
68 var visitor = new _AssignmentFinder(e); | |
69 context.accept(visitor); | |
70 return visitor._potentiallyMutated; | |
71 } | |
72 return true; | |
73 } | |
74 return false; | |
75 } | |
76 | |
77 /// Adapted from VariableResolverVisitor. Finds an assignment to a given | |
78 /// local variable. | |
79 class _AssignmentFinder extends RecursiveAstVisitor { | |
80 final VariableElement _variable; | |
81 bool _potentiallyMutated = false; | |
82 | |
83 _AssignmentFinder(this._variable); | |
84 | |
85 @override | |
86 visitSimpleIdentifier(SimpleIdentifier node) { | |
87 // Ignore if qualified. | |
88 AstNode parent = node.parent; | |
89 if (parent is PrefixedIdentifier && identical(parent.identifier, node)) { | |
90 return; | |
91 } | |
92 if (parent is PropertyAccess && identical(parent.propertyName, node)) { | |
93 return; | |
94 } | |
95 if (parent is MethodInvocation && identical(parent.methodName, node)) { | |
96 return; | |
97 } | |
98 if (parent is ConstructorName) return; | |
99 if (parent is Label) return; | |
100 | |
101 if (node.inSetterContext() && node.staticElement == _variable) { | |
102 _potentiallyMutated = true; | |
103 } | |
104 } | |
105 } | |
106 | |
107 class ConstFieldVisitor { | |
108 final ConstantVisitor _constantVisitor; | |
109 | |
110 ConstFieldVisitor(TypeProvider types, Source source) | |
111 // TODO(jmesserly): support -D variables on the command line | |
112 : _constantVisitor = new ConstantVisitor( | |
113 new ConstantEvaluationEngine(types, new DeclaredVariables()), | |
114 new ErrorReporter(AnalysisErrorListener.NULL_LISTENER, source)); | |
115 | |
116 // TODO(jmesserly): this is used to determine if the field initialization is | |
117 // side effect free. We should make the check more general, as things like | |
118 // list/map literals/regexp are also side effect free and fairly common | |
119 // to use as field initializers. | |
120 bool isFieldInitConstant(VariableDeclaration field) => | |
121 field.initializer == null || computeConstant(field) != null; | |
122 | |
123 DartObject computeConstant(VariableDeclaration field) { | |
124 // If the constant is already computed by ConstantEvaluator, just return it. | |
125 VariableElement element = field.element; | |
126 var result = element.constantValue; | |
127 if (result != null) return result; | |
128 | |
129 // ConstantEvaluator will not compute constants for non-const fields, | |
130 // so run ConstantVisitor for those to figure out if the initializer is | |
131 // actually a constant (and therefore side effect free to evaluate). | |
132 assert(!field.isConst); | |
133 | |
134 var initializer = field.initializer; | |
135 if (initializer == null) return null; | |
136 return initializer.accept(_constantVisitor); | |
137 } | |
138 } | |
OLD | NEW |