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

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

Issue 285423002: New analyzer snapshot (with CaughtException). (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Replace AnalysisException with CaughtException Created 6 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 | « pkg/analyzer/lib/src/generated/ast.dart ('k') | 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 79e29da912065a90c9fab38cb99dd61809c85d87..ef4fd68d0605e31dd219aa041666f58f3e5c6c14 100644
--- a/pkg/analyzer/lib/src/generated/constant.dart
+++ b/pkg/analyzer/lib/src/generated/constant.dart
@@ -112,6 +112,9 @@ class BoolState extends InstanceState {
bool get isBoolNumStringOrNull => true;
@override
+ bool get isUnknown => value == null;
+
+ @override
BoolState logicalAnd(InstanceState rightOperand) {
assertBool(rightOperand);
if (value == null) {
@@ -204,7 +207,7 @@ class ConstantEvaluator {
ConstantEvaluator(this._source, this._typeProvider);
EvaluationResult evaluate(Expression expression) {
- EvaluationResultImpl result = expression.accept(new ConstantVisitor(_typeProvider));
+ EvaluationResultImpl result = expression.accept(new ConstantVisitor.con1(_typeProvider));
if (result is ValidResult) {
return EvaluationResult.forValue(result.value);
}
@@ -228,6 +231,37 @@ class ConstantFinder extends RecursiveAstVisitor<Object> {
*/
final Map<VariableElement, VariableDeclaration> variableMap = new Map<VariableElement, VariableDeclaration>();
+ /**
+ * A table mapping constant constructors to the declarations of those constructors.
+ */
+ final Map<ConstructorElement, ConstructorDeclaration> constructorMap = new Map<ConstructorElement, ConstructorDeclaration>();
+
+ /**
+ * A collection of constant constructor invocations.
+ */
+ final List<InstanceCreationExpression> constructorInvocations = new List<InstanceCreationExpression>();
+
+ @override
+ Object visitConstructorDeclaration(ConstructorDeclaration node) {
+ super.visitConstructorDeclaration(node);
+ if (node.constKeyword != null) {
+ ConstructorElement element = node.element;
+ if (element != null) {
+ constructorMap[element] = node;
+ }
+ }
+ return null;
+ }
+
+ @override
+ Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+ super.visitInstanceCreationExpression(node);
+ if (node.isConst) {
+ constructorInvocations.add(node);
+ }
+ return null;
+ }
+
@override
Object visitVariableDeclaration(VariableDeclaration node) {
super.visitVariableDeclaration(node);
@@ -243,124 +277,430 @@ class ConstantFinder extends RecursiveAstVisitor<Object> {
}
/**
- * Instances of the class `ConstantValueComputer` compute the values of constant variables in
- * one or more compilation units. The expected usage pattern is for the compilation units to be
- * added to this computer using the method [add] and then for the method
- * [computeValues] to be invoked exactly once. Any use of an instance after invoking the
- * method [computeValues] will result in unpredictable behavior.
+ * Instances of the class `ConstantValueComputer` compute the values of constant variables and
+ * constant constructor invocations in one or more compilation units. The expected usage pattern is
+ * for the compilation units to be added to this computer using the method
+ * [add] and then for the method [computeValues] to be invoked
+ * exactly once. Any use of an instance after invoking the method [computeValues] will
+ * result in unpredictable behavior.
*/
class ConstantValueComputer {
/**
* The type provider used to access the known types.
*/
- final TypeProvider _typeProvider;
+ TypeProvider typeProvider;
/**
- * The object used to find constant variables in the compilation units that were added.
+ * 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 constant variables and the edges are from each variable to
- * the other constant variables that are referenced in the head's initializer.
+ * 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<VariableElement> _referenceGraph = new DirectedGraph<VariableElement>();
+ DirectedGraph<AstNode> referenceGraph = new DirectedGraph<AstNode>();
/**
* A table mapping constant variables to the declarations of those variables.
*/
- Map<VariableElement, VariableDeclaration> _declarationMap;
+ Map<VariableElement, VariableDeclaration> _variableDeclarationMap;
+
+ /**
+ * A table mapping constant constructors to the declarations of those constructors.
+ */
+ Map<ConstructorElement, ConstructorDeclaration> constructorDeclarationMap;
+
+ /**
+ * A collection of constant constructor invocations.
+ */
+ List<InstanceCreationExpression> _constructorInvocations;
/**
* Initialize a newly created constant value computer.
*
* @param typeProvider the type provider used to access known types
*/
- ConstantValueComputer(this._typeProvider);
+ ConstantValueComputer(TypeProvider typeProvider) {
+ this.typeProvider = typeProvider;
+ }
/**
- * Add the constant variables in the given compilation unit to the list of constant variables
- * whose value needs to be computed.
+ * Add the constants in the given compilation unit to the list of constants whose value needs to
+ * be computed.
*
- * @param unit the compilation unit defining the constant variables to be added
+ * @param unit the compilation unit defining the constants to be added
*/
void add(CompilationUnit unit) {
unit.accept(_constantFinder);
}
/**
- * Compute values for all of the constant variables in the compilation units that were added.
+ * Compute values for all of the constants in the compilation units that were added.
*/
void computeValues() {
- _declarationMap = _constantFinder.variableMap;
- for (MapEntry<VariableElement, VariableDeclaration> entry in getMapEntrySet(_declarationMap)) {
- VariableElement element = entry.getKey();
- ReferenceFinder referenceFinder = new ReferenceFinder(element, _referenceGraph);
- _referenceGraph.addNode(element);
- entry.getValue().initializer.accept(referenceFinder);
- }
- while (!_referenceGraph.isEmpty) {
- VariableElement element = _referenceGraph.removeSink();
- while (element != null) {
- _computeValueFor(element);
- element = _referenceGraph.removeSink();
+ _variableDeclarationMap = _constantFinder.variableMap;
+ constructorDeclarationMap = _constantFinder.constructorMap;
+ _constructorInvocations = _constantFinder.constructorInvocations;
+ for (MapEntry<VariableElement, VariableDeclaration> entry in getMapEntrySet(_variableDeclarationMap)) {
+ VariableDeclaration declaration = entry.getValue();
+ ReferenceFinder referenceFinder = new ReferenceFinder(declaration, referenceGraph, _variableDeclarationMap, constructorDeclarationMap);
+ referenceGraph.addNode(declaration);
+ declaration.initializer.accept(referenceFinder);
+ }
+ for (MapEntry<ConstructorElement, ConstructorDeclaration> entry in getMapEntrySet(constructorDeclarationMap)) {
+ ConstructorDeclaration declaration = entry.getValue();
+ 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 (!_referenceGraph.isEmpty) {
- List<VariableElement> variablesInCycle = _referenceGraph.findCycle();
- if (variablesInCycle == null) {
- //
- // This should not happen. Either the graph should be empty, or there should be at least
- // one sink, or there should be a cycle. If this does happen we exit to prevent an
- // infinite loop.
- //
- AnalysisEngine.instance.logger.logError("Exiting constant value computer with ${_referenceGraph.nodeCount} variables that are neither sinks nor in a cycle");
- return;
+ if (!superInvocationFound) {
+ // No explicit superconstructor invocation found, so we need to manually insert
+ // a reference to the implicit superconstructor.
+ InterfaceType superclass = (entry.getKey().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 (VariableElement variable in variablesInCycle) {
- _generateCycleError(variablesInCycle, variable);
+ }
+ for (FormalParameter parameter in declaration.parameters.parameters) {
+ referenceGraph.addNode(parameter);
+ referenceGraph.addEdge(declaration, parameter);
+ if (parameter is DefaultFormalParameter) {
+ Expression defaultValue = parameter.defaultValue;
+ if (defaultValue != null) {
+ ReferenceFinder parameterReferenceFinder = new ReferenceFinder(parameter, referenceGraph, _variableDeclarationMap, constructorDeclarationMap);
+ defaultValue.accept(parameterReferenceFinder);
+ }
+ }
+ }
+ }
+ for (InstanceCreationExpression expression in _constructorInvocations) {
+ referenceGraph.addNode(expression);
+ ConstructorElement constructor = expression.staticElement;
+ if (constructor == null) {
+ break;
+ }
+ constructor = _followConstantRedirectionChain(constructor);
+ ConstructorDeclaration declaration = findConstructorDeclaration(constructor);
+ // An instance creation expression depends both on the constructor and the arguments passed
+ // to it.
+ ReferenceFinder referenceFinder = new ReferenceFinder(expression, referenceGraph, _variableDeclarationMap, constructorDeclarationMap);
+ if (declaration != null) {
+ referenceGraph.addEdge(expression, declaration);
+ }
+ expression.argumentList.accept(referenceFinder);
+ }
+ List<List<AstNode>> topologicalSort = referenceGraph.computeTopologicalSort();
+ for (List<AstNode> constantsInCycle in topologicalSort) {
+ if (constantsInCycle.length == 1) {
+ _computeValueFor(constantsInCycle[0]);
+ } else {
+ for (AstNode constant in constantsInCycle) {
+ _generateCycleError(constantsInCycle, constant);
}
- _referenceGraph.removeAllNodes(variablesInCycle);
}
}
}
/**
- * Compute a value for the given variable.
+ * This method is called just before computing the constant value associated with an AST node.
+ * Unit tests will override this method to introduce additional error checking.
+ */
+ void beforeComputeValue(AstNode constNode) {
+ }
+
+ /**
+ * This method is called just before getting the constant initializers associated with a
+ * constructor AST node. Unit tests will override this method to introduce additional error
+ * checking.
+ */
+ void beforeGetConstantInitializers(ConstructorElement constructor) {
+ }
+
+ /**
+ * This method is called just before getting a parameter's default value. Unit tests will override
+ * this method to introduce additional error checking.
+ */
+ void beforeGetParameterDefault(ParameterElement parameter) {
+ }
+
+ /**
+ * Create the ConstantVisitor used to evaluate constants. Unit tests will override this method to
+ * introduce additional error checking.
+ */
+ ConstantVisitor createConstantVisitor() => new ConstantVisitor.con1(typeProvider);
+
+ ConstructorDeclaration findConstructorDeclaration(ConstructorElement constructor) => constructorDeclarationMap[_getConstructorBase(constructor)];
+
+ /**
+ * Compute a value for the given constant.
*
- * @param variable the variable for which a value is to be computed
- */
- void _computeValueFor(VariableElement variable) {
- VariableDeclaration declaration = _declarationMap[variable];
- if (declaration == null) {
- //
- // The declaration will be null when the variable was added to the graph as a result of being
- // referenced by another variable but is not defined in the compilation units that were added
- // to this computer. 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.
- //
+ * @param constNode the constant for which a value is to be computed
+ */
+ void _computeValueFor(AstNode constNode) {
+ beforeComputeValue(constNode);
+ if (constNode is VariableDeclaration) {
+ VariableDeclaration declaration = constNode;
+ Element element = declaration.element;
+ EvaluationResultImpl result = declaration.initializer.accept(createConstantVisitor());
+ (element as VariableElementImpl).evaluationResult = result;
+ } else if (constNode is InstanceCreationExpression) {
+ InstanceCreationExpression expression = constNode;
+ ConstructorElement constructor = expression.staticElement;
+ if (constructor == null) {
+ // Couldn't resolve the constructor so we can't compute a value. No problem--the error
+ // has already been reported.
+ return;
+ }
+ ConstantVisitor constantVisitor = createConstantVisitor();
+ ValidResult result = _evaluateConstructorCall(expression.argumentList.arguments, constructor, constantVisitor);
+ expression.evaluationResult = result;
+ } else if (constNode is ConstructorDeclaration) {
+ ConstructorDeclaration declaration = constNode;
+ NodeList<ConstructorInitializer> initializers = declaration.initializers;
+ ConstructorElementImpl constructor = declaration.element as ConstructorElementImpl;
+ constructor.constantInitializers = new ConstantValueComputer_InitializerCloner().cloneNodeList(initializers);
+ } else if (constNode is FormalParameter) {
+ if (constNode is DefaultFormalParameter) {
+ DefaultFormalParameter parameter = constNode;
+ ParameterElement element = parameter.element;
+ Expression defaultValue = parameter.defaultValue;
+ if (defaultValue != null) {
+ EvaluationResultImpl result = defaultValue.accept(createConstantVisitor());
+ (element as ParameterElementImpl).evaluationResult = result;
+ }
+ }
+ } else {
+ // Should not happen.
+ AnalysisEngine.instance.logger.logError("Constant value computer trying to compute the value of a node which is not a VariableDeclaration, InstanceCreationExpression, FormalParameter, or ConstructorDeclaration");
return;
}
- EvaluationResultImpl result = declaration.initializer.accept(new ConstantVisitor(_typeProvider));
- (variable as VariableElementImpl).evaluationResult = result;
- if (result is ErrorResult) {
- List<AnalysisError> errors = new List<AnalysisError>();
- for (ErrorResult_ErrorData data in result.errorData) {
- AstNode node = data.node;
- Source source = variable.getAncestor((element) => element is CompilationUnitElement).source;
- errors.add(new AnalysisError.con2(source, node.offset, node.length, data.errorCode, []));
+ }
+
+ ValidResult _evaluateConstructorCall(NodeList<Expression> arguments, ConstructorElement constructor, ConstantVisitor constantVisitor) {
+ int argumentCount = arguments.length;
+ List<DartObjectImpl> argumentValues = new List<DartObjectImpl>(argumentCount);
+ Map<String, DartObjectImpl> namedArgumentValues = new Map<String, DartObjectImpl>();
+ for (int i = 0; i < argumentCount; i++) {
+ Expression argument = arguments[i];
+ if (argument is NamedExpression) {
+ NamedExpression namedExpression = argument;
+ String name = namedExpression.name.label.name;
+ namedArgumentValues[name] = constantVisitor._valueOf(namedExpression.expression);
+ argumentValues[i] = constantVisitor.null2;
+ } else {
+ argumentValues[i] = constantVisitor._valueOf(argument);
}
}
+ constructor = _followConstantRedirectionChain(constructor);
+ InterfaceType definingClass = constructor.returnType as InterfaceType;
+ if (constructor.isFactory) {
+ // We couldn't find a non-factory constructor. See if it's because we reached an external
+ // const factory constructor that we can emulate.
+ // TODO(paulberry): if the constructor is one of {bool,int,String}.fromEnvironment(),
+ // we may be able to infer the value based on -D flags provided to the analyzer (see
+ // dartbug.com/17234).
+ if (identical(definingClass, typeProvider.symbolType) && argumentCount == 1) {
+ String argumentValue = argumentValues[0].stringValue;
+ if (argumentValue != null) {
+ return constantVisitor._valid(definingClass, new SymbolState(argumentValue));
+ }
+ }
+ // Either it's an external const factory constructor that we can't emulate, or an error
+ // occurred (a cycle, or a const constructor trying to delegate to a non-const constructor).
+ // In the former case, the best we can do is consider it an unknown value. In the latter
+ // case, the error has already been reported, so considering it an unknown value will
+ // suppress further errors.
+ return constantVisitor._validWithUnknownValue(definingClass);
+ }
+ beforeGetConstantInitializers(constructor);
+ ConstructorElementImpl constructorBase = _getConstructorBase(constructor) as ConstructorElementImpl;
+ List<ConstructorInitializer> initializers = constructorBase.constantInitializers;
+ if (initializers == null) {
+ // This can happen in some cases where there are compile errors in the code being analyzed
+ // (for example if the code is trying to create a const instance using a non-const
+ // constructor, or the node we're visiting is involved in a cycle). The error has already
+ // been reported, so consider it an unknown value to suppress further errors.
+ return constantVisitor._validWithUnknownValue(definingClass);
+ }
+ Map<String, DartObjectImpl> fieldMap = new Map<String, DartObjectImpl>();
+ Map<String, DartObjectImpl> parameterMap = new Map<String, DartObjectImpl>();
+ List<ParameterElement> parameters = constructorBase.parameters;
+ int parameterCount = parameters.length;
+ for (int i = 0; i < parameterCount; i++) {
+ ParameterElement parameter = parameters[i];
+ while (parameter is ParameterMember) {
+ parameter = (parameter as ParameterMember).baseElement;
+ }
+ DartObjectImpl argumentValue = null;
+ if (parameter.parameterKind == ParameterKind.NAMED) {
+ argumentValue = namedArgumentValues[parameter.name];
+ } else if (i < argumentCount) {
+ argumentValue = argumentValues[i];
+ }
+ if (argumentValue == null && parameter is ParameterElementImpl) {
+ // The parameter is an optional positional parameter for which no value was provided, so
+ // use the default value.
+ beforeGetParameterDefault(parameter);
+ EvaluationResultImpl evaluationResult = (parameter as ParameterElementImpl).evaluationResult;
+ if (evaluationResult is ValidResult) {
+ argumentValue = evaluationResult.value;
+ } else if (evaluationResult == null) {
+ // No default was provided, so the default value is null.
+ argumentValue = constantVisitor.null2;
+ }
+ }
+ if (argumentValue != null) {
+ if (parameter.isInitializingFormal) {
+ FieldElement field = (parameter as FieldFormalParameterElement).field;
+ if (field != null) {
+ String fieldName = field.name;
+ fieldMap[fieldName] = argumentValue;
+ }
+ } else {
+ String name = parameter.name;
+ parameterMap[name] = argumentValue;
+ }
+ }
+ }
+ ConstantVisitor initializerVisitor = new ConstantVisitor.con2(typeProvider, parameterMap);
+ String superName = null;
+ NodeList<Expression> superArguments = null;
+ for (ConstructorInitializer initializer in initializers) {
+ if (initializer is ConstructorFieldInitializer) {
+ ConstructorFieldInitializer constructorFieldInitializer = initializer;
+ Expression initializerExpression = constructorFieldInitializer.expression;
+ EvaluationResultImpl evaluationResult = initializerExpression.accept(initializerVisitor);
+ if (evaluationResult is ValidResult) {
+ DartObjectImpl value = evaluationResult.value;
+ String fieldName = constructorFieldInitializer.fieldName.name;
+ fieldMap[fieldName] = value;
+ }
+ } else if (initializer is SuperConstructorInvocation) {
+ SuperConstructorInvocation superConstructorInvocation = initializer;
+ SimpleIdentifier name = superConstructorInvocation.constructorName;
+ if (name != null) {
+ superName = name.name;
+ }
+ superArguments = superConstructorInvocation.argumentList.arguments;
+ }
+ }
+ // Evaluate explicit or implicit call to super().
+ InterfaceType superclass = definingClass.superclass;
+ if (superclass != null && !superclass.isObject) {
+ ConstructorElement superConstructor = superclass.lookUpConstructor(superName, constructor.library);
+ if (superConstructor != null) {
+ if (superArguments == null) {
+ superArguments = new NodeList<Expression>(null);
+ }
+ _evaluateSuperConstructorCall(fieldMap, superConstructor, superArguments, initializerVisitor);
+ }
+ }
+ return constantVisitor._valid(definingClass, new GenericState(fieldMap));
+ }
+
+ void _evaluateSuperConstructorCall(Map<String, DartObjectImpl> fieldMap, ConstructorElement superConstructor, NodeList<Expression> superArguments, ConstantVisitor initializerVisitor) {
+ if (superConstructor != null && superConstructor.isConst) {
+ ValidResult evaluationResult = _evaluateConstructorCall(superArguments, superConstructor, initializerVisitor);
+ fieldMap[GenericState.SUPERCLASS_FIELD] = evaluationResult.value;
+ }
+ }
+
+ /**
+ * Attempt to follow the chain of factory redirections until a constructor is reached which is not
+ * a const factory constructor.
+ *
+ * @return the constant constructor which terminates the chain of factory redirections, if the
+ * chain terminates. If there is a problem (e.g. a redirection can't be found, or a cycle
+ * is encountered), the chain will be followed as far as possible and then a const factory
+ * constructor will be returned.
+ */
+ ConstructorElement _followConstantRedirectionChain(ConstructorElement constructor) {
+ Set<ConstructorElement> constructorsVisited = new Set<ConstructorElement>();
+ while (constructor.isFactory) {
+ if (identical(constructor.enclosingElement.type, typeProvider.symbolType)) {
+ // The dart:core.Symbol has a const factory constructor that redirects to
+ // dart:_internal.Symbol. That in turn redirects to an external const constructor, which
+ // we won't be able to evaluate. So stop following the chain of redirections at
+ // dart:core.Symbol, and let [evaluateInstanceCreationExpression] handle it specially.
+ break;
+ }
+ constructorsVisited.add(constructor);
+ ConstructorElement redirectedConstructor = constructor.redirectedConstructor;
+ if (redirectedConstructor == null) {
+ // This can happen if constructor is an external factory constructor.
+ break;
+ }
+ if (!redirectedConstructor.isConst) {
+ // Delegating to a non-const constructor--this is not allowed (and
+ // is checked elsewhere--see [ErrorVerifier.checkForRedirectToNonConstConstructor()]).
+ break;
+ }
+ if (constructorsVisited.contains(redirectedConstructor)) {
+ // Cycle in redirecting factory constructors--this is not allowed
+ // and is checked elsewhere--see [ErrorVerifier.checkForRecursiveFactoryRedirect()]).
+ break;
+ }
+ constructor = redirectedConstructor;
+ }
+ return constructor;
}
/**
- * Generate an error indicating that the given variable is not a valid compile-time constant
- * because it references at least one of the variables in the given cycle, each of which directly
- * or indirectly references the variable.
+ * 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.
*
- * @param variablesInCycle the variables in the cycle that includes the given variable
- * @param variable the variable that is not a valid compile-time constant
+ * @param constantsInCycle the constants in the cycle that includes the given constant
+ * @param constant the constant that is not a valid compile-time constant
*/
- void _generateCycleError(List<VariableElement> variablesInCycle, VariableElement variable) {
+ void _generateCycleError(List<AstNode> constantsInCycle, AstNode constant) {
+ }
+
+ ConstructorElement _getConstructorBase(ConstructorElement constructor) {
+ while (constructor is ConstructorMember) {
+ constructor = (constructor as ConstructorMember).baseElement;
+ }
+ return constructor;
+ }
+}
+
+/**
+ * [AstCloner] that copies the necessary information from the AST to allow const constructor
+ * initializers to be evaluated.
+ */
+class ConstantValueComputer_InitializerCloner extends AstCloner {
+ @override
+ InstanceCreationExpression visitInstanceCreationExpression(InstanceCreationExpression node) {
+ // All we need is the evaluation result, and the keyword so that we know whether it's const.
+ InstanceCreationExpression expression = new InstanceCreationExpression(node.keyword, null, null);
+ expression.evaluationResult = node.evaluationResult;
+ return expression;
+ }
+
+ @override
+ SimpleIdentifier visitSimpleIdentifier(SimpleIdentifier node) {
+ SimpleIdentifier identifier = super.visitSimpleIdentifier(node);
+ identifier.staticElement = node.staticElement;
+ return identifier;
+ }
+
+ @override
+ SuperConstructorInvocation visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+ SuperConstructorInvocation invocation = super.visitSuperConstructorInvocation(node);
+ invocation.staticElement = node.staticElement;
+ return invocation;
}
}
@@ -418,12 +758,29 @@ class ConstantVisitor extends UnifyingAstVisitor<EvaluationResultImpl> {
*/
DartObjectImpl _nullObject;
+ Map<String, DartObjectImpl> _lexicalEnvironment;
+
/**
* Initialize a newly created constant visitor.
*
* @param typeProvider the type provider used to access known types
+ * @param lexicalEnvironment values which should override simpleIdentifiers, or null if no
+ * overriding is necessary.
*/
- ConstantVisitor(this._typeProvider);
+ ConstantVisitor.con1(this._typeProvider) {
+ this._lexicalEnvironment = null;
+ }
+
+ /**
+ * Initialize a newly created constant visitor.
+ *
+ * @param typeProvider the type provider used to access known types
+ * @param lexicalEnvironment values which should override simpleIdentifiers, or null if no
+ * overriding is necessary.
+ */
+ ConstantVisitor.con2(this._typeProvider, Map<String, DartObjectImpl> lexicalEnvironment) {
+ this._lexicalEnvironment = lexicalEnvironment;
+ }
@override
EvaluationResultImpl visitAdjacentStrings(AdjacentStrings node) {
@@ -539,84 +896,10 @@ class ConstantVisitor extends UnifyingAstVisitor<EvaluationResultImpl> {
// TODO(brianwilkerson) Figure out which error to report.
return _error(node, null);
}
- ConstructorElement constructor = node.staticElement;
- if (constructor != null && constructor.isConst) {
- NodeList<Expression> arguments = node.argumentList.arguments;
- int argumentCount = arguments.length;
- List<DartObjectImpl> argumentValues = new List<DartObjectImpl>(argumentCount);
- Map<String, DartObjectImpl> namedArgumentValues = new Map<String, DartObjectImpl>();
- for (int i = 0; i < argumentCount; i++) {
- Expression argument = arguments[i];
- if (argument is NamedExpression) {
- NamedExpression namedExpression = argument;
- String name = namedExpression.name.label.name;
- namedArgumentValues[name] = _valueOf(namedExpression.expression);
- argumentValues[i] = null2;
- } else {
- argumentValues[i] = _valueOf(argument);
- }
- }
- Set<ConstructorElement> constructorsVisited = new Set<ConstructorElement>();
- InterfaceType definingClass = constructor.returnType as InterfaceType;
- while (constructor.isFactory) {
- if (definingClass.element.library.isDartCore) {
- String className = definingClass.name;
- if (className == "Symbol" && argumentCount == 1) {
- String argumentValue = argumentValues[0].stringValue;
- if (argumentValue != null) {
- return _valid(definingClass, new SymbolState(argumentValue));
- }
- }
- }
- constructorsVisited.add(constructor);
- ConstructorElement redirectedConstructor = constructor.redirectedConstructor;
- if (redirectedConstructor == null) {
- // This can happen if constructor is an external factory constructor. Since there is no
- // constructor to delegate to, we currently can't evaluate the constant.
- // TODO(paulberry): if the constructor is one of {bool,int,String}.fromEnvironment(),
- // we may be able to infer the value based on -D flags provided to the analyzer (see
- // dartbug.com/17234).
- return _error(node, null);
- }
- if (!redirectedConstructor.isConst) {
- // Delegating to a non-const constructor--this is not allowed (and
- // is checked elsewhere--see [ErrorVerifier.checkForRedirectToNonConstConstructor()]).
- // So if we encounter it just error out.
- return _error(node, null);
- }
- if (constructorsVisited.contains(redirectedConstructor)) {
- // Cycle in redirecting factory constructors--this is not allowed
- // and is checked elsewhere--see [ErrorVerifier.checkForRecursiveFactoryRedirect()]).
- // So if we encounter it just error out.
- return _error(node, null);
- }
- constructor = redirectedConstructor;
- definingClass = constructor.returnType as InterfaceType;
- }
- Map<String, DartObjectImpl> fieldMap = new Map<String, DartObjectImpl>();
- List<ParameterElement> parameters = constructor.parameters;
- int parameterCount = parameters.length;
- for (int i = 0; i < parameterCount; i++) {
- ParameterElement parameter = parameters[i];
- if (parameter.isInitializingFormal) {
- FieldElement field = (parameter as FieldFormalParameterElement).field;
- if (field != null) {
- String fieldName = field.name;
- if (parameter.parameterKind == ParameterKind.NAMED) {
- DartObjectImpl argumentValue = namedArgumentValues[parameter.name];
- if (argumentValue != null) {
- fieldMap[fieldName] = argumentValue;
- }
- } else if (i < argumentCount) {
- fieldMap[fieldName] = argumentValues[i];
- }
- }
- }
- }
- // TODO(brianwilkerson) This doesn't handle fields initialized in an initializer. We should be
- // able to handle fields initialized by the superclass' constructor fairly easily, but other
- // initializers will be harder.
- return _valid(definingClass, new GenericState(fieldMap));
+ beforeGetEvaluationResult(node);
+ EvaluationResultImpl result = node.evaluationResult;
+ if (result != null) {
+ return result;
}
// TODO(brianwilkerson) Figure out which error to report.
return _error(node, null);
@@ -755,7 +1038,12 @@ class ConstantVisitor extends UnifyingAstVisitor<EvaluationResultImpl> {
EvaluationResultImpl visitPropertyAccess(PropertyAccess node) => _getConstantValue(node, node.propertyName.staticElement);
@override
- EvaluationResultImpl visitSimpleIdentifier(SimpleIdentifier node) => _getConstantValue(node, node.staticElement);
+ EvaluationResultImpl visitSimpleIdentifier(SimpleIdentifier node) {
+ if (_lexicalEnvironment != null && _lexicalEnvironment.containsKey(node.name)) {
+ return new ValidResult(_lexicalEnvironment[node.name]);
+ }
+ return _getConstantValue(node, node.staticElement);
+ }
@override
EvaluationResultImpl visitSimpleStringLiteral(SimpleStringLiteral node) => _valid(_typeProvider.stringType, new StringState(node.value));
@@ -787,6 +1075,58 @@ class ConstantVisitor extends UnifyingAstVisitor<EvaluationResultImpl> {
}
/**
+ * This method is called just before retrieving an evaluation result from an AST node. Unit tests
+ * will override it to introduce additional error checking.
+ */
+ void beforeGetEvaluationResult(AstNode node) {
+ }
+
+ /**
+ * Return an object representing the value 'null'.
+ *
+ * @return an object representing the value 'null'
+ */
+ DartObjectImpl get null2 {
+ if (_nullObject == null) {
+ _nullObject = new DartObjectImpl(_typeProvider.nullType, NullState.NULL_STATE);
+ }
+ return _nullObject;
+ }
+
+ ValidResult _valid(InterfaceType type, InstanceState state) => new ValidResult(new DartObjectImpl(type, state));
+
+ ValidResult _validWithUnknownValue(InterfaceType type) {
+ if (type.element.library.isDartCore) {
+ String typeName = type.name;
+ if (typeName == "bool") {
+ return _valid(type, BoolState.UNKNOWN_VALUE);
+ } else if (typeName == "double") {
+ return _valid(type, DoubleState.UNKNOWN_VALUE);
+ } else if (typeName == "int") {
+ return _valid(type, IntState.UNKNOWN_VALUE);
+ } else if (typeName == "String") {
+ return _valid(type, StringState.UNKNOWN_VALUE);
+ }
+ }
+ return _valid(type, GenericState.UNKNOWN_VALUE);
+ }
+
+ /**
+ * Return the value of the given expression, or a representation of 'null' if the expression
+ * cannot be evaluated.
+ *
+ * @param expression the expression whose value is to be returned
+ * @return the value of the given expression
+ */
+ DartObjectImpl _valueOf(Expression expression) {
+ EvaluationResultImpl expressionValue = expression.accept(this);
+ if (expressionValue is ValidResult) {
+ return expressionValue.value;
+ }
+ return null2;
+ }
+
+ /**
* Return a result object representing an error associated with the given node.
*
* @param node the AST node associated with the error
@@ -808,6 +1148,7 @@ class ConstantVisitor extends UnifyingAstVisitor<EvaluationResultImpl> {
}
if (element is VariableElementImpl) {
VariableElementImpl variableElementImpl = element;
+ beforeGetEvaluationResult(node);
EvaluationResultImpl value = variableElementImpl.evaluationResult;
if (variableElementImpl.isConst && value != null) {
return value;
@@ -825,18 +1166,6 @@ class ConstantVisitor extends UnifyingAstVisitor<EvaluationResultImpl> {
}
/**
- * Return an object representing the value 'null'.
- *
- * @return an object representing the value 'null'
- */
- DartObjectImpl get null2 {
- if (_nullObject == null) {
- _nullObject = new DartObjectImpl(_typeProvider.nullType, NullState.NULL_STATE);
- }
- return _nullObject;
- }
-
- /**
* Return the union of the errors encoded in the given results.
*
* @param leftResult the first set of errors, or `null` if there was no previous collection
@@ -855,39 +1184,6 @@ class ConstantVisitor extends UnifyingAstVisitor<EvaluationResultImpl> {
}
return leftResult;
}
-
- ValidResult _valid(InterfaceType type, InstanceState state) => new ValidResult(new DartObjectImpl(type, state));
-
- ValidResult _validWithUnknownValue(InterfaceType type) {
- if (type.element.library.isDartCore) {
- String typeName = type.name;
- if (typeName == "bool") {
- return _valid(type, BoolState.UNKNOWN_VALUE);
- } else if (typeName == "double") {
- return _valid(type, DoubleState.UNKNOWN_VALUE);
- } else if (typeName == "int") {
- return _valid(type, IntState.UNKNOWN_VALUE);
- } else if (typeName == "String") {
- return _valid(type, StringState.UNKNOWN_VALUE);
- }
- }
- return _valid(type, GenericState.UNKNOWN_VALUE);
- }
-
- /**
- * Return the value of the given expression, or a representation of 'null' if the expression
- * cannot be evaluated.
- *
- * @param expression the expression whose value is to be returned
- * @return the value of the given expression
- */
- DartObjectImpl _valueOf(Expression expression) {
- EvaluationResultImpl expressionValue = expression.accept(this);
- if (expressionValue is ValidResult) {
- return expressionValue.value;
- }
- return null2;
- }
}
/**
@@ -1143,6 +1439,8 @@ class DartObjectImpl implements DartObject {
return null;
}
+ Map<String, DartObjectImpl> get fields => _state.fields;
+
@override
int get intValue {
if (_state is IntState) {
@@ -1223,6 +1521,11 @@ class DartObjectImpl implements DartObject {
bool get isTrue => _state is BoolState && identical((_state as BoolState).value, true);
/**
+ * Return true if this object represents an unknown value.
+ */
+ bool get isUnknown => _state.isUnknown;
+
+ /**
* Return `true` if this object represents an instance of a user-defined class.
*
* @return `true` if this object represents an instance of a user-defined class
@@ -2449,6 +2752,11 @@ class GenericState extends InstanceState {
final Map<String, DartObjectImpl> _fieldMap;
/**
+ * Pseudo-field that we use to represent fields in the superclass.
+ */
+ static String SUPERCLASS_FIELD = "(super)";
+
+ /**
* A state that can be used to represent an object whose state is not known.
*/
static GenericState UNKNOWN_VALUE = new GenericState(new Map<String, DartObjectImpl>());
@@ -2464,6 +2772,15 @@ class GenericState extends InstanceState {
StringState convertToString() => StringState.UNKNOWN_VALUE;
@override
+ BoolState equalEqual(InstanceState rightOperand) {
+ assertBoolNumStringOrNull(rightOperand);
+ if (rightOperand is DynamicState) {
+ return BoolState.UNKNOWN_VALUE;
+ }
+ return BoolState.from(this == rightOperand);
+ }
+
+ @override
bool operator ==(Object object) {
if (object is! GenericState) {
return false;
@@ -2485,13 +2802,7 @@ class GenericState extends InstanceState {
}
@override
- BoolState equalEqual(InstanceState rightOperand) {
- assertBoolNumStringOrNull(rightOperand);
- if (rightOperand is DynamicState) {
- return BoolState.UNKNOWN_VALUE;
- }
- return BoolState.from(this == rightOperand);
- }
+ Map<String, DartObjectImpl> get fields => _fieldMap;
@override
String get typeName => "user defined type";
@@ -2504,6 +2815,9 @@ class GenericState extends InstanceState {
}
return hashCode;
}
+
+ @override
+ bool get isUnknown => identical(this, UNKNOWN_VALUE);
}
/**
@@ -2625,6 +2939,12 @@ abstract class InstanceState {
BoolState equalEqual(InstanceState rightOperand);
/**
+ * If this represents a generic dart object, return a map from its fieldnames to their values.
+ * Otherwise return null.
+ */
+ Map<String, DartObjectImpl> get fields => null;
+
+ /**
* Return the name of the type of this value.
*
* @return the name of the type of this value
@@ -2702,6 +3022,11 @@ abstract class InstanceState {
bool get isBoolNumStringOrNull => false;
/**
+ * Return true if this object represents an unknown value.
+ */
+ bool get isUnknown => false;
+
+ /**
* Return the result of invoking the '&lt;' operator on this object with the given argument.
*
* @param rightOperand the right-hand operand of the operation
@@ -3602,15 +3927,15 @@ class NumState extends InstanceState {
}
@override
- bool operator ==(Object object) => object is NumState;
-
- @override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
+ bool operator ==(Object object) => object is NumState;
+
+ @override
String get typeName => "num";
@override
@@ -3648,6 +3973,9 @@ class NumState extends InstanceState {
bool get isBoolNumStringOrNull => true;
@override
+ bool get isUnknown => identical(this, UNKNOWN_VALUE);
+
+ @override
BoolState lessThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
@@ -3690,15 +4018,25 @@ class NumState extends InstanceState {
*/
class ReferenceFinder extends RecursiveAstVisitor<Object> {
/**
- * The element representing the variable whose initializer will be visited.
+ * The element representing the construct that will be visited.
*/
- final VariableElement _source;
+ final AstNode _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<VariableElement> _referenceGraph;
+ final DirectedGraph<AstNode> _referenceGraph;
+
+ /**
+ * A table mapping constant variables to the declarations of those variables.
+ */
+ final Map<VariableElement, VariableDeclaration> _variableDeclarationMap;
+
+ /**
+ * A table mapping constant constructors to the declarations of those constructors.
+ */
+ final Map<ConstructorElement, ConstructorDeclaration> _constructorDeclarationMap;
/**
* Initialize a newly created reference finder to find references from the given variable to other
@@ -3707,8 +4045,20 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
* @param source the element representing the variable whose initializer will be visited
* @param referenceGraph a graph recording which variables (heads) reference which other variables
* (tails) in their initializers
+ * @param variableDeclarationMap A table mapping constant variables to the declarations of those
+ * variables.
+ * @param constructorDeclarationMap A table mapping constant constructors to the declarations of
+ * those constructors.
*/
- ReferenceFinder(this._source, this._referenceGraph);
+ ReferenceFinder(this._source, this._referenceGraph, this._variableDeclarationMap, this._constructorDeclarationMap);
+
+ @override
+ Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+ if (node.isConst) {
+ _referenceGraph.addEdge(_source, node);
+ }
+ return null;
+ }
@override
Object visitSimpleIdentifier(SimpleIdentifier node) {
@@ -3719,7 +4069,31 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
if (element is VariableElement) {
VariableElement variable = element as VariableElement;
if (variable.isConst) {
- _referenceGraph.addEdge(_source, variable);
+ VariableDeclaration variableDeclaration = _variableDeclarationMap[variable];
+ // 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);
+ }
+ }
+ }
+ return null;
+ }
+
+ @override
+ Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+ super.visitSuperConstructorInvocation(node);
+ ConstructorElement constructor = 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);
}
}
return null;
@@ -3802,6 +4176,9 @@ class StringState extends InstanceState {
bool get isBoolNumStringOrNull => true;
@override
+ bool get isUnknown => value == null;
+
+ @override
String toString() => value == null ? "-unknown-" : "'${value}'";
}
« no previous file with comments | « pkg/analyzer/lib/src/generated/ast.dart ('k') | pkg/analyzer/lib/src/generated/element.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698