Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Unified Diff: pkg/analyzer/lib/src/generated/constant.dart

Issue 1134133002: Unify evaluation of annotations with evaluation of other constants. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/generated/element.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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}");
}
}
}
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/generated/element.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698