Index: pkg/analyzer/lib/src/generated/constant.dart |
diff --git a/pkg/analyzer/lib/src/generated/constant.dart b/pkg/analyzer/lib/src/generated/constant.dart |
index 24c8f6d963d9dd9f380864b5dab0c06e5dcc027b..001f6cdc30bb4465c530327e7c9f383240da26be 100644 |
--- a/pkg/analyzer/lib/src/generated/constant.dart |
+++ b/pkg/analyzer/lib/src/generated/constant.dart |
@@ -9,7 +9,9 @@ library engine.constant; |
import 'dart:collection'; |
+import 'package:analyzer/src/generated/engine.dart'; |
import 'package:analyzer/src/generated/utilities_general.dart'; |
+import 'package:analyzer/src/task/dart.dart'; |
import 'ast.dart'; |
import 'element.dart'; |
@@ -25,7 +27,7 @@ import 'utilities_dart.dart' show ParameterKind; |
/** |
* Callback used by [ReferenceFinder] to report that a dependency was found. |
*/ |
-typedef void ReferenceFinderCallback(Element dependency); |
+typedef void ReferenceFinderCallback(ConstantEvaluationTarget dependency); |
/** |
* The state of an object representing a boolean value. |
@@ -308,84 +310,118 @@ class ConstantEvaluationEngine { |
} |
/** |
- * Compute the constant value associated with the given [element]. |
+ * Compute the constant value associated with the given [constant]. |
*/ |
- void computeConstantValue(Element element) { |
- validator.beforeComputeValue(element); |
- if (element is ParameterElement) { |
- if (element.initializer != null) { |
+ void computeConstantValue(ConstantEvaluationTarget constant) { |
+ validator.beforeComputeValue(constant); |
+ if (constant is ParameterElement) { |
+ if (constant.initializer != null) { |
Expression defaultValue = |
- (element as PotentiallyConstVariableElement).constantInitializer; |
+ (constant as PotentiallyConstVariableElement).constantInitializer; |
if (defaultValue != null) { |
RecordingErrorListener errorListener = new RecordingErrorListener(); |
ErrorReporter errorReporter = |
- new ErrorReporter(errorListener, element.source); |
+ new ErrorReporter(errorListener, constant.source); |
DartObjectImpl dartObject = |
defaultValue.accept(new ConstantVisitor(this, errorReporter)); |
- (element as ParameterElementImpl).evaluationResult = |
+ (constant as ParameterElementImpl).evaluationResult = |
new EvaluationResultImpl(dartObject, errorListener.errors); |
} |
} |
- } else if (element is VariableElement) { |
+ } else if (constant is VariableElement) { |
RecordingErrorListener errorListener = new RecordingErrorListener(); |
ErrorReporter errorReporter = |
- new ErrorReporter(errorListener, element.source); |
+ new ErrorReporter(errorListener, constant.source); |
DartObjectImpl dartObject = |
- (element as PotentiallyConstVariableElement).constantInitializer |
+ (constant as PotentiallyConstVariableElement).constantInitializer |
.accept(new ConstantVisitor(this, errorReporter)); |
// Only check the type for truly const declarations (don't check final |
// fields with initializers, since their types may be generic. The type |
// of the final field will be checked later, when the constructor is |
// invoked). |
- if (dartObject != null && element.isConst) { |
- if (!runtimeTypeMatch(dartObject, element.type)) { |
+ if (dartObject != null && constant.isConst) { |
+ if (!runtimeTypeMatch(dartObject, constant.type)) { |
errorReporter.reportErrorForElement( |
- CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, element, [ |
- dartObject.type, |
- element.type |
- ]); |
+ CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, constant, |
+ [dartObject.type, constant.type]); |
} |
} |
- (element as VariableElementImpl).evaluationResult = |
+ (constant as VariableElementImpl).evaluationResult = |
new EvaluationResultImpl(dartObject, errorListener.errors); |
- } else if (element is ConstructorElement) { |
+ } else if (constant is ConstructorElement) { |
// No evaluation needs to be done; constructor declarations are only in |
// the dependency graph to ensure that any constants referred to in |
// initializer lists and parameter defaults are evaluated before |
// invocations of the constructor. However we do need to annotate the |
// element as being free of constant evaluation cycles so that later code |
// will know that it is safe to evaluate. |
- (element as ConstructorElementImpl).isCycleFree = true; |
+ (constant as ConstructorElementImpl).isCycleFree = true; |
+ } else if (constant is ConstantEvaluationTarget_Annotation) { |
+ Annotation constNode = constant.annotation; |
+ ElementAnnotationImpl elementAnnotation = constNode.elementAnnotation; |
+ // elementAnnotation is null if the annotation couldn't be resolved, in |
+ // which case we skip it. |
+ if (elementAnnotation != null) { |
+ Element element = elementAnnotation.element; |
+ if (element is PropertyAccessorElement && |
+ element.variable is VariableElementImpl) { |
+ // The annotation is a reference to a compile-time constant variable. |
+ // Just copy the evaluation result. |
+ VariableElementImpl variableElement = |
+ element.variable as VariableElementImpl; |
+ elementAnnotation.evaluationResult = variableElement.evaluationResult; |
+ } else if (element is ConstructorElementImpl && |
+ constNode.arguments != null) { |
+ RecordingErrorListener errorListener = new RecordingErrorListener(); |
+ CompilationUnit sourceCompilationUnit = |
+ constNode.getAncestor((node) => node is CompilationUnit); |
+ ErrorReporter errorReporter = new ErrorReporter( |
+ errorListener, sourceCompilationUnit.element.source); |
+ ConstantVisitor constantVisitor = |
+ new ConstantVisitor(this, errorReporter); |
+ DartObjectImpl result = evaluateConstructorCall(constNode, |
+ constNode.arguments.arguments, element, constantVisitor, |
+ errorReporter); |
+ elementAnnotation.evaluationResult = |
+ new EvaluationResultImpl(result, errorListener.errors); |
+ } else { |
+ // This may happen for invalid code (e.g. failing to pass arguments |
+ // to an annotation which references a const constructor). The error |
+ // is detected elsewhere, so just silently ignore it here. |
+ elementAnnotation.evaluationResult = new EvaluationResultImpl(null); |
+ } |
+ } |
} else { |
// Should not happen. |
assert(false); |
AnalysisEngine.instance.logger.logError( |
- "Constant value computer trying to compute the value of a node of type ${element.runtimeType}"); |
+ "Constant value computer trying to compute the value of a node of type ${constant.runtimeType}"); |
return; |
} |
} |
/** |
* Determine which constant elements need to have their values computed |
- * prior to computing the value of [element], and report them using |
+ * prior to computing the value of [constant], and report them using |
* [callback]. |
*/ |
- void computeDependencies(Element element, ReferenceFinderCallback callback) { |
+ void computeDependencies( |
+ ConstantEvaluationTarget constant, ReferenceFinderCallback callback) { |
ReferenceFinder referenceFinder = new ReferenceFinder(callback); |
- if (element is ParameterElement) { |
- if (element.initializer != null) { |
+ if (constant is ParameterElement) { |
+ if (constant.initializer != null) { |
Expression defaultValue = |
- (element as ConstVariableElement).constantInitializer; |
+ (constant as ConstVariableElement).constantInitializer; |
if (defaultValue != null) { |
defaultValue.accept(referenceFinder); |
} |
} |
- } else if (element is PotentiallyConstVariableElement) { |
- element.constantInitializer.accept(referenceFinder); |
- } else if (element is ConstructorElementImpl) { |
- element.isCycleFree = false; |
+ } else if (constant is PotentiallyConstVariableElement) { |
+ constant.constantInitializer.accept(referenceFinder); |
+ } else if (constant is ConstructorElementImpl) { |
+ constant.isCycleFree = false; |
ConstructorElement redirectedConstructor = |
- getConstRedirectedConstructor(element); |
+ getConstRedirectedConstructor(constant); |
if (redirectedConstructor != null) { |
ConstructorElement redirectedConstructorBase = |
ConstantEvaluationEngine._getConstructorBase(redirectedConstructor); |
@@ -393,7 +429,7 @@ class ConstantEvaluationEngine { |
return; |
} |
bool superInvocationFound = false; |
- List<ConstructorInitializer> initializers = element.constantInitializers; |
+ List<ConstructorInitializer> initializers = constant.constantInitializers; |
for (ConstructorInitializer initializer in initializers) { |
if (initializer is SuperConstructorInvocation) { |
superInvocationFound = true; |
@@ -404,7 +440,7 @@ class ConstantEvaluationEngine { |
// No explicit superconstructor invocation found, so we need to |
// manually insert a reference to the implicit superconstructor. |
InterfaceType superclass = |
- (element.returnType as InterfaceType).superclass; |
+ (constant.returnType as InterfaceType).superclass; |
if (superclass != null && !superclass.isObject) { |
ConstructorElement unnamedConstructor = ConstantEvaluationEngine |
._getConstructorBase(superclass.element.unnamedConstructor); |
@@ -413,7 +449,7 @@ class ConstantEvaluationEngine { |
} |
} |
} |
- for (FieldElement field in element.enclosingElement.fields) { |
+ for (FieldElement field in constant.enclosingElement.fields) { |
// Note: non-static const isn't allowed but we handle it anyway so that |
// we won't be confused by incorrect code. |
if ((field.isFinal || field.isConst) && |
@@ -422,14 +458,40 @@ class ConstantEvaluationEngine { |
callback(field); |
} |
} |
- for (ParameterElement parameterElement in element.parameters) { |
+ for (ParameterElement parameterElement in constant.parameters) { |
callback(parameterElement); |
} |
+ } else if (constant is ConstantEvaluationTarget_Annotation) { |
+ Annotation constNode = constant.annotation; |
+ ElementAnnotationImpl elementAnnotation = constNode.elementAnnotation; |
+ // elementAnnotation is null if the annotation couldn't be resolved, in |
+ // which case we skip it. |
+ if (elementAnnotation != null) { |
+ Element element = elementAnnotation.element; |
+ if (element is PropertyAccessorElement && |
+ element.variable is VariableElementImpl) { |
+ // The annotation is a reference to a compile-time constant variable, |
+ // so it depends on the variable. |
+ callback(element.variable); |
+ } else if (element is ConstructorElementImpl && element.isConst) { |
+ // The annotation is a constructor invocation, so it depends on the |
+ // constructor. |
+ // TODO(paulberry): make sure the right thing happens if the |
+ // constructor is non-const. |
+ callback(element); |
+ } else { |
+ // This could happen in the event of invalid code. The error will be |
+ // reported at constant evaluation time. |
+ } |
+ } |
+ if (constNode.arguments != null) { |
+ constNode.arguments.accept(referenceFinder); |
+ } |
} else { |
// Should not happen. |
assert(false); |
AnalysisEngine.instance.logger.logError( |
- "Constant value computer trying to compute the value of a node of type ${element.runtimeType}"); |
+ "Constant value computer trying to compute the value of a node of type ${constant.runtimeType}"); |
} |
} |
@@ -827,16 +889,30 @@ class ConstantEvaluationEngine { |
} |
/** |
+ * Wrapper around an [Annotation] which can be used as a |
+ * [ConstantEvaluationTarget]. |
+ */ |
+class ConstantEvaluationTarget_Annotation implements ConstantEvaluationTarget { |
+ final AnalysisContext context; |
+ final Source source; |
+ final Source librarySource; |
+ final Annotation annotation; |
+ |
+ ConstantEvaluationTarget_Annotation( |
+ this.context, this.source, this.librarySource, this.annotation); |
+} |
+ |
+/** |
* Interface used by unit tests to verify correct dependency analysis during |
* constant evaluation. |
*/ |
abstract class ConstantEvaluationValidator { |
/** |
* This method is called just before computing the constant value associated |
- * with [element]. Unit tests will override this method to introduce |
+ * with [constant]. Unit tests will override this method to introduce |
* additional error checking. |
*/ |
- void beforeComputeValue(Element element); |
+ void beforeComputeValue(ConstantEvaluationTarget constant); |
/** |
* This method is called just before getting the constant initializers |
@@ -850,7 +926,7 @@ abstract class ConstantEvaluationValidator { |
* element. Unit tests will override it to introduce additional error |
* checking. |
*/ |
- void beforeGetEvaluationResult(Element element); |
+ void beforeGetEvaluationResult(ConstantEvaluationTarget constant); |
/** |
* This method is called just before getting the constant value of a field |
@@ -873,13 +949,13 @@ abstract class ConstantEvaluationValidator { |
class ConstantEvaluationValidator_ForProduction |
implements ConstantEvaluationValidator { |
@override |
- void beforeComputeValue(Element element) {} |
+ void beforeComputeValue(ConstantEvaluationTarget constant) {} |
@override |
void beforeGetConstantInitializers(ConstructorElement constructor) {} |
@override |
- void beforeGetEvaluationResult(Element element) {} |
+ void beforeGetEvaluationResult(ConstantEvaluationTarget constant) {} |
@override |
void beforeGetFieldEvaluationResult(FieldElementImpl field) {} |
@@ -982,26 +1058,28 @@ class ConstantEvaluator { |
* those compilation units. |
*/ |
class ConstantFinder extends RecursiveAstVisitor<Object> { |
- /** |
- * The elements whose constant values need to be computed, with the exception |
- * of annotations. |
- */ |
- HashSet<Element> constantsToCompute = new HashSet<Element>(); |
+ final AnalysisContext context; |
+ final Source source; |
+ final Source librarySource; |
/** |
- * A collection of annotations. |
+ * The elements and AST nodes whose constant values need to be computed. |
*/ |
- final List<Annotation> annotations = <Annotation>[]; |
+ HashSet<ConstantEvaluationTarget> constantsToCompute = |
+ new HashSet<ConstantEvaluationTarget>(); |
/** |
* True if instance variables marked as "final" should be treated as "const". |
*/ |
bool treatFinalInstanceVarAsConst = false; |
+ ConstantFinder(this.context, this.source, this.librarySource); |
+ |
@override |
Object visitAnnotation(Annotation node) { |
super.visitAnnotation(node); |
- annotations.add(node); |
+ constantsToCompute.add(new ConstantEvaluationTarget_Annotation( |
+ context, source, librarySource, node)); |
return null; |
} |
@@ -1077,16 +1155,11 @@ class ConstantValueComputer { |
"(?:assert|break|c(?:a(?:se|tch)|lass|on(?:st|tinue))|d(?:efault|o)|e(?:lse|num|xtends)|f(?:alse|inal(?:ly)?|or)|i[fns]|n(?:ew|ull)|ret(?:hrow|urn)|s(?:uper|witch)|t(?:h(?:is|row)|r(?:ue|y))|v(?:ar|oid)|w(?:hile|ith))"; |
/** |
- * The object used to find constant variables and constant constructor |
- * invocations in the compilation units that were added. |
- */ |
- ConstantFinder _constantFinder = new ConstantFinder(); |
- |
- /** |
* A graph in which the nodes are the constants, and the edges are from each |
* constant to the other constants that are referenced by it. |
*/ |
- DirectedGraph<Element> referenceGraph = new DirectedGraph<Element>(); |
+ DirectedGraph<ConstantEvaluationTarget> referenceGraph = |
+ new DirectedGraph<ConstantEvaluationTarget>(); |
/** |
* The elements whose constant values need to be computed. Any elements |
@@ -1095,12 +1168,8 @@ class ConstantValueComputer { |
* computed during a previous stage of resolution stage (e.g. constants |
* associated with enums). |
*/ |
- HashSet<Element> _constantsToCompute; |
- |
- /** |
- * A collection of annotations. |
- */ |
- List<Annotation> _annotations; |
+ HashSet<ConstantEvaluationTarget> _constantsToCompute = |
+ new HashSet<ConstantEvaluationTarget>(); |
/** |
* The evaluation engine that does the work of evaluating instance creation |
@@ -1108,13 +1177,15 @@ class ConstantValueComputer { |
*/ |
final ConstantEvaluationEngine evaluationEngine; |
+ final AnalysisContext _context; |
+ |
/** |
* Initialize a newly created constant value computer. The [typeProvider] is |
* the type provider used to access known types. The [declaredVariables] is |
* the set of variables declared on the command line using '-D'. |
*/ |
- ConstantValueComputer( |
- TypeProvider typeProvider, DeclaredVariables declaredVariables, |
+ ConstantValueComputer(this._context, TypeProvider typeProvider, |
+ DeclaredVariables declaredVariables, |
[ConstantEvaluationValidator validator]) |
: evaluationEngine = new ConstantEvaluationEngine( |
typeProvider, declaredVariables, validator: validator); |
@@ -1123,8 +1194,11 @@ class ConstantValueComputer { |
* Add the constants in the given compilation [unit] to the list of constants |
* whose value needs to be computed. |
*/ |
- void add(CompilationUnit unit) { |
- unit.accept(_constantFinder); |
+ void add(CompilationUnit unit, Source source, Source librarySource) { |
+ ConstantFinder constantFinder = |
+ new ConstantFinder(_context, source, librarySource); |
+ unit.accept(constantFinder); |
+ _constantsToCompute.addAll(constantFinder.constantsToCompute); |
} |
/** |
@@ -1132,103 +1206,58 @@ class ConstantValueComputer { |
* added. |
*/ |
void computeValues() { |
- _constantsToCompute = _constantFinder.constantsToCompute; |
- _annotations = _constantFinder.annotations; |
- for (Element element in _constantsToCompute) { |
- referenceGraph.addNode(element); |
- evaluationEngine.computeDependencies(element, (Element dependency) { |
- referenceGraph.addEdge(element, dependency); |
+ for (ConstantEvaluationTarget constant in _constantsToCompute) { |
+ referenceGraph.addNode(constant); |
+ evaluationEngine.computeDependencies(constant, |
+ (ConstantEvaluationTarget dependency) { |
+ referenceGraph.addEdge(constant, dependency); |
}); |
} |
- List<List<Element>> topologicalSort = |
+ List<List<ConstantEvaluationTarget>> topologicalSort = |
referenceGraph.computeTopologicalSort(); |
- for (List<Element> constantsInCycle in topologicalSort) { |
+ for (List<ConstantEvaluationTarget> constantsInCycle in topologicalSort) { |
if (constantsInCycle.length == 1) { |
_computeValueFor(constantsInCycle[0]); |
} else { |
- for (Element constant in constantsInCycle) { |
+ for (ConstantEvaluationTarget constant in constantsInCycle) { |
_generateCycleError(constantsInCycle, constant); |
} |
} |
} |
- // Since no constant can depend on an annotation, we don't waste time |
- // including them in the topological sort. We just process all the |
- // annotations after all other constants are finished. |
- for (Annotation annotation in _annotations) { |
- _computeValueForAnnotation(annotation); |
- } |
} |
/** |
- * Compute a value for the given [element]. |
+ * Compute a value for the given [constant]. |
*/ |
- void _computeValueFor(Element element) { |
- if (!_constantsToCompute.contains(element)) { |
+ void _computeValueFor(ConstantEvaluationTarget constant) { |
+ if (!_constantsToCompute.contains(constant)) { |
// Element is in the dependency graph but should have been computed by |
// a previous stage of analysis. |
return; |
Brian Wilkerson
2015/05/11 18:43:32
This sounds like a problem. Should we be logging t
Paul Berry
2015/05/11 19:24:51
Actually, it's not a problem. There are two situa
|
} |
- evaluationEngine.computeConstantValue(element); |
- } |
- |
- /** |
- * Compute a value for the given annotation. |
- */ |
- void _computeValueForAnnotation(Annotation constNode) { |
- ElementAnnotationImpl elementAnnotation = constNode.elementAnnotation; |
- // elementAnnotation is null if the annotation couldn't be resolved, in |
- // which case we skip it. |
- if (elementAnnotation != null) { |
- Element element = elementAnnotation.element; |
- if (element is PropertyAccessorElement && |
- element.variable is VariableElementImpl) { |
- // The annotation is a reference to a compile-time constant variable. |
- // Just copy the evaluation result. |
- VariableElementImpl variableElement = |
- element.variable as VariableElementImpl; |
- elementAnnotation.evaluationResult = variableElement.evaluationResult; |
- } else if (element is ConstructorElementImpl && |
- constNode.arguments != null) { |
- RecordingErrorListener errorListener = new RecordingErrorListener(); |
- CompilationUnit sourceCompilationUnit = |
- constNode.getAncestor((node) => node is CompilationUnit); |
- ErrorReporter errorReporter = new ErrorReporter( |
- errorListener, sourceCompilationUnit.element.source); |
- ConstantVisitor constantVisitor = |
- new ConstantVisitor(evaluationEngine, errorReporter); |
- DartObjectImpl result = evaluationEngine.evaluateConstructorCall( |
- constNode, constNode.arguments.arguments, element, constantVisitor, |
- errorReporter); |
- elementAnnotation.evaluationResult = |
- new EvaluationResultImpl(result, errorListener.errors); |
- } else { |
- // This may happen for invalid code (e.g. failing to pass arguments |
- // to an annotation which references a const constructor). The error |
- // is detected elsewhere, so just silently ignore it here. |
- elementAnnotation.evaluationResult = new EvaluationResultImpl(null); |
- } |
- } |
+ evaluationEngine.computeConstantValue(constant); |
} |
/** |
- * Generate an error indicating that the given [constNode] is not a valid |
+ * Generate an error indicating that the given [constant] is not a valid |
* compile-time constant because it references at least one of the constants |
* in the given [cycle], each of which directly or indirectly references the |
* constant. |
*/ |
- void _generateCycleError(List<Element> cycle, Element element) { |
- if (element is VariableElement) { |
+ void _generateCycleError( |
+ List<ConstantEvaluationTarget> cycle, ConstantEvaluationTarget constant) { |
+ if (constant is VariableElement) { |
RecordingErrorListener errorListener = new RecordingErrorListener(); |
ErrorReporter errorReporter = |
- new ErrorReporter(errorListener, element.source); |
+ new ErrorReporter(errorListener, constant.source); |
// TODO(paulberry): It would be really nice if we could extract enough |
// information from the 'cycle' argument to provide the user with a |
// description of the cycle. |
errorReporter.reportErrorForElement( |
- CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, element, []); |
- (element as VariableElementImpl).evaluationResult = |
+ CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, constant, []); |
+ (constant as VariableElementImpl).evaluationResult = |
new EvaluationResultImpl(null, errorListener.errors); |
- } else if (element is ConstructorElement) { |
+ } else if (constant is ConstructorElement) { |
// We don't report cycle errors on constructor declarations since there |
// is nowhere to put the error information. |
} else { |
@@ -1236,7 +1265,7 @@ class ConstantValueComputer { |
// never appear as part of a cycle because they can't be referred to. |
assert(false); |
AnalysisEngine.instance.logger.logError( |
- "Constant value computer trying to report a cycle error for a node of type ${element.runtimeType}"); |
+ "Constant value computer trying to report a cycle error for a node of type ${constant.runtimeType}"); |
} |
} |
} |