Store elements in the constant eval dependency graph rather than AST nodes.

This paves the way for constant evaluation to be integrated into the
new task model, since the new tasks for constants will use elements as
their targets.

Since the element model makes a distinction between constructor
members and constructor elements, we have to be careful to be
consistent about what kinds of elements are stored in the dependency
graph.  We always store constructor elements in the dependency graph.

R=brianwilkerson@google.com

Review URL: https://codereview.chromium.org//1129143003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45611 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
paulberry@google.com 2015-05-07 19:38:37 +00:00
parent 1bc667b086
commit 7040257579
3 changed files with 260 additions and 362 deletions

View file

@ -428,8 +428,8 @@ class ConstantEvaluationEngine {
// it an unknown value will suppress further errors.
return new DartObjectImpl.validWithUnknownValue(definingClass);
}
validator.beforeGetConstantInitializers(constructor);
ConstructorElementImpl constructorBase = _getConstructorBase(constructor);
validator.beforeGetConstantInitializers(constructorBase);
List<ConstructorInitializer> initializers =
constructorBase.constantInitializers;
if (initializers == null) {
@ -702,10 +702,10 @@ class ConstantEvaluationEngine {
abstract class ConstantEvaluationValidator {
/**
* This method is called just before computing the constant value associated
* with [constNode]. Unit tests will override this method to introduce
* with [element]. Unit tests will override this method to introduce
* additional error checking.
*/
void beforeComputeValue(AstNode constNode);
void beforeComputeValue(Element element);
/**
* This method is called just before getting the constant initializers
@ -716,10 +716,10 @@ abstract class ConstantEvaluationValidator {
/**
* This method is called just before retrieving an evaluation result from an
* AST node. Unit tests will override it to introduce additional error
* element. Unit tests will override it to introduce additional error
* checking.
*/
void beforeGetEvaluationResult(AstNode node);
void beforeGetEvaluationResult(Element element);
/**
* This method is called just before getting the constant value of a field
@ -742,13 +742,13 @@ abstract class ConstantEvaluationValidator {
class ConstantEvaluationValidator_ForProduction
implements ConstantEvaluationValidator {
@override
void beforeComputeValue(AstNode constNode) {}
void beforeComputeValue(Element element) {}
@override
void beforeGetConstantInitializers(ConstructorElement constructor) {}
@override
void beforeGetEvaluationResult(AstNode node) {}
void beforeGetEvaluationResult(Element element) {}
@override
void beforeGetFieldEvaluationResult(FieldElementImpl field) {}
@ -852,18 +852,10 @@ class ConstantEvaluator {
*/
class ConstantFinder extends RecursiveAstVisitor<Object> {
/**
* A table mapping constant variable elements to the declarations of those
* variables.
* The elements whose constant values need to be computed, with the exception
* of annotations.
*/
final HashMap<PotentiallyConstVariableElement, VariableDeclaration> variableMap =
new HashMap<PotentiallyConstVariableElement, VariableDeclaration>();
/**
* A table mapping constant constructors to the declarations of those
* constructors.
*/
final HashMap<ConstructorElement, ConstructorDeclaration> constructorMap =
new HashMap<ConstructorElement, ConstructorDeclaration>();
HashSet<Element> constantsToCompute = new HashSet<Element>();
/**
* A collection of annotations.
@ -904,7 +896,8 @@ class ConstantFinder extends RecursiveAstVisitor<Object> {
if (node.constKeyword != null) {
ConstructorElement element = node.element;
if (element != null) {
constructorMap[element] = node;
constantsToCompute.add(element);
constantsToCompute.addAll(element.parameters);
}
}
return null;
@ -922,7 +915,7 @@ class ConstantFinder extends RecursiveAstVisitor<Object> {
node.isFinal &&
!element.isStatic)) {
if (node.element != null) {
variableMap[node.element as PotentiallyConstVariableElement] = node;
constantsToCompute.add(node.element);
}
}
return null;
@ -962,18 +955,16 @@ class ConstantValueComputer {
* 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<AstNode> referenceGraph = new DirectedGraph<AstNode>();
DirectedGraph<Element> referenceGraph = new DirectedGraph<Element>();
/**
* A table mapping constant variables to the declarations of those variables.
* The elements whose constant values need to be computed. Any elements
* which appear in [referenceGraph] but not in this set either belong to a
* different library cycle (and hence don't need to be recomputed) or were
* computed during a previous stage of resolution stage (e.g. constants
* associated with enums).
*/
HashMap<PotentiallyConstVariableElement, VariableDeclaration> _variableDeclarationMap;
/**
* A table mapping constant constructors to the declarations of those
* constructors.
*/
HashMap<ConstructorElement, ConstructorDeclaration> constructorDeclarationMap;
HashSet<Element> _constantsToCompute;
/**
* A collection of annotations.
@ -1010,85 +1001,85 @@ class ConstantValueComputer {
* added.
*/
void computeValues() {
_variableDeclarationMap = _constantFinder.variableMap;
constructorDeclarationMap = _constantFinder.constructorMap;
_constantsToCompute = _constantFinder.constantsToCompute;
_annotations = _constantFinder.annotations;
_variableDeclarationMap.values.forEach((VariableDeclaration declaration) {
ReferenceFinder referenceFinder = new ReferenceFinder(declaration,
referenceGraph, _variableDeclarationMap, constructorDeclarationMap);
referenceGraph.addNode(declaration);
declaration.initializer.accept(referenceFinder);
});
constructorDeclarationMap.forEach((ConstructorElementImpl element,
ConstructorDeclaration declaration) {
element.isCycleFree = false;
ConstructorElement redirectedConstructor =
evaluationEngine.getConstRedirectedConstructor(element);
if (redirectedConstructor != null) {
ConstructorElement redirectedConstructorBase =
ConstantEvaluationEngine._getConstructorBase(redirectedConstructor);
ConstructorDeclaration redirectedConstructorDeclaration =
findConstructorDeclaration(redirectedConstructorBase);
referenceGraph.addEdge(declaration, redirectedConstructorDeclaration);
return;
}
ReferenceFinder referenceFinder = new ReferenceFinder(declaration,
referenceGraph, _variableDeclarationMap, constructorDeclarationMap);
referenceGraph.addNode(declaration);
bool superInvocationFound = false;
NodeList<ConstructorInitializer> initializers = declaration.initializers;
for (ConstructorInitializer initializer in initializers) {
if (initializer is SuperConstructorInvocation) {
superInvocationFound = true;
}
initializer.accept(referenceFinder);
}
if (!superInvocationFound) {
// No explicit superconstructor invocation found, so we need to
// manually insert a reference to the implicit superconstructor.
InterfaceType superclass =
(element.returnType as InterfaceType).superclass;
if (superclass != null && !superclass.isObject) {
ConstructorElement unnamedConstructor =
superclass.element.unnamedConstructor;
ConstructorDeclaration superConstructorDeclaration =
findConstructorDeclaration(unnamedConstructor);
if (superConstructorDeclaration != null) {
referenceGraph.addEdge(declaration, superConstructorDeclaration);
}
}
}
for (FieldElement field in element.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) && !field.isStatic) {
VariableDeclaration fieldDeclaration = _variableDeclarationMap[field];
if (fieldDeclaration != null) {
referenceGraph.addEdge(declaration, fieldDeclaration);
}
}
}
for (FormalParameter parameter in declaration.parameters.parameters) {
referenceGraph.addNode(parameter);
referenceGraph.addEdge(declaration, parameter);
if (parameter is DefaultFormalParameter) {
Expression defaultValue = parameter.defaultValue;
for (Element element in _constantsToCompute) {
referenceGraph.addNode(element);
if (element is ParameterElement) {
if (element.initializer != null) {
Expression defaultValue =
(element as ConstVariableElement).constantInitializer;
if (defaultValue != null) {
ReferenceFinder parameterReferenceFinder = new ReferenceFinder(
parameter, referenceGraph, _variableDeclarationMap,
constructorDeclarationMap);
ReferenceFinder parameterReferenceFinder =
new ReferenceFinder(element, referenceGraph);
defaultValue.accept(parameterReferenceFinder);
}
}
} else if (element is PotentiallyConstVariableElement) {
ReferenceFinder referenceFinder =
new ReferenceFinder(element, referenceGraph);
element.constantInitializer.accept(referenceFinder);
} else if (element is ConstructorElementImpl) {
element.isCycleFree = false;
ConstructorElement redirectedConstructor =
evaluationEngine.getConstRedirectedConstructor(element);
if (redirectedConstructor != null) {
ConstructorElement redirectedConstructorBase =
ConstantEvaluationEngine
._getConstructorBase(redirectedConstructor);
referenceGraph.addEdge(element, redirectedConstructorBase);
continue;
}
ReferenceFinder referenceFinder =
new ReferenceFinder(element, referenceGraph);
bool superInvocationFound = false;
List<ConstructorInitializer> initializers =
element.constantInitializers;
for (ConstructorInitializer initializer in initializers) {
if (initializer is SuperConstructorInvocation) {
superInvocationFound = true;
}
initializer.accept(referenceFinder);
}
if (!superInvocationFound) {
// No explicit superconstructor invocation found, so we need to
// manually insert a reference to the implicit superconstructor.
InterfaceType superclass =
(element.returnType as InterfaceType).superclass;
if (superclass != null && !superclass.isObject) {
ConstructorElement unnamedConstructor = ConstantEvaluationEngine
._getConstructorBase(superclass.element.unnamedConstructor);
if (unnamedConstructor != null) {
referenceGraph.addEdge(element, unnamedConstructor);
}
}
}
for (FieldElement field in element.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) &&
!field.isStatic &&
field.initializer != null) {
referenceGraph.addEdge(element, field);
}
}
for (ParameterElement parameterElement in element.parameters) {
referenceGraph.addEdge(element, parameterElement);
}
} 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}");
}
});
List<List<AstNode>> topologicalSort =
}
List<List<Element>> topologicalSort =
referenceGraph.computeTopologicalSort();
for (List<AstNode> constantsInCycle in topologicalSort) {
for (List<Element> constantsInCycle in topologicalSort) {
if (constantsInCycle.length == 1) {
_computeValueFor(constantsInCycle[0]);
} else {
for (AstNode constant in constantsInCycle) {
for (Element constant in constantsInCycle) {
_generateCycleError(constantsInCycle, constant);
}
}
@ -1097,58 +1088,22 @@ class ConstantValueComputer {
// including them in the topological sort. We just process all the
// annotations after all other constants are finished.
for (Annotation annotation in _annotations) {
_computeValueFor(annotation);
_computeValueForAnnotation(annotation);
}
}
ConstructorDeclaration findConstructorDeclaration(
ConstructorElement constructor) => constructorDeclarationMap[
ConstantEvaluationEngine._getConstructorBase(constructor)];
VariableDeclaration findVariableDeclaration(
PotentiallyConstVariableElement variable) =>
_variableDeclarationMap[variable];
/**
* Compute a value for the given [constNode].
* Compute a value for the given [element].
*/
void _computeValueFor(AstNode constNode) {
evaluationEngine.validator.beforeComputeValue(constNode);
if (constNode is VariableDeclaration) {
VariableElement element = constNode.element;
RecordingErrorListener errorListener = new RecordingErrorListener();
ErrorReporter errorReporter =
new ErrorReporter(errorListener, element.source);
DartObjectImpl dartObject =
(element as PotentiallyConstVariableElement).constantInitializer
.accept(new ConstantVisitor(evaluationEngine, 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 && constNode.isConst) {
if (!evaluationEngine.runtimeTypeMatch(dartObject, element.type)) {
errorReporter.reportErrorForElement(
CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, element, [
dartObject.type,
element.type
]);
}
}
(element as VariableElementImpl).evaluationResult =
new EvaluationResultImpl.con2(dartObject, errorListener.errors);
} else if (constNode is ConstructorDeclaration) {
// 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.
ConstructorElementImpl constructor = constNode.element;
constructor.isCycleFree = true;
} else if (constNode is FormalParameter) {
if (constNode is DefaultFormalParameter) {
ParameterElement element = constNode.element;
void _computeValueFor(Element element) {
if (!_constantsToCompute.contains(element)) {
// Element is in the dependency graph but should have been computed by
// a previous stage of analysis.
return;
}
evaluationEngine.validator.beforeComputeValue(element);
if (element is ParameterElement) {
if (element.initializer != null) {
Expression defaultValue =
(element as PotentiallyConstVariableElement).constantInitializer;
if (defaultValue != null) {
@ -1161,59 +1116,93 @@ class ConstantValueComputer {
new EvaluationResultImpl.con2(dartObject, errorListener.errors);
}
}
} else if (constNode is 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(evaluationEngine, errorReporter);
DartObjectImpl result = evaluationEngine.evaluateConstructorCall(
constNode, constNode.arguments.arguments, element,
constantVisitor, errorReporter);
elementAnnotation.evaluationResult =
new EvaluationResultImpl.con2(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.con1(null);
} else if (element is VariableElement) {
RecordingErrorListener errorListener = new RecordingErrorListener();
ErrorReporter errorReporter =
new ErrorReporter(errorListener, element.source);
DartObjectImpl dartObject =
(element as PotentiallyConstVariableElement).constantInitializer
.accept(new ConstantVisitor(evaluationEngine, 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 (!evaluationEngine.runtimeTypeMatch(dartObject, element.type)) {
errorReporter.reportErrorForElement(
CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, element, [
dartObject.type,
element.type
]);
}
}
(element as VariableElementImpl).evaluationResult =
new EvaluationResultImpl.con2(dartObject, errorListener.errors);
} else if (element 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;
} else {
// Should not happen.
assert(false);
AnalysisEngine.instance.logger.logError(
"Constant value computer trying to compute the value of a node which is not a VariableDeclaration, InstanceCreationExpression, FormalParameter, or ConstructorDeclaration");
"Constant value computer trying to compute the value of a node of type ${element.runtimeType}");
return;
}
}
/**
* 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.con2(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.con1(null);
}
}
}
/**
* Generate an error indicating that the given [constNode] 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<AstNode> cycle, AstNode constNode) {
if (constNode is VariableDeclaration) {
VariableElement element = constNode.element;
void _generateCycleError(List<Element> cycle, Element element) {
if (element is VariableElement) {
RecordingErrorListener errorListener = new RecordingErrorListener();
ErrorReporter errorReporter =
new ErrorReporter(errorListener, element.source);
@ -1224,7 +1213,7 @@ class ConstantValueComputer {
CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, element, []);
(element as VariableElementImpl).evaluationResult =
new EvaluationResultImpl.con2(null, errorListener.errors);
} else if (constNode is ConstructorDeclaration) {
} else if (element is ConstructorElement) {
// We don't report cycle errors on constructor declarations since there
// is nowhere to put the error information.
} else {
@ -1232,7 +1221,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 ${constNode.runtimeType}");
"Constant value computer trying to report a cycle error for a node of type ${element.runtimeType}");
}
}
}
@ -1723,7 +1712,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
}
if (element is VariableElementImpl) {
VariableElementImpl variableElementImpl = element;
evaluationEngine.validator.beforeGetEvaluationResult(node);
evaluationEngine.validator.beforeGetEvaluationResult(element);
EvaluationResultImpl value = variableElementImpl.evaluationResult;
if (variableElementImpl.isConst && value != null) {
return value.value;
@ -4878,53 +4867,31 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
/**
* The element representing the construct that will be visited.
*/
final AstNode _source;
final Element _source;
/**
* A graph in which the nodes are the constant variables and the edges are
* from each variable to the other constant variables that are referenced in
* the head's initializer.
*/
final DirectedGraph<AstNode> _referenceGraph;
/**
* A table mapping constant variables to the declarations of those variables.
*/
final HashMap<PotentiallyConstVariableElement, VariableDeclaration> _variableDeclarationMap;
/**
* A table mapping constant constructors to the declarations of those
* constructors.
*/
final HashMap<ConstructorElement, ConstructorDeclaration> _constructorDeclarationMap;
final DirectedGraph<Element> _referenceGraph;
/**
* Initialize a newly created reference finder to find references from a given
* variable to other variables and to add those references to the given graph.
* The [source] is the element representing the variable whose initializer
* will be visited. The [referenceGraph] is a graph recording which variables
* (heads) reference which other variables (tails) in their initializers. The
* [variableDeclarationMap] is a table mapping constant variables to the
* declarations of those variables. The [constructorDeclarationMap] is a table
* mapping constant constructors to the declarations of those constructors.
* (heads) reference which other variables (tails) in their initializers.
*/
ReferenceFinder(this._source, this._referenceGraph,
this._variableDeclarationMap, this._constructorDeclarationMap);
ConstructorDeclaration findConstructorDeclaration(
ConstructorElement constructor) => _constructorDeclarationMap[
ConstantEvaluationEngine._getConstructorBase(constructor)];
ReferenceFinder(this._source, this._referenceGraph);
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
if (node.isConst) {
ConstructorElement constructor = node.staticElement;
ConstructorElement constructor =
ConstantEvaluationEngine._getConstructorBase(node.staticElement);
if (constructor != null) {
ConstructorDeclaration declaration =
findConstructorDeclaration(constructor);
if (declaration != null) {
_referenceGraph.addEdge(_source, declaration);
}
_referenceGraph.addEdge(_source, constructor);
}
}
return super.visitInstanceCreationExpression(node);
@ -4934,13 +4901,10 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
Object visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
super.visitRedirectingConstructorInvocation(node);
ConstructorElement target = node.staticElement;
ConstructorElement target =
ConstantEvaluationEngine._getConstructorBase(node.staticElement);
if (target != null && target.isConst) {
ConstructorDeclaration targetDeclaration =
_constructorDeclarationMap[target];
if (targetDeclaration != null) {
_referenceGraph.addEdge(_source, targetDeclaration);
}
_referenceGraph.addEdge(_source, target);
}
return null;
}
@ -4953,16 +4917,7 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
}
if (element is VariableElement) {
if (element.isConst) {
VariableDeclaration variableDeclaration =
_variableDeclarationMap[element];
// The declaration will be null when the variable is not defined in the
// compilation units that were used to produce the
// variableDeclarationMap. In such cases, the variable should already
// have a value associated with it, but we don't bother to check because
// there's nothing we can do about it at this point.
if (variableDeclaration != null) {
_referenceGraph.addEdge(_source, variableDeclaration);
}
_referenceGraph.addEdge(_source, element);
}
}
return null;
@ -4971,18 +4926,10 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
@override
Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
super.visitSuperConstructorInvocation(node);
ConstructorElement constructor = node.staticElement;
ConstructorElement constructor =
ConstantEvaluationEngine._getConstructorBase(node.staticElement);
if (constructor != null && constructor.isConst) {
ConstructorDeclaration constructorDeclaration =
_constructorDeclarationMap[constructor];
// The declaration will be null when the constructor is not defined in the
// compilation units that were used to produce the
// constructorDeclarationMap. In such cases, the constructor should
// already have its initializer AST's stored in it, but we don't bother
// to check because there's nothing we can do about it at this point.
if (constructorDeclaration != null) {
_referenceGraph.addEdge(_source, constructorDeclaration);
}
_referenceGraph.addEdge(_source, constructor);
}
return null;
}

View file

@ -8679,7 +8679,7 @@ class ParameterMember extends VariableMember implements ParameterElement {
*
* This class is not intended to be part of the public API for analyzer.
*/
abstract class PotentiallyConstVariableElement {
abstract class PotentiallyConstVariableElement implements VariableElementImpl {
/**
* If this element represents a constant variable, and it has an initializer,
* a copy of the initializer for the constant. Otherwise `null`.

View file

@ -7,8 +7,6 @@
library engine.all_the_rest_test;
import 'dart:collection';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
@ -475,72 +473,40 @@ class ConstantEvaluationValidator_ForTest
implements ConstantEvaluationValidator {
ConstantValueComputer computer;
AstNode _nodeBeingEvaluated;
Element _nodeBeingEvaluated;
@override
void beforeComputeValue(AstNode constNode) {
_nodeBeingEvaluated = constNode;
void beforeComputeValue(Element element) {
_nodeBeingEvaluated = element;
}
@override
void beforeGetConstantInitializers(ConstructorElement constructor) {
// If we are getting the constant initializers for a node in the graph,
// make sure we properly recorded the dependency.
ConstructorDeclaration node =
computer.findConstructorDeclaration(constructor);
if (node != null && computer.referenceGraph.nodes.contains(node)) {
expect(computer.referenceGraph.containsPath(_nodeBeingEvaluated, node),
isTrue);
}
// Make sure we properly recorded the dependency.
expect(
computer.referenceGraph.containsPath(_nodeBeingEvaluated, constructor),
isTrue);
}
@override
void beforeGetEvaluationResult(AstNode node) {
// If we are getting the evaluation result for a node in the graph,
// make sure we properly recorded the dependency.
if (computer.referenceGraph.nodes.contains(node)) {
expect(computer.referenceGraph.containsPath(_nodeBeingEvaluated, node),
isTrue);
}
void beforeGetEvaluationResult(Element element) {
// Make sure we properly recorded the dependency.
expect(computer.referenceGraph.containsPath(_nodeBeingEvaluated, element),
isTrue);
}
@override
void beforeGetFieldEvaluationResult(FieldElementImpl field) {
// If we are getting the constant value for a node in the graph, make sure
// we properly recorded the dependency.
VariableDeclaration node = computer.findVariableDeclaration(field);
if (node != null && computer.referenceGraph.nodes.contains(node)) {
expect(computer.referenceGraph.containsPath(_nodeBeingEvaluated, node),
isTrue);
}
// Make sure we properly recorded the dependency.
expect(computer.referenceGraph.containsPath(_nodeBeingEvaluated, field),
isTrue);
}
@override
void beforeGetParameterDefault(ParameterElement parameter) {
// Find the ConstructorElement and figure out which
// parameter we're talking about.
ConstructorElement constructor =
parameter.getAncestor((element) => element is ConstructorElement);
int parameterIndex;
List<ParameterElement> parameters = constructor.parameters;
int numParameters = parameters.length;
for (parameterIndex = 0; parameterIndex < numParameters; parameterIndex++) {
if (identical(parameters[parameterIndex], parameter)) {
break;
}
}
expect(parameterIndex < numParameters, isTrue);
// If we are getting the default parameter for a constructor in the graph,
// make sure we properly recorded the dependency on the parameter.
ConstructorDeclaration constructorNode =
computer.constructorDeclarationMap[constructor];
if (constructorNode != null) {
FormalParameter parameterNode =
constructorNode.parameters.parameters[parameterIndex];
expect(computer.referenceGraph.nodes.contains(parameterNode), isTrue);
expect(computer.referenceGraph.containsPath(
_nodeBeingEvaluated, parameterNode), isTrue);
}
// Make sure we properly recorded the dependency.
expect(computer.referenceGraph.containsPath(_nodeBeingEvaluated, parameter),
isTrue);
}
}
@ -957,73 +923,73 @@ class ConstantFinderTest extends EngineTestCase {
void test_visitConstructorDeclaration_const() {
ConstructorElement element = _setupConstructorDeclaration("A", true);
expect(_findConstantDeclarations()[element], same(_node));
expect(_findConstants(), contains(element));
}
void test_visitConstructorDeclaration_nonConst() {
_setupConstructorDeclaration("A", false);
expect(_findConstantDeclarations().isEmpty, isTrue);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_const() {
VariableElement element = _setupVariableDeclaration("v", true, true);
expect(_findVariableDeclarations()[element], same(_node));
expect(_findConstants(), contains(element));
}
void test_visitVariableDeclaration_final_inClass() {
_setupFieldDeclaration('C', 'f', Keyword.FINAL);
expect(_findVariableDeclarations(), isEmpty);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_final_inClassWithConstConstructor() {
VariableDeclaration field = _setupFieldDeclaration('C', 'f', Keyword.FINAL,
hasConstConstructor: true);
expect(_findVariableDeclarations()[field.element], same(field));
expect(_findConstants(), contains(field.element));
}
void test_visitVariableDeclaration_final_outsideClass() {
_setupVariableDeclaration('v', false, true, isFinal: true);
expect(_findVariableDeclarations(), isEmpty);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_noInitializer() {
_setupVariableDeclaration("v", true, false);
expect(_findVariableDeclarations().isEmpty, isTrue);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_nonConst() {
_setupVariableDeclaration("v", false, true);
expect(_findVariableDeclarations().isEmpty, isTrue);
expect(_findConstants(), isEmpty);
}
void test_visitVariableDeclaration_static_const_inClass() {
VariableDeclaration field =
_setupFieldDeclaration('C', 'f', Keyword.CONST, isStatic: true);
expect(_findVariableDeclarations()[field.element], same(field));
expect(_findConstants(), contains(field.element));
}
void test_visitVariableDeclaration_static_const_inClassWithConstConstructor() {
VariableDeclaration field = _setupFieldDeclaration('C', 'f', Keyword.CONST,
isStatic: true, hasConstConstructor: true);
expect(_findVariableDeclarations()[field.element], same(field));
expect(_findConstants(), contains(field.element));
}
void test_visitVariableDeclaration_static_final_inClassWithConstConstructor() {
_setupFieldDeclaration('C', 'f', Keyword.FINAL,
VariableDeclaration field = _setupFieldDeclaration('C', 'f', Keyword.FINAL,
isStatic: true, hasConstConstructor: true);
expect(_findVariableDeclarations(), isEmpty);
expect(_findConstants(), isNot(contains(field.element)));
}
void test_visitVariableDeclaration_uninitialized_final_inClassWithConstConstructor() {
_setupFieldDeclaration('C', 'f', Keyword.FINAL,
VariableDeclaration field = _setupFieldDeclaration('C', 'f', Keyword.FINAL,
isInitialized: false, hasConstConstructor: true);
expect(_findVariableDeclarations(), isEmpty);
expect(_findConstants(), isNot(contains(field.element)));
}
void test_visitVariableDeclaration_uninitialized_static_const_inClass() {
_setupFieldDeclaration('C', 'f', Keyword.CONST,
isStatic: true, isInitialized: false);
expect(_findVariableDeclarations(), isEmpty);
expect(_findConstants(), isEmpty);
}
List<Annotation> _findAnnotations() {
@ -1034,22 +1000,12 @@ class ConstantFinderTest extends EngineTestCase {
return annotations;
}
Map<ConstructorElement, ConstructorDeclaration> _findConstantDeclarations() {
Set<Element> _findConstants() {
ConstantFinder finder = new ConstantFinder();
_node.accept(finder);
Map<ConstructorElement, ConstructorDeclaration> constructorMap =
finder.constructorMap;
expect(constructorMap, isNotNull);
return constructorMap;
}
Map<PotentiallyConstVariableElement, VariableDeclaration> _findVariableDeclarations() {
ConstantFinder finder = new ConstantFinder();
_node.accept(finder);
Map<PotentiallyConstVariableElement, VariableDeclaration> variableMap =
finder.variableMap;
expect(variableMap, isNotNull);
return variableMap;
Set<Element> constants = finder.constantsToCompute;
expect(constants, isNotNull);
return constants;
}
ConstructorElement _setupConstructorDeclaration(String name, bool isConst) {
@ -1382,6 +1338,23 @@ const int d = c;''');
_validate(true, (members[0] as TopLevelVariableDeclaration).variables);
}
void test_computeValues_value_depends_on_enum() {
Source librarySource = addSource('''
enum E { id0, id1 }
const E e = E.id0;
''');
LibraryElement libraryElement = resolve(librarySource);
CompilationUnit unit =
analysisContext.resolveCompilationUnit(librarySource, libraryElement);
expect(unit, isNotNull);
ConstantValueComputer computer = _makeConstantValueComputer();
computer.add(unit);
computer.computeValues();
TopLevelVariableDeclaration declaration = unit.declarations
.firstWhere((member) => member is TopLevelVariableDeclaration);
_validate(true, declaration.variables);
}
void test_dependencyOnConstructor() {
// x depends on "const A()"
_assertProperDependencies(r'''
@ -8039,71 +8012,54 @@ class MockDartSdk implements DartSdk {
@reflectiveTest
class ReferenceFinderTest extends EngineTestCase {
DirectedGraph<AstNode> _referenceGraph;
Map<PotentiallyConstVariableElement, VariableDeclaration> _variableDeclarationMap;
Map<ConstructorElement, ConstructorDeclaration> _constructorDeclarationMap;
VariableDeclaration _head;
AstNode _tail;
DirectedGraph<Element> _referenceGraph;
VariableElement _head;
Element _tail;
@override
void setUp() {
_referenceGraph = new DirectedGraph<AstNode>();
_variableDeclarationMap =
new HashMap<PotentiallyConstVariableElement, VariableDeclaration>();
_constructorDeclarationMap =
new HashMap<ConstructorElement, ConstructorDeclaration>();
_head = AstFactory.variableDeclaration("v1");
_referenceGraph = new DirectedGraph<Element>();
_head = ElementFactory.topLevelVariableElement2("v1");
}
void test_visitSimpleIdentifier_const() {
_visitNode(_makeTailVariable("v2", true, true));
_visitNode(_makeTailVariable("v2", true));
_assertOneArc(_tail);
}
void test_visitSimpleIdentifier_nonConst() {
_visitNode(_makeTailVariable("v2", false, true));
_assertNoArcs();
}
void test_visitSimpleIdentifier_notInMap() {
_visitNode(_makeTailVariable("v2", true, false));
_visitNode(_makeTailVariable("v2", false));
_assertNoArcs();
}
void test_visitSuperConstructorInvocation_const() {
_visitNode(_makeTailSuperConstructorInvocation("A", true, true));
_visitNode(_makeTailSuperConstructorInvocation("A", true));
_assertOneArc(_tail);
}
void test_visitSuperConstructorInvocation_nonConst() {
_visitNode(_makeTailSuperConstructorInvocation("A", false, true));
_assertNoArcs();
}
void test_visitSuperConstructorInvocation_notInMap() {
_visitNode(_makeTailSuperConstructorInvocation("A", true, false));
_visitNode(_makeTailSuperConstructorInvocation("A", false));
_assertNoArcs();
}
void test_visitSuperConstructorInvocation_unresolved() {
SuperConstructorInvocation superConstructorInvocation =
AstFactory.superConstructorInvocation();
_tail = superConstructorInvocation;
_visitNode(superConstructorInvocation);
_assertNoArcs();
}
void _assertNoArcs() {
Set<AstNode> tails = _referenceGraph.getTails(_head);
Set<Element> tails = _referenceGraph.getTails(_head);
expect(tails, hasLength(0));
}
void _assertOneArc(AstNode tail) {
Set<AstNode> tails = _referenceGraph.getTails(_head);
void _assertOneArc(Element tail) {
Set<Element> tails = _referenceGraph.getTails(_head);
expect(tails, hasLength(1));
expect(tails.first, same(tail));
}
ReferenceFinder _createReferenceFinder(AstNode source) => new ReferenceFinder(
source, _referenceGraph, _variableDeclarationMap,
_constructorDeclarationMap);
ReferenceFinder _createReferenceFinder(Element source) =>
new ReferenceFinder(source, _referenceGraph);
SuperConstructorInvocation _makeTailSuperConstructorInvocation(
String name, bool isConst, bool inMap) {
String name, bool isConst) {
List<ConstructorInitializer> initializers =
new List<ConstructorInitializer>();
ConstructorDeclaration constructorDeclaration = AstFactory
.constructorDeclaration(AstFactory.identifier3(name), null,
AstFactory.formalParameterList(), initializers);
_tail = constructorDeclaration;
if (isConst) {
constructorDeclaration.constKeyword = new KeywordToken(Keyword.CONST, 0);
}
@ -8112,24 +8068,19 @@ class ReferenceFinderTest extends EngineTestCase {
AstFactory.superConstructorInvocation();
ConstructorElementImpl constructorElement =
ElementFactory.constructorElement(classElement, name, isConst);
if (inMap) {
_constructorDeclarationMap[constructorElement] = constructorDeclaration;
}
_tail = constructorElement;
superConstructorInvocation.staticElement = constructorElement;
return superConstructorInvocation;
}
SimpleIdentifier _makeTailVariable(String name, bool isConst, bool inMap) {
SimpleIdentifier _makeTailVariable(String name, bool isConst) {
VariableDeclaration variableDeclaration =
AstFactory.variableDeclaration(name);
_tail = variableDeclaration;
ConstLocalVariableElementImpl variableElement =
ElementFactory.constLocalVariableElement(name);
_tail = variableElement;
variableElement.const3 = isConst;
AstFactory.variableDeclarationList2(
isConst ? Keyword.CONST : Keyword.VAR, [variableDeclaration]);
if (inMap) {
_variableDeclarationMap[variableElement] = variableDeclaration;
}
SimpleIdentifier identifier = AstFactory.identifier3(name);
identifier.staticElement = variableElement;
return identifier;