| 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 |