Chromium Code Reviews| Index: editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/constant/ConstantValueComputer.java |
| diff --git a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/constant/ConstantValueComputer.java b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/constant/ConstantValueComputer.java |
| index a6029e2419d27d996aa4b8bab41cda7227f44fff..34d80d7c637fb5c5c805f6f1d5bf2e7607406446 100644 |
| --- a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/constant/ConstantValueComputer.java |
| +++ b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/constant/ConstantValueComputer.java |
| @@ -16,18 +16,33 @@ package com.google.dart.engine.internal.constant; |
| import com.google.dart.engine.AnalysisEngine; |
| import com.google.dart.engine.ast.AstNode; |
| import com.google.dart.engine.ast.CompilationUnit; |
| +import com.google.dart.engine.ast.ConstructorDeclaration; |
| +import com.google.dart.engine.ast.Expression; |
| import com.google.dart.engine.ast.InstanceCreationExpression; |
| +import com.google.dart.engine.ast.NamedExpression; |
| +import com.google.dart.engine.ast.NodeList; |
| import com.google.dart.engine.ast.VariableDeclaration; |
| import com.google.dart.engine.element.CompilationUnitElement; |
| +import com.google.dart.engine.element.ConstructorElement; |
| +import com.google.dart.engine.element.Element; |
| +import com.google.dart.engine.element.FieldElement; |
| +import com.google.dart.engine.element.FieldFormalParameterElement; |
| +import com.google.dart.engine.element.ParameterElement; |
| import com.google.dart.engine.element.VariableElement; |
| import com.google.dart.engine.error.AnalysisError; |
| import com.google.dart.engine.internal.element.VariableElementImpl; |
| +import com.google.dart.engine.internal.object.DartObjectImpl; |
| +import com.google.dart.engine.internal.object.GenericState; |
| +import com.google.dart.engine.internal.object.SymbolState; |
| import com.google.dart.engine.internal.resolver.TypeProvider; |
| import com.google.dart.engine.source.Source; |
| +import com.google.dart.engine.type.InterfaceType; |
| import com.google.dart.engine.utilities.collection.DirectedGraph; |
| +import com.google.dart.engine.utilities.dart.ParameterKind; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| +import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| @@ -63,6 +78,16 @@ public class ConstantValueComputer { |
| private HashMap<VariableElement, VariableDeclaration> variableDeclarationMap; |
| /** |
| + * A table mapping constant constructors to the declarations of those constructors. |
| + */ |
| + private HashMap<ConstructorElement, ConstructorDeclaration> constructorDeclarationMap; |
| + |
| + /** |
| + * A collection of constant constructor invocations. |
| + */ |
| + private HashSet<InstanceCreationExpression> constructorInvocations; |
| + |
| + /** |
| * Initialize a newly created constant value computer. |
| * |
| * @param typeProvider the type provider used to access known types |
| @@ -86,16 +111,34 @@ public class ConstantValueComputer { |
| */ |
| public void computeValues() { |
| variableDeclarationMap = constantFinder.getVariableMap(); |
| + constructorDeclarationMap = constantFinder.getConstructorMap(); |
| + constructorInvocations = constantFinder.getConstructorInvocations(); |
| for (Map.Entry<VariableElement, VariableDeclaration> entry : variableDeclarationMap.entrySet()) { |
| VariableDeclaration declaration = entry.getValue(); |
| ReferenceFinder referenceFinder = new ReferenceFinder( |
| declaration, |
| referenceGraph, |
| - variableDeclarationMap); |
| + variableDeclarationMap, |
| + constructorDeclarationMap); |
| referenceGraph.addNode(declaration); |
| declaration.getInitializer().accept(referenceFinder); |
| } |
| - // TODO(paulberry): Do the same for constant constructor invocations. |
| + for (InstanceCreationExpression expression : constructorInvocations) { |
| + ConstructorElement constructor = expression.getStaticElement(); |
| + referenceGraph.addNode(expression); |
| + ConstructorDeclaration declaration = constructorDeclarationMap.get(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) { |
| + declaration.accept(referenceFinder); |
| + } |
| + expression.getArgumentList().accept(referenceFinder); |
| + } |
| beforeGraphWalk(); |
| while (!referenceGraph.isEmpty()) { |
| AstNode node = referenceGraph.removeSink(); |
| @@ -153,31 +196,127 @@ public class ConstantValueComputer { |
| */ |
| private void computeValueFor(AstNode constNode) { |
| beforeComputeValue(constNode); |
| + EvaluationResultImpl result; |
| + Element element; |
| if (constNode instanceof VariableDeclaration) { |
| VariableDeclaration declaration = (VariableDeclaration) constNode; |
| - VariableElement variable = declaration.getElement(); |
| - EvaluationResultImpl result = declaration.getInitializer().accept(createConstantVisitor()); |
| - ((VariableElementImpl) variable).setEvaluationResult(result); |
| - if (result instanceof ErrorResult) { |
| - ArrayList<AnalysisError> errors = new ArrayList<AnalysisError>(); |
| - for (ErrorResult.ErrorData data : ((ErrorResult) result).getErrorData()) { |
| - AstNode node = data.getNode(); |
| - Source source = variable.getAncestor(CompilationUnitElement.class).getSource(); |
| - errors.add(new AnalysisError( |
| - source, |
| - node.getOffset(), |
| - node.getLength(), |
| - data.getErrorCode())); |
| - } |
| - } |
| + element = declaration.getElement(); |
| + result = declaration.getInitializer().accept(createConstantVisitor()); |
| + ((VariableElementImpl) element).setEvaluationResult(result); |
| } else if (constNode instanceof InstanceCreationExpression) { |
| - // TODO(paulberry): compute constant value for constant constructor invocations. |
| + InstanceCreationExpression expression = (InstanceCreationExpression) constNode; |
| + ConstructorElement constructor = expression.getStaticElement(); |
| + if (constructor == null) { |
| + // Couldn't resolve the constructor so we can't compute a value. No problem--the error |
| + // has already been reported. |
| + return; |
| + } |
| + element = constructor; |
| + ConstantVisitor constantVisitor = createConstantVisitor(); |
| + result = evaluateInstanceCreationExpression(expression, constructor, constantVisitor); |
| + expression.setEvaluationResult(result); |
| } else { |
| // Should not happen. |
| AnalysisEngine.getInstance().getLogger().logError( |
| "Constant value computer trying to compute the value of a node which is neither a " |
| + "VariableDeclaration nor an InstanceCreationExpression"); |
| + return; |
| + } |
| + if (result instanceof ErrorResult) { |
| + ArrayList<AnalysisError> errors = new ArrayList<AnalysisError>(); |
|
Brian Wilkerson
2014/05/08 22:12:49
Am I missing something? It looks like we're popula
Paul Berry
2014/05/08 23:12:46
Wow, you're right. It looks like the entire if-bl
|
| + for (ErrorResult.ErrorData data : ((ErrorResult) result).getErrorData()) { |
| + AstNode node = data.getNode(); |
| + Source source = element.getAncestor(CompilationUnitElement.class).getSource(); |
|
Brian Wilkerson
2014/05/08 22:12:49
Can the source be computed outside the loop?
|
| + errors.add(new AnalysisError( |
| + source, |
| + node.getOffset(), |
| + node.getLength(), |
| + data.getErrorCode())); |
| + } |
| + } |
| + } |
| + |
| + private EvaluationResultImpl evaluateInstanceCreationExpression( |
| + InstanceCreationExpression expression, ConstructorElement constructor, |
| + ConstantVisitor constantVisitor) { |
| + NodeList<Expression> arguments = expression.getArgumentList().getArguments(); |
| + int argumentCount = arguments.size(); |
| + DartObjectImpl[] argumentValues = new DartObjectImpl[argumentCount]; |
| + HashMap<String, DartObjectImpl> namedArgumentValues = new HashMap<String, DartObjectImpl>(); |
| + for (int i = 0; i < argumentCount; i++) { |
| + Expression argument = arguments.get(i); |
| + if (argument instanceof NamedExpression) { |
| + NamedExpression namedExpression = (NamedExpression) argument; |
| + String name = namedExpression.getName().getLabel().getName(); |
| + namedArgumentValues.put(name, constantVisitor.valueOf(namedExpression.getExpression())); |
| + argumentValues[i] = constantVisitor.getNull(); |
| + } else { |
| + argumentValues[i] = constantVisitor.valueOf(argument); |
| + } |
| + } |
| + HashSet<ConstructorElement> constructorsVisited = new HashSet<ConstructorElement>(); |
| + InterfaceType definingClass = (InterfaceType) constructor.getReturnType(); |
| + while (constructor.isFactory()) { |
| + if (definingClass.getElement().getLibrary().isDartCore()) { |
| + String className = definingClass.getName(); |
| + if (className.equals("Symbol") && argumentCount == 1) { |
| + String argumentValue = argumentValues[0].getStringValue(); |
| + if (argumentValue != null) { |
| + return constantVisitor.valid(definingClass, new SymbolState(argumentValue)); |
| + } |
| + } |
| + } |
| + constructorsVisited.add(constructor); |
| + ConstructorElement redirectedConstructor = constructor.getRedirectedConstructor(); |
| + 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 constantVisitor.validWithUnknownValue(definingClass); |
| + } |
| + 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 consider it an unknown value to suppress further errors. |
| + return constantVisitor.validWithUnknownValue(definingClass); |
| + } |
| + 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 consider it an unknown value to suppress further errors. |
| + return constantVisitor.validWithUnknownValue(definingClass); |
| + } |
| + constructor = redirectedConstructor; |
| + definingClass = (InterfaceType) constructor.getReturnType(); |
| + } |
| + HashMap<String, DartObjectImpl> fieldMap = new HashMap<String, DartObjectImpl>(); |
| + ParameterElement[] parameters = constructor.getParameters(); |
| + int parameterCount = parameters.length; |
| + for (int i = 0; i < parameterCount; i++) { |
| + ParameterElement parameter = parameters[i]; |
| + if (parameter.isInitializingFormal()) { |
| + FieldElement field = ((FieldFormalParameterElement) parameter).getField(); |
| + if (field != null) { |
| + String fieldName = field.getName(); |
| + if (parameter.getParameterKind() == ParameterKind.NAMED) { |
| + DartObjectImpl argumentValue = namedArgumentValues.get(parameter.getName()); |
| + if (argumentValue != null) { |
| + fieldMap.put(fieldName, argumentValue); |
| + } |
| + } else if (i < argumentCount) { |
| + fieldMap.put(fieldName, argumentValues[i]); |
| + // Otherwise, the parameter is assumed to be an optional positional parameter for which |
| + // no value was provided. |
| + } |
| + } |
| + } |
| } |
| + // 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 constantVisitor.valid(definingClass, new GenericState(fieldMap)); |
| } |
| /** |