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

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

Issue 1129563002: Create a class for evaluating const instance creation expressions. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/generated/incremental_resolver.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 0fe56bf35da8de4029630ec8ba3ca923b24f4065..f9babd13443d15d5e4cde60945fb888e3eeb7572 100644
--- a/pkg/analyzer/lib/src/generated/constant.dart
+++ b/pkg/analyzer/lib/src/generated/constant.dart
@@ -186,993 +186,1078 @@ class ConstantAstCloner extends AstCloner {
}
/**
- * Interface used by unit tests to verify correct dependency analysis during
- * constant evaluation.
+ * Helper class encapsulating the methods for evaluating constant instance
+ * constant instance creation expressions.
*/
-abstract class ConstantEvaluationValidator {
+class ConstantEvaluationEngine {
/**
- * This method is called just before computing the constant value associated
- * with [constNode]. Unit tests will override this method to introduce
- * additional error checking.
+ * Parameter to "fromEnvironment" methods that denotes the default value.
*/
- void beforeComputeValue(AstNode constNode);
+ static String _DEFAULT_VALUE_PARAM = "defaultValue";
/**
- * This method is called just before getting the constant initializers
- * associated with the [constructor]. Unit tests will override this method to
- * introduce additional error checking.
+ * Source of RegExp matching any public identifier.
+ * From sdk/lib/internal/symbol.dart.
*/
- void beforeGetConstantInitializers(ConstructorElement constructor);
+ static String _PUBLIC_IDENTIFIER_RE =
+ "(?!${ConstantValueComputer._RESERVED_WORD_RE}\\b(?!\\\$))[a-zA-Z\$][\\w\$]*";
/**
- * This method is called just before retrieving an evaluation result from an
- * AST node. Unit tests will override it to introduce additional error
- * checking.
+ * RegExp that validates a non-empty non-private symbol.
+ * From sdk/lib/internal/symbol.dart.
*/
- void beforeGetEvaluationResult(AstNode node);
+ static RegExp _PUBLIC_SYMBOL_PATTERN = new RegExp(
+ "^(?:${ConstantValueComputer._OPERATOR_RE}\$|$_PUBLIC_IDENTIFIER_RE(?:=?\$|[.](?!\$)))+?\$");
/**
- * This method is called just before getting the constant value of a field
- * with an initializer. Unit tests will override this method to introduce
- * additional error checking.
+ * The type provider used to access the known types.
*/
- void beforeGetFieldEvaluationResult(FieldElementImpl field);
+ final TypeProvider typeProvider;
/**
- * This method is called just before getting a parameter's default value. Unit
- * tests will override this method to introduce additional error checking.
+ * The set of variables declared on the command line using '-D'.
*/
- void beforeGetParameterDefault(ParameterElement parameter);
-}
-
-/**
- * Implementation of [ConstantEvaluationValidator] used in production; does no
- * validation.
- */
-class ConstantEvaluationValidator_ForProduction
- implements ConstantEvaluationValidator {
- @override
- void beforeComputeValue(AstNode constNode) {}
-
- @override
- void beforeGetConstantInitializers(ConstructorElement constructor) {}
-
- @override
- void beforeGetEvaluationResult(AstNode node) {}
-
- @override
- void beforeGetFieldEvaluationResult(FieldElementImpl field) {}
-
- @override
- void beforeGetParameterDefault(ParameterElement parameter) {}
-}
+ final DeclaredVariables _declaredVariables;
-/**
- * Instances of the class `ConstantEvaluator` evaluate constant expressions to
- * produce their compile-time value. According to the Dart Language
- * Specification:
- * <blockquote>
- * A constant expression is one of the following:
- * * A literal number.
- * * A literal boolean.
- * * A literal string where any interpolated expression is a compile-time
- * constant that evaluates to a numeric, string or boolean value or to
- * <b>null</b>.
- * * A literal symbol.
- * * <b>null</b>.
- * * A qualified reference to a static constant variable.
- * * An identifier expression that denotes a constant variable, class or type
- * alias.
- * * A constant constructor invocation.
- * * A constant list literal.
- * * A constant map literal.
- * * A simple or qualified identifier denoting a top-level function or a static
- * method.
- * * A parenthesized expression <i>(e)</i> where <i>e</i> is a constant
- * expression.
- * * An expression of the form <i>identical(e<sub>1</sub>, e<sub>2</sub>)</i>
- * where <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
- * expressions and <i>identical()</i> is statically bound to the predefined
- * dart function <i>identical()</i> discussed above.
- * * An expression of one of the forms <i>e<sub>1</sub> == e<sub>2</sub></i> or
- * <i>e<sub>1</sub> != e<sub>2</sub></i> where <i>e<sub>1</sub></i> and
- * <i>e<sub>2</sub></i> are constant expressions that evaluate to a numeric,
- * string or boolean value.
- * * An expression of one of the forms <i>!e</i>, <i>e<sub>1</sub> &amp;&amp;
- * e<sub>2</sub></i> or <i>e<sub>1</sub> || e<sub>2</sub></i>, where <i>e</i>,
- * <i>e1</sub></i> and <i>e2</sub></i> are constant expressions that evaluate
- * to a boolean value.
- * * An expression of one of the forms <i>~e</i>, <i>e<sub>1</sub> ^
- * e<sub>2</sub></i>, <i>e<sub>1</sub> &amp; e<sub>2</sub></i>,
- * <i>e<sub>1</sub> | e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;&gt;
- * e<sub>2</sub></i> or <i>e<sub>1</sub> &lt;&lt; e<sub>2</sub></i>, where
- * <i>e</i>, <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
- * expressions that evaluate to an integer value or to <b>null</b>.
- * * An expression of one of the forms <i>-e</i>, <i>e<sub>1</sub> +
- * e<sub>2</sub></i>, <i>e<sub>1</sub> -e<sub>2</sub></i>, <i>e<sub>1</sub> *
- * e<sub>2</sub></i>, <i>e<sub>1</sub> / e<sub>2</sub></i>, <i>e<sub>1</sub>
- * ~/ e<sub>2</sub></i>, <i>e<sub>1</sub> &gt; e<sub>2</sub></i>,
- * <i>e<sub>1</sub> &lt; e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;=
- * e<sub>2</sub></i>, <i>e<sub>1</sub> &lt;= e<sub>2</sub></i> or
- * <i>e<sub>1</sub> % e<sub>2</sub></i>, where <i>e</i>, <i>e<sub>1</sub></i>
- * and <i>e<sub>2</sub></i> are constant expressions that evaluate to a
- * numeric value or to <b>null</b>.
- * * An expression of the form <i>e<sub>1</sub> ? e<sub>2</sub> :
- * e<sub>3</sub></i> where <i>e<sub>1</sub></i>, <i>e<sub>2</sub></i> and
- * <i>e<sub>3</sub></i> are constant expressions, and <i>e<sub>1</sub></i>
- * evaluates to a boolean value.
- * </blockquote>
- */
-class ConstantEvaluator {
/**
- * The source containing the expression(s) that will be evaluated.
+ * Validator used to verify correct dependency analysis when running unit
+ * tests.
*/
- final Source _source;
+ final ConstantEvaluationValidator validator;
/**
- * The type provider used to access the known types.
+ * Initialize a newly created [ConstantEvaluationEngine]. The [typeProvider]
+ * is used to access known types. [_declaredVariables] is the set of
+ * variables declared on the command line using '-D'. The [validator], if
+ * given, is used to verify correct dependency analysis when running unit
+ * tests.
*/
- final TypeProvider _typeProvider;
+ ConstantEvaluationEngine(this.typeProvider, this._declaredVariables,
+ {ConstantEvaluationValidator validator})
+ : validator = validator != null
+ ? validator
+ : new ConstantEvaluationValidator_ForProduction();
/**
- * Initialize a newly created evaluator to evaluate expressions in the given
- * [source]. The [typeProvider] is the type provider used to access known
- * types.
+ * Check that the arguments to a call to fromEnvironment() are correct. The
+ * [arguments] are the AST nodes of the arguments. The [argumentValues] are
+ * the values of the unnamed arguments. The [namedArgumentValues] are the
+ * values of the named arguments. The [expectedDefaultValueType] is the
+ * allowed type of the "defaultValue" parameter (if present). Note:
+ * "defaultValue" is always allowed to be null. Return `true` if the arguments
+ * are correct, `false` if there is an error.
*/
- ConstantEvaluator(this._source, this._typeProvider);
-
- EvaluationResult evaluate(Expression expression) {
- RecordingErrorListener errorListener = new RecordingErrorListener();
- ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
- DartObjectImpl result =
- expression.accept(new ConstantVisitor(_typeProvider, errorReporter));
- if (result != null) {
- return EvaluationResult.forValue(result);
+ bool checkFromEnvironmentArguments(NodeList<Expression> arguments,
+ List<DartObjectImpl> argumentValues,
+ HashMap<String, DartObjectImpl> namedArgumentValues,
+ InterfaceType expectedDefaultValueType) {
+ int argumentCount = arguments.length;
+ if (argumentCount < 1 || argumentCount > 2) {
+ return false;
}
- return EvaluationResult.forErrors(errorListener.errors);
+ if (arguments[0] is NamedExpression) {
+ return false;
+ }
+ if (!identical(argumentValues[0].type, typeProvider.stringType)) {
+ return false;
+ }
+ if (argumentCount == 2) {
+ if (arguments[1] is! NamedExpression) {
+ return false;
+ }
+ if (!((arguments[1] as NamedExpression).name.label.name ==
+ _DEFAULT_VALUE_PARAM)) {
+ return false;
+ }
+ ParameterizedType defaultValueType =
+ namedArgumentValues[_DEFAULT_VALUE_PARAM].type;
+ if (!(identical(defaultValueType, expectedDefaultValueType) ||
+ identical(defaultValueType, typeProvider.nullType))) {
+ return false;
+ }
+ }
+ return true;
}
-}
-
-/**
- * A visitor used to traverse the AST structures of all of the compilation units
- * being resolved and build tables of the constant variables, constant
- * constructors, constant constructor invocations, and annotations found in
- * those compilation units.
- */
-class ConstantFinder extends RecursiveAstVisitor<Object> {
- /**
- * A table mapping constant variable elements to the declarations of those
- * variables.
- */
- 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>();
-
- /**
- * A collection of constant constructor invocations.
- */
- final List<InstanceCreationExpression> constructorInvocations =
- new List<InstanceCreationExpression>();
-
- /**
- * A collection of annotations.
- */
- final List<Annotation> annotations = <Annotation>[];
/**
- * True if instance variables marked as "final" should be treated as "const".
+ * Check that the arguments to a call to Symbol() are correct. The [arguments]
+ * are the AST nodes of the arguments. The [argumentValues] are the values of
+ * the unnamed arguments. The [namedArgumentValues] are the values of the
+ * named arguments. Return `true` if the arguments are correct, `false` if
+ * there is an error.
*/
- bool treatFinalInstanceVarAsConst = false;
-
- @override
- Object visitAnnotation(Annotation node) {
- super.visitAnnotation(node);
- annotations.add(node);
- return null;
- }
-
- @override
- Object visitClassDeclaration(ClassDeclaration node) {
- bool prevTreatFinalInstanceVarAsConst = treatFinalInstanceVarAsConst;
- if (node.element.constructors.any((ConstructorElement e) => e.isConst)) {
- // Instance vars marked "final" need to be included in the dependency
- // graph, since constant constructors implicitly use the values in their
- // initializers.
- treatFinalInstanceVarAsConst = true;
+ bool checkSymbolArguments(NodeList<Expression> arguments,
+ List<DartObjectImpl> argumentValues,
+ HashMap<String, DartObjectImpl> namedArgumentValues) {
+ if (arguments.length != 1) {
+ return false;
}
- try {
- return super.visitClassDeclaration(node);
- } finally {
- treatFinalInstanceVarAsConst = prevTreatFinalInstanceVarAsConst;
+ if (arguments[0] is NamedExpression) {
+ return false;
}
- }
-
- @override
- Object visitConstructorDeclaration(ConstructorDeclaration node) {
- super.visitConstructorDeclaration(node);
- if (node.constKeyword != null) {
- ConstructorElement element = node.element;
- if (element != null) {
- constructorMap[element] = node;
- }
+ if (!identical(argumentValues[0].type, typeProvider.stringType)) {
+ return false;
}
- return null;
+ String name = argumentValues[0].stringValue;
+ return isValidPublicSymbol(name);
}
- @override
- Object visitInstanceCreationExpression(InstanceCreationExpression node) {
- super.visitInstanceCreationExpression(node);
- if (node.isConst) {
- constructorInvocations.add(node);
- }
- return null;
- }
-
- @override
- Object visitVariableDeclaration(VariableDeclaration node) {
- super.visitVariableDeclaration(node);
- Expression initializer = node.initializer;
- VariableElement element = node.element;
- if (initializer != null &&
- (node.isConst ||
- treatFinalInstanceVarAsConst &&
- element is FieldElement &&
- node.isFinal &&
- !element.isStatic)) {
- if (node.element != null) {
- variableMap[node.element as PotentiallyConstVariableElement] = node;
- }
- }
- return null;
- }
-}
-
-/**
- * An object used to 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 {
- /**
- * Parameter to "fromEnvironment" methods that denotes the default value.
- */
- static String _DEFAULT_VALUE_PARAM = "defaultValue";
-
- /**
- * Source of RegExp matching declarable operator names.
- * From sdk/lib/internal/symbol.dart.
- */
- static String _OPERATOR_RE =
- "(?:[\\-+*/%&|^]|\\[\\]=?|==|~/?|<[<=]?|>[>=]?|unary-)";
-
- /**
- * Source of RegExp matching any public identifier.
- * From sdk/lib/internal/symbol.dart.
- */
- static String _PUBLIC_IDENTIFIER_RE =
- "(?!${ConstantValueComputer._RESERVED_WORD_RE}\\b(?!\\\$))[a-zA-Z\$][\\w\$]*";
-
- /**
- * Source of RegExp matching Dart reserved words.
- * From sdk/lib/internal/symbol.dart.
- */
- static String _RESERVED_WORD_RE =
- "(?:assert|break|c(?:a(?:se|tch)|lass|on(?:st|tinue))|d(?:efault|o)|e(?:lse|num|xtends)|f(?:alse|inal(?:ly)?|or)|i[fns]|n(?:ew|ull)|ret(?:hrow|urn)|s(?:uper|witch)|t(?:h(?:is|row)|r(?:ue|y))|v(?:ar|oid)|w(?:hile|ith))";
-
- /**
- * RegExp that validates a non-empty non-private symbol.
- * From sdk/lib/internal/symbol.dart.
- */
- static RegExp _PUBLIC_SYMBOL_PATTERN = new RegExp(
- "^(?:${ConstantValueComputer._OPERATOR_RE}\$|$_PUBLIC_IDENTIFIER_RE(?:=?\$|[.](?!\$)))+?\$");
-
/**
- * The type provider used to access the known types.
- */
- final TypeProvider typeProvider;
-
- /**
- * Validator used to verify correct dependency analysis when running unit
- * tests.
- */
- final ConstantEvaluationValidator validator;
-
- /**
- * The object used to find constant variables and constant constructor
- * invocations in the compilation units that were added.
- */
- ConstantFinder _constantFinder = new ConstantFinder();
-
- /**
- * A graph in which the nodes are the constants, and the edges are from each
- * constant to the other constants that are referenced by it.
- */
- DirectedGraph<AstNode> referenceGraph = new DirectedGraph<AstNode>();
-
- /**
- * A table mapping constant variables to the declarations of those variables.
- */
- HashMap<PotentiallyConstVariableElement, VariableDeclaration> _variableDeclarationMap;
-
- /**
- * A table mapping constant constructors to the declarations of those
- * constructors.
- */
- HashMap<ConstructorElement, ConstructorDeclaration> constructorDeclarationMap;
-
- /**
- * A collection of constant constructor invocations.
- */
- List<InstanceCreationExpression> _constructorInvocations;
-
- /**
- * A collection of annotations.
- */
- List<Annotation> _annotations;
-
- /**
- * The set of variables declared on the command line using '-D'.
- */
- final DeclaredVariables _declaredVariables;
-
- /**
- * Initialize a newly created constant value computer. The [typeProvider] is
- * the type provider used to access known types. The [declaredVariables] is
- * the set of variables declared on the command line using '-D'.
- */
- ConstantValueComputer(this.typeProvider, this._declaredVariables,
- [ConstantEvaluationValidator validator])
- : validator = validator != null
- ? validator
- : new ConstantEvaluationValidator_ForProduction();
-
- /**
- * Add the constants in the given compilation [unit] to the list of constants
- * whose value needs to be computed.
+ * Evaluate a call to fromEnvironment() on the bool, int, or String class. The
+ * [environmentValue] is the value fetched from the environment. The
+ * [builtInDefaultValue] is the value that should be used as the default if no
+ * "defaultValue" argument appears in [namedArgumentValues]. The
+ * [namedArgumentValues] are the values of the named parameters passed to
+ * fromEnvironment(). Return a [DartObjectImpl] object corresponding to the
+ * evaluated result.
*/
- void add(CompilationUnit unit) {
- unit.accept(_constantFinder);
+ DartObjectImpl computeValueFromEnvironment(DartObject environmentValue,
+ DartObjectImpl builtInDefaultValue,
+ HashMap<String, DartObjectImpl> namedArgumentValues) {
+ DartObjectImpl value = environmentValue as DartObjectImpl;
+ if (value.isUnknown || value.isNull) {
+ // The name either doesn't exist in the environment or we couldn't parse
+ // the corresponding value.
+ // If the code supplied an explicit default, use it.
+ if (namedArgumentValues.containsKey(_DEFAULT_VALUE_PARAM)) {
+ value = namedArgumentValues[_DEFAULT_VALUE_PARAM];
+ } else if (value.isNull) {
+ // The code didn't supply an explicit default.
+ // The name exists in the environment but we couldn't parse the
+ // corresponding value.
+ // So use the built-in default value, because this is what the VM does.
+ value = builtInDefaultValue;
+ } else {
+ // The code didn't supply an explicit default.
+ // The name doesn't exist in the environment.
+ // The VM would use the built-in default value, but we don't want to do
+ // that for analysis because it's likely to lead to cascading errors.
+ // So just leave [value] in the unknown state.
+ }
+ }
+ return value;
}
- /**
- * Compute values for all of the constants in the compilation units that were
- * added.
- */
- void computeValues() {
- _variableDeclarationMap = _constantFinder.variableMap;
- constructorDeclarationMap = _constantFinder.constructorMap;
- _constructorInvocations = _constantFinder.constructorInvocations;
- _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 =
- _getConstRedirectedConstructor(element);
- if (redirectedConstructor != null) {
- ConstructorElement redirectedConstructorBase =
- _getConstructorBase(redirectedConstructor);
- ConstructorDeclaration redirectedConstructorDeclaration =
- findConstructorDeclaration(redirectedConstructorBase);
- referenceGraph.addEdge(declaration, redirectedConstructorDeclaration);
- return;
+ DartObjectImpl evaluateConstructorCall(AstNode node,
+ NodeList<Expression> arguments, ConstructorElement constructor,
+ ConstantVisitor constantVisitor, ErrorReporter errorReporter) {
+ if (!_getConstructorBase(constructor).isCycleFree) {
+ // It's not safe to evaluate this constructor, so bail out.
+ // TODO(paulberry): ensure that a reasonable error message is produced
+ // in this case, as well as other cases involving constant expression
+ // circularities (e.g. "compile-time constant expression depends on
+ // itself")
+ return new DartObjectImpl.validWithUnknownValue(constructor.returnType);
+ }
+ int argumentCount = arguments.length;
+ List<DartObjectImpl> argumentValues =
+ new List<DartObjectImpl>(argumentCount);
+ List<Expression> argumentNodes = new List<Expression>(argumentCount);
+ HashMap<String, DartObjectImpl> namedArgumentValues =
+ new HashMap<String, DartObjectImpl>();
+ HashMap<String, NamedExpression> namedArgumentNodes =
+ new HashMap<String, NamedExpression>();
+ for (int i = 0; i < argumentCount; i++) {
+ Expression argument = arguments[i];
+ if (argument is NamedExpression) {
+ String name = argument.name.label.name;
+ namedArgumentValues[name] =
+ constantVisitor._valueOf(argument.expression);
+ namedArgumentNodes[name] = argument;
+ argumentValues[i] = typeProvider.nullObject;
+ } else {
+ argumentValues[i] = constantVisitor._valueOf(argument);
+ argumentNodes[i] = argument;
}
- 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;
+ }
+ 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.
+ if (constructor.name == "fromEnvironment") {
+ if (!checkFromEnvironmentArguments(
+ arguments, argumentValues, namedArgumentValues, definingClass)) {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
+ return null;
+ }
+ String variableName =
+ argumentCount < 1 ? null : argumentValues[0].stringValue;
+ if (identical(definingClass, typeProvider.boolType)) {
+ DartObject valueFromEnvironment;
+ valueFromEnvironment =
+ _declaredVariables.getBool(typeProvider, variableName);
+ return computeValueFromEnvironment(valueFromEnvironment,
+ new DartObjectImpl(typeProvider.boolType, BoolState.FALSE_STATE),
+ namedArgumentValues);
+ } else if (identical(definingClass, typeProvider.intType)) {
+ DartObject valueFromEnvironment;
+ valueFromEnvironment =
+ _declaredVariables.getInt(typeProvider, variableName);
+ return computeValueFromEnvironment(valueFromEnvironment,
+ new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE),
+ namedArgumentValues);
+ } else if (identical(definingClass, typeProvider.stringType)) {
+ DartObject valueFromEnvironment;
+ valueFromEnvironment =
+ _declaredVariables.getString(typeProvider, variableName);
+ return computeValueFromEnvironment(valueFromEnvironment,
+ new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE),
+ namedArgumentValues);
+ }
+ } else if (constructor.name == "" &&
+ identical(definingClass, typeProvider.symbolType) &&
+ argumentCount == 1) {
+ if (!checkSymbolArguments(
+ arguments, argumentValues, namedArgumentValues)) {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
+ return null;
+ }
+ String argumentValue = argumentValues[0].stringValue;
+ return new DartObjectImpl(
+ 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 new DartObjectImpl.validWithUnknownValue(definingClass);
+ }
+ validator.beforeGetConstantInitializers(constructor);
+ ConstructorElementImpl constructorBase = _getConstructorBase(constructor);
+ 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 new DartObjectImpl.validWithUnknownValue(definingClass);
+ }
+ HashMap<String, DartObjectImpl> fieldMap =
+ new HashMap<String, DartObjectImpl>();
+ // Start with final fields that are initialized at their declaration site.
+ for (FieldElement field in constructor.enclosingElement.fields) {
+ if ((field.isFinal || field.isConst) &&
+ !field.isStatic &&
+ field is ConstFieldElementImpl) {
+ validator.beforeGetFieldEvaluationResult(field);
+ EvaluationResultImpl evaluationResult = field.evaluationResult;
+ DartType fieldType =
+ FieldMember.from(field, constructor.returnType).type;
+ DartObjectImpl fieldValue = evaluationResult.value;
+ if (fieldValue != null && !runtimeTypeMatch(fieldValue, fieldType)) {
+ errorReporter.reportErrorForNode(
+ CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH,
+ node, [fieldValue.type, field.name, fieldType]);
}
- initializer.accept(referenceFinder);
+ fieldMap[field.name] = evaluationResult.value;
}
- 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);
- }
+ }
+ // Now evaluate the constructor declaration.
+ HashMap<String, DartObjectImpl> parameterMap =
+ new HashMap<String, DartObjectImpl>();
+ List<ParameterElement> parameters = constructor.parameters;
+ int parameterCount = parameters.length;
+ for (int i = 0; i < parameterCount; i++) {
+ ParameterElement parameter = parameters[i];
+ ParameterElement baseParameter = parameter;
+ while (baseParameter is ParameterMember) {
+ baseParameter = (baseParameter as ParameterMember).baseElement;
+ }
+ DartObjectImpl argumentValue = null;
+ AstNode errorTarget = null;
+ if (baseParameter.parameterKind == ParameterKind.NAMED) {
+ argumentValue = namedArgumentValues[baseParameter.name];
+ errorTarget = namedArgumentNodes[baseParameter.name];
+ } else if (i < argumentCount) {
+ argumentValue = argumentValues[i];
+ errorTarget = argumentNodes[i];
+ }
+ if (errorTarget == null) {
+ // No argument node that we can direct error messages to, because we
+ // are handling an optional parameter that wasn't specified. So just
+ // direct error messages to the constructor call.
+ errorTarget = node;
+ }
+ if (argumentValue == null && baseParameter is ParameterElementImpl) {
+ // The parameter is an optional positional parameter for which no value
+ // was provided, so use the default value.
+ validator.beforeGetParameterDefault(baseParameter);
+ EvaluationResultImpl evaluationResult = baseParameter.evaluationResult;
+ if (evaluationResult == null) {
+ // No default was provided, so the default value is null.
+ argumentValue = typeProvider.nullObject;
+ } else if (evaluationResult.value != null) {
+ argumentValue = evaluationResult.value;
}
}
- 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);
+ if (argumentValue != null) {
+ if (!runtimeTypeMatch(argumentValue, parameter.type)) {
+ errorReporter.reportErrorForNode(
+ CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
+ errorTarget, [argumentValue.type, parameter.type]);
+ }
+ if (baseParameter.isInitializingFormal) {
+ FieldElement field = (parameter as FieldFormalParameterElement).field;
+ if (field != null) {
+ DartType fieldType = field.type;
+ if (fieldType != parameter.type) {
+ // We've already checked that the argument can be assigned to the
+ // parameter; we also need to check that it can be assigned to
+ // the field.
+ if (!runtimeTypeMatch(argumentValue, fieldType)) {
+ errorReporter.reportErrorForNode(
+ CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
+ errorTarget, [argumentValue.type, fieldType]);
+ }
+ }
+ String fieldName = field.name;
+ fieldMap[fieldName] = argumentValue;
}
+ } else {
+ String name = baseParameter.name;
+ parameterMap[name] = argumentValue;
}
}
- 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);
+ }
+ ConstantVisitor initializerVisitor = new ConstantVisitor(
+ this, errorReporter, lexicalEnvironment: parameterMap);
+ String superName = null;
+ NodeList<Expression> superArguments = null;
+ for (ConstructorInitializer initializer in initializers) {
+ if (initializer is ConstructorFieldInitializer) {
+ ConstructorFieldInitializer constructorFieldInitializer = initializer;
+ Expression initializerExpression =
+ constructorFieldInitializer.expression;
+ DartObjectImpl evaluationResult =
+ initializerExpression.accept(initializerVisitor);
+ if (evaluationResult != null) {
+ String fieldName = constructorFieldInitializer.fieldName.name;
+ fieldMap[fieldName] = evaluationResult;
+ PropertyAccessorElement getter = definingClass.getGetter(fieldName);
+ if (getter != null) {
+ PropertyInducingElement field = getter.variable;
+ if (!runtimeTypeMatch(evaluationResult, field.type)) {
+ errorReporter.reportErrorForNode(
+ CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH,
+ node, [evaluationResult.type, fieldName, field.type]);
+ }
}
}
+ } else if (initializer is SuperConstructorInvocation) {
+ SuperConstructorInvocation superConstructorInvocation = initializer;
+ SimpleIdentifier name = superConstructorInvocation.constructorName;
+ if (name != null) {
+ superName = name.name;
+ }
+ superArguments = superConstructorInvocation.argumentList.arguments;
+ } else if (initializer is RedirectingConstructorInvocation) {
+ // This is a redirecting constructor, so just evaluate the constructor
+ // it redirects to.
+ ConstructorElement constructor = initializer.staticElement;
+ if (constructor != null && constructor.isConst) {
+ return evaluateConstructorCall(node,
+ initializer.argumentList.arguments, constructor,
+ initializerVisitor, errorReporter);
+ }
}
- });
- for (InstanceCreationExpression expression in _constructorInvocations) {
- referenceGraph.addNode(expression);
- ConstructorElement constructor = expression.staticElement;
- if (constructor == null) {
- continue;
+ }
+ // 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(node, fieldMap, superConstructor,
+ superArguments, initializerVisitor, errorReporter);
}
- 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);
+ }
+ return new DartObjectImpl(definingClass, new GenericState(fieldMap));
+ }
+
+ void evaluateSuperConstructorCall(AstNode node,
+ HashMap<String, DartObjectImpl> fieldMap,
+ ConstructorElement superConstructor, NodeList<Expression> superArguments,
+ ConstantVisitor initializerVisitor, ErrorReporter errorReporter) {
+ if (superConstructor != null && superConstructor.isConst) {
+ DartObjectImpl evaluationResult = evaluateConstructorCall(node,
+ superArguments, superConstructor, initializerVisitor, errorReporter);
+ if (evaluationResult != null) {
+ fieldMap[GenericState.SUPERCLASS_FIELD] = evaluationResult;
}
- expression.argumentList.accept(referenceFinder);
}
- List<List<AstNode>> topologicalSort =
- referenceGraph.computeTopologicalSort();
- for (List<AstNode> constantsInCycle in topologicalSort) {
- if (constantsInCycle.length == 1) {
- _computeValueFor(constantsInCycle[0]);
+ }
+
+ /**
+ * 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) {
+ HashSet<ConstructorElement> constructorsVisited =
+ new HashSet<ConstructorElement>();
+ while (true) {
+ ConstructorElement redirectedConstructor =
+ getConstRedirectedConstructor(constructor);
+ if (redirectedConstructor == null) {
+ break;
} else {
- for (AstNode constant in constantsInCycle) {
- _generateCycleError(constantsInCycle, constant);
+ ConstructorElement constructorBase = _getConstructorBase(constructor);
+ constructorsVisited.add(constructorBase);
+ ConstructorElement redirectedConstructorBase =
+ _getConstructorBase(redirectedConstructor);
+ if (constructorsVisited.contains(redirectedConstructorBase)) {
+ // Cycle in redirecting factory constructors--this is not allowed
+ // and is checked elsewhere--see
+ // [ErrorVerifier.checkForRecursiveFactoryRedirect()]).
+ break;
}
}
+ constructor = redirectedConstructor;
}
- // Since no constant can depend on an annotation, we don't waste time
- // including them in the topological sort. We just process all the
- // annotations after all other constants are finished.
- for (Annotation annotation in _annotations) {
- _computeValueFor(annotation);
- }
+ return constructor;
}
- ConstructorDeclaration findConstructorDeclaration(
- ConstructorElement constructor) =>
- constructorDeclarationMap[_getConstructorBase(constructor)];
-
- VariableDeclaration findVariableDeclaration(
- PotentiallyConstVariableElement variable) =>
- _variableDeclarationMap[variable];
-
/**
- * Check that the arguments to a call to fromEnvironment() are correct. The
- * [arguments] are the AST nodes of the arguments. The [argumentValues] are
- * the values of the unnamed arguments. The [namedArgumentValues] are the
- * values of the named arguments. The [expectedDefaultValueType] is the
- * allowed type of the "defaultValue" parameter (if present). Note:
- * "defaultValue" is always allowed to be null. Return `true` if the arguments
- * are correct, `false` if there is an error.
+ * If [constructor] redirects to another const constructor, return the
+ * const constructor it redirects to. Otherwise return `null`.
*/
- bool _checkFromEnvironmentArguments(NodeList<Expression> arguments,
- List<DartObjectImpl> argumentValues,
- HashMap<String, DartObjectImpl> namedArgumentValues,
- InterfaceType expectedDefaultValueType) {
- int argumentCount = arguments.length;
- if (argumentCount < 1 || argumentCount > 2) {
- return false;
+ ConstructorElement getConstRedirectedConstructor(
+ ConstructorElement constructor) {
+ if (!constructor.isFactory) {
+ return null;
}
- if (arguments[0] is NamedExpression) {
- return false;
+ 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.
+ return null;
}
- if (!identical(argumentValues[0].type, typeProvider.stringType)) {
- return false;
+ ConstructorElement redirectedConstructor =
+ constructor.redirectedConstructor;
+ if (redirectedConstructor == null) {
+ // This can happen if constructor is an external factory constructor.
+ return null;
}
- if (argumentCount == 2) {
- if (arguments[1] is! NamedExpression) {
- return false;
- }
- if (!((arguments[1] as NamedExpression).name.label.name ==
- _DEFAULT_VALUE_PARAM)) {
- return false;
- }
- ParameterizedType defaultValueType =
- namedArgumentValues[_DEFAULT_VALUE_PARAM].type;
- if (!(identical(defaultValueType, expectedDefaultValueType) ||
- identical(defaultValueType, typeProvider.nullType))) {
- return false;
- }
+ if (!redirectedConstructor.isConst) {
+ // Delegating to a non-const constructor--this is not allowed (and
+ // is checked elsewhere--see
+ // [ErrorVerifier.checkForRedirectToNonConstConstructor()]).
+ return null;
}
- return true;
+ return redirectedConstructor;
}
/**
- * Check that the arguments to a call to Symbol() are correct. The [arguments]
- * are the AST nodes of the arguments. The [argumentValues] are the values of
- * the unnamed arguments. The [namedArgumentValues] are the values of the
- * named arguments. Return `true` if the arguments are correct, `false` if
- * there is an error.
+ * Check if the object [obj] matches the type [type] according to runtime type
+ * checking rules.
*/
- bool _checkSymbolArguments(NodeList<Expression> arguments,
- List<DartObjectImpl> argumentValues,
- HashMap<String, DartObjectImpl> namedArgumentValues) {
- if (arguments.length != 1) {
- return false;
+ bool runtimeTypeMatch(DartObjectImpl obj, DartType type) {
+ if (obj.isNull) {
+ return true;
}
- if (arguments[0] is NamedExpression) {
+ if (type.isUndefined) {
return false;
}
- if (!identical(argumentValues[0].type, typeProvider.stringType)) {
- return false;
+ return obj.type.isSubtypeOf(type);
+ }
+
+ ConstructorElementImpl _getConstructorBase(ConstructorElement constructor) {
+ while (constructor is ConstructorMember) {
+ constructor = (constructor as ConstructorMember).baseElement;
}
- String name = argumentValues[0].stringValue;
- return isValidPublicSymbol(name);
+ return constructor;
}
/**
- * Compute a value for the given [constNode].
+ * Determine whether the given string is a valid name for a public symbol
+ * (i.e. whether it is allowed for a call to the Symbol constructor).
*/
- void _computeValueFor(AstNode constNode) {
- 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(typeProvider, errorReporter,
- validator: validator));
- if (dartObject != null) {
- if (!_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 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.
- // But we still need to store an evaluation result.
- expression.constantHandle.evaluationResult =
- new EvaluationResultImpl.con1(null);
- return;
- }
- RecordingErrorListener errorListener = new RecordingErrorListener();
- CompilationUnit sourceCompilationUnit =
- expression.getAncestor((node) => node is CompilationUnit);
- ErrorReporter errorReporter = new ErrorReporter(
- errorListener, sourceCompilationUnit.element.source);
- ConstantVisitor constantVisitor = new ConstantVisitor(
- typeProvider, errorReporter, validator: validator);
- DartObjectImpl result = _evaluateConstructorCall(constNode,
- expression.argumentList.arguments, constructor, constantVisitor,
- errorReporter);
- expression.constantHandle.evaluationResult =
- new EvaluationResultImpl.con2(result, 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) {
- DefaultFormalParameter parameter = constNode;
- ParameterElement element = parameter.element;
- Expression defaultValue = parameter.defaultValue;
- if (defaultValue != null) {
- RecordingErrorListener errorListener = new RecordingErrorListener();
- ErrorReporter errorReporter =
- new ErrorReporter(errorListener, element.source);
- DartObjectImpl dartObject = defaultValue.accept(new ConstantVisitor(
- typeProvider, errorReporter, validator: validator));
- (element as ParameterElementImpl).evaluationResult =
- 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(
- typeProvider, errorReporter, validator: validator);
- DartObjectImpl result = _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 {
- // 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;
+ static bool isValidPublicSymbol(String name) => name.isEmpty ||
+ name == "void" ||
+ new JavaPatternMatcher(_PUBLIC_SYMBOL_PATTERN, name).matches();
+}
+
+/**
+ * Interface used by unit tests to verify correct dependency analysis during
+ * constant evaluation.
+ */
+abstract class ConstantEvaluationValidator {
+ /**
+ * This method is called just before computing the constant value associated
+ * with [constNode]. 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 the [constructor]. Unit tests will override this method to
+ * introduce additional error checking.
+ */
+ void beforeGetConstantInitializers(ConstructorElement constructor);
+
+ /**
+ * 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);
+
+ /**
+ * This method is called just before getting the constant value of a field
+ * with an initializer. Unit tests will override this method to introduce
+ * additional error checking.
+ */
+ void beforeGetFieldEvaluationResult(FieldElementImpl field);
+
+ /**
+ * 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);
+}
+
+/**
+ * Implementation of [ConstantEvaluationValidator] used in production; does no
+ * validation.
+ */
+class ConstantEvaluationValidator_ForProduction
+ implements ConstantEvaluationValidator {
+ @override
+ void beforeComputeValue(AstNode constNode) {}
+
+ @override
+ void beforeGetConstantInitializers(ConstructorElement constructor) {}
+
+ @override
+ void beforeGetEvaluationResult(AstNode node) {}
+
+ @override
+ void beforeGetFieldEvaluationResult(FieldElementImpl field) {}
+
+ @override
+ void beforeGetParameterDefault(ParameterElement parameter) {}
+}
+
+/**
+ * Instances of the class `ConstantEvaluator` evaluate constant expressions to
+ * produce their compile-time value. According to the Dart Language
+ * Specification:
+ * <blockquote>
+ * A constant expression is one of the following:
+ * * A literal number.
+ * * A literal boolean.
+ * * A literal string where any interpolated expression is a compile-time
+ * constant that evaluates to a numeric, string or boolean value or to
+ * <b>null</b>.
+ * * A literal symbol.
+ * * <b>null</b>.
+ * * A qualified reference to a static constant variable.
+ * * An identifier expression that denotes a constant variable, class or type
+ * alias.
+ * * A constant constructor invocation.
+ * * A constant list literal.
+ * * A constant map literal.
+ * * A simple or qualified identifier denoting a top-level function or a static
+ * method.
+ * * A parenthesized expression <i>(e)</i> where <i>e</i> is a constant
+ * expression.
+ * * An expression of the form <i>identical(e<sub>1</sub>, e<sub>2</sub>)</i>
+ * where <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
+ * expressions and <i>identical()</i> is statically bound to the predefined
+ * dart function <i>identical()</i> discussed above.
+ * * An expression of one of the forms <i>e<sub>1</sub> == e<sub>2</sub></i> or
+ * <i>e<sub>1</sub> != e<sub>2</sub></i> where <i>e<sub>1</sub></i> and
+ * <i>e<sub>2</sub></i> are constant expressions that evaluate to a numeric,
+ * string or boolean value.
+ * * An expression of one of the forms <i>!e</i>, <i>e<sub>1</sub> &amp;&amp;
+ * e<sub>2</sub></i> or <i>e<sub>1</sub> || e<sub>2</sub></i>, where <i>e</i>,
+ * <i>e1</sub></i> and <i>e2</sub></i> are constant expressions that evaluate
+ * to a boolean value.
+ * * An expression of one of the forms <i>~e</i>, <i>e<sub>1</sub> ^
+ * e<sub>2</sub></i>, <i>e<sub>1</sub> &amp; e<sub>2</sub></i>,
+ * <i>e<sub>1</sub> | e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;&gt;
+ * e<sub>2</sub></i> or <i>e<sub>1</sub> &lt;&lt; e<sub>2</sub></i>, where
+ * <i>e</i>, <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
+ * expressions that evaluate to an integer value or to <b>null</b>.
+ * * An expression of one of the forms <i>-e</i>, <i>e<sub>1</sub> +
+ * e<sub>2</sub></i>, <i>e<sub>1</sub> -e<sub>2</sub></i>, <i>e<sub>1</sub> *
+ * e<sub>2</sub></i>, <i>e<sub>1</sub> / e<sub>2</sub></i>, <i>e<sub>1</sub>
+ * ~/ e<sub>2</sub></i>, <i>e<sub>1</sub> &gt; e<sub>2</sub></i>,
+ * <i>e<sub>1</sub> &lt; e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;=
+ * e<sub>2</sub></i>, <i>e<sub>1</sub> &lt;= e<sub>2</sub></i> or
+ * <i>e<sub>1</sub> % e<sub>2</sub></i>, where <i>e</i>, <i>e<sub>1</sub></i>
+ * and <i>e<sub>2</sub></i> are constant expressions that evaluate to a
+ * numeric value or to <b>null</b>.
+ * * An expression of the form <i>e<sub>1</sub> ? e<sub>2</sub> :
+ * e<sub>3</sub></i> where <i>e<sub>1</sub></i>, <i>e<sub>2</sub></i> and
+ * <i>e<sub>3</sub></i> are constant expressions, and <i>e<sub>1</sub></i>
+ * evaluates to a boolean value.
+ * </blockquote>
+ */
+class ConstantEvaluator {
+ /**
+ * The source containing the expression(s) that will be evaluated.
+ */
+ final Source _source;
+
+ /**
+ * The type provider used to access the known types.
+ */
+ final TypeProvider _typeProvider;
+
+ /**
+ * Initialize a newly created evaluator to evaluate expressions in the given
+ * [source]. The [typeProvider] is the type provider used to access known
+ * types.
+ */
+ ConstantEvaluator(this._source, this._typeProvider);
+
+ EvaluationResult evaluate(Expression expression) {
+ RecordingErrorListener errorListener = new RecordingErrorListener();
+ ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
+ DartObjectImpl result = expression.accept(new ConstantVisitor(
+ new ConstantEvaluationEngine(_typeProvider, new DeclaredVariables()),
+ errorReporter));
+ if (result != null) {
+ return EvaluationResult.forValue(result);
}
+ return EvaluationResult.forErrors(errorListener.errors);
}
+}
+
+/**
+ * A visitor used to traverse the AST structures of all of the compilation units
+ * being resolved and build tables of the constant variables, constant
+ * constructors, constant constructor invocations, and annotations found in
+ * those compilation units.
+ */
+class ConstantFinder extends RecursiveAstVisitor<Object> {
+ /**
+ * A table mapping constant variable elements to the declarations of those
+ * variables.
+ */
+ final HashMap<PotentiallyConstVariableElement, VariableDeclaration> variableMap =
+ new HashMap<PotentiallyConstVariableElement, VariableDeclaration>();
/**
- * Evaluate a call to fromEnvironment() on the bool, int, or String class. The
- * [environmentValue] is the value fetched from the environment. The
- * [builtInDefaultValue] is the value that should be used as the default if no
- * "defaultValue" argument appears in [namedArgumentValues]. The
- * [namedArgumentValues] are the values of the named parameters passed to
- * fromEnvironment(). Return a [DartObjectImpl] object corresponding to the
- * evaluated result.
+ * A table mapping constant constructors to the declarations of those
+ * constructors.
*/
- DartObjectImpl _computeValueFromEnvironment(DartObject environmentValue,
- DartObjectImpl builtInDefaultValue,
- HashMap<String, DartObjectImpl> namedArgumentValues) {
- DartObjectImpl value = environmentValue as DartObjectImpl;
- if (value.isUnknown || value.isNull) {
- // The name either doesn't exist in the environment or we couldn't parse
- // the corresponding value.
- // If the code supplied an explicit default, use it.
- if (namedArgumentValues.containsKey(_DEFAULT_VALUE_PARAM)) {
- value = namedArgumentValues[_DEFAULT_VALUE_PARAM];
- } else if (value.isNull) {
- // The code didn't supply an explicit default.
- // The name exists in the environment but we couldn't parse the
- // corresponding value.
- // So use the built-in default value, because this is what the VM does.
- value = builtInDefaultValue;
- } else {
- // The code didn't supply an explicit default.
- // The name doesn't exist in the environment.
- // The VM would use the built-in default value, but we don't want to do
- // that for analysis because it's likely to lead to cascading errors.
- // So just leave [value] in the unknown state.
- }
- }
- return value;
+ final HashMap<ConstructorElement, ConstructorDeclaration> constructorMap =
+ new HashMap<ConstructorElement, ConstructorDeclaration>();
+
+ /**
+ * A collection of constant constructor invocations.
+ */
+ final List<InstanceCreationExpression> constructorInvocations =
+ new List<InstanceCreationExpression>();
+
+ /**
+ * A collection of annotations.
+ */
+ final List<Annotation> annotations = <Annotation>[];
+
+ /**
+ * True if instance variables marked as "final" should be treated as "const".
+ */
+ bool treatFinalInstanceVarAsConst = false;
+
+ @override
+ Object visitAnnotation(Annotation node) {
+ super.visitAnnotation(node);
+ annotations.add(node);
+ return null;
}
- DartObjectImpl _evaluateConstructorCall(AstNode node,
- NodeList<Expression> arguments, ConstructorElement constructor,
- ConstantVisitor constantVisitor, ErrorReporter errorReporter) {
- if (!_getConstructorBase(constructor).isCycleFree) {
- // It's not safe to evaluate this constructor, so bail out.
- // TODO(paulberry): ensure that a reasonable error message is produced
- // in this case, as well as other cases involving constant expression
- // circularities (e.g. "compile-time constant expression depends on
- // itself")
- return new DartObjectImpl.validWithUnknownValue(constructor.returnType);
- }
- int argumentCount = arguments.length;
- List<DartObjectImpl> argumentValues =
- new List<DartObjectImpl>(argumentCount);
- List<Expression> argumentNodes = new List<Expression>(argumentCount);
- HashMap<String, DartObjectImpl> namedArgumentValues =
- new HashMap<String, DartObjectImpl>();
- HashMap<String, NamedExpression> namedArgumentNodes =
- new HashMap<String, NamedExpression>();
- for (int i = 0; i < argumentCount; i++) {
- Expression argument = arguments[i];
- if (argument is NamedExpression) {
- String name = argument.name.label.name;
- namedArgumentValues[name] =
- constantVisitor._valueOf(argument.expression);
- namedArgumentNodes[name] = argument;
- argumentValues[i] = typeProvider.nullObject;
- } else {
- argumentValues[i] = constantVisitor._valueOf(argument);
- argumentNodes[i] = 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.
- if (constructor.name == "fromEnvironment") {
- if (!_checkFromEnvironmentArguments(
- arguments, argumentValues, namedArgumentValues, definingClass)) {
- errorReporter.reportErrorForNode(
- CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
- return null;
- }
- String variableName =
- argumentCount < 1 ? null : argumentValues[0].stringValue;
- if (identical(definingClass, typeProvider.boolType)) {
- DartObject valueFromEnvironment;
- valueFromEnvironment =
- _declaredVariables.getBool(typeProvider, variableName);
- return _computeValueFromEnvironment(valueFromEnvironment,
- new DartObjectImpl(typeProvider.boolType, BoolState.FALSE_STATE),
- namedArgumentValues);
- } else if (identical(definingClass, typeProvider.intType)) {
- DartObject valueFromEnvironment;
- valueFromEnvironment =
- _declaredVariables.getInt(typeProvider, variableName);
- return _computeValueFromEnvironment(valueFromEnvironment,
- new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE),
- namedArgumentValues);
- } else if (identical(definingClass, typeProvider.stringType)) {
- DartObject valueFromEnvironment;
- valueFromEnvironment =
- _declaredVariables.getString(typeProvider, variableName);
- return _computeValueFromEnvironment(valueFromEnvironment,
- new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE),
- namedArgumentValues);
- }
- } else if (constructor.name == "" &&
- identical(definingClass, typeProvider.symbolType) &&
- argumentCount == 1) {
- if (!_checkSymbolArguments(
- arguments, argumentValues, namedArgumentValues)) {
- errorReporter.reportErrorForNode(
- CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
- return null;
- }
- String argumentValue = argumentValues[0].stringValue;
- return new DartObjectImpl(
- 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 new DartObjectImpl.validWithUnknownValue(definingClass);
+ @override
+ Object visitClassDeclaration(ClassDeclaration node) {
+ bool prevTreatFinalInstanceVarAsConst = treatFinalInstanceVarAsConst;
+ if (node.element.constructors.any((ConstructorElement e) => e.isConst)) {
+ // Instance vars marked "final" need to be included in the dependency
+ // graph, since constant constructors implicitly use the values in their
+ // initializers.
+ treatFinalInstanceVarAsConst = true;
}
- validator.beforeGetConstantInitializers(constructor);
- ConstructorElementImpl constructorBase = _getConstructorBase(constructor);
- 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 new DartObjectImpl.validWithUnknownValue(definingClass);
+ try {
+ return super.visitClassDeclaration(node);
+ } finally {
+ treatFinalInstanceVarAsConst = prevTreatFinalInstanceVarAsConst;
}
- HashMap<String, DartObjectImpl> fieldMap =
- new HashMap<String, DartObjectImpl>();
- // Start with final fields that are initialized at their declaration site.
- for (FieldElement field in constructor.enclosingElement.fields) {
- if ((field.isFinal || field.isConst) &&
- !field.isStatic &&
- field is ConstFieldElementImpl) {
- validator.beforeGetFieldEvaluationResult(field);
- EvaluationResultImpl evaluationResult = field.evaluationResult;
- DartType fieldType =
- FieldMember.from(field, constructor.returnType).type;
- DartObjectImpl fieldValue = evaluationResult.value;
- if (fieldValue != null && !_runtimeTypeMatch(fieldValue, fieldType)) {
- errorReporter.reportErrorForNode(
- CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH,
- node, [fieldValue.type, field.name, fieldType]);
- }
- fieldMap[field.name] = evaluationResult.value;
+ }
+
+ @override
+ Object visitConstructorDeclaration(ConstructorDeclaration node) {
+ super.visitConstructorDeclaration(node);
+ if (node.constKeyword != null) {
+ ConstructorElement element = node.element;
+ if (element != null) {
+ constructorMap[element] = node;
}
}
- // Now evaluate the constructor declaration.
- HashMap<String, DartObjectImpl> parameterMap =
- new HashMap<String, DartObjectImpl>();
- List<ParameterElement> parameters = constructor.parameters;
- int parameterCount = parameters.length;
- for (int i = 0; i < parameterCount; i++) {
- ParameterElement parameter = parameters[i];
- ParameterElement baseParameter = parameter;
- while (baseParameter is ParameterMember) {
- baseParameter = (baseParameter as ParameterMember).baseElement;
- }
- DartObjectImpl argumentValue = null;
- AstNode errorTarget = null;
- if (baseParameter.parameterKind == ParameterKind.NAMED) {
- argumentValue = namedArgumentValues[baseParameter.name];
- errorTarget = namedArgumentNodes[baseParameter.name];
- } else if (i < argumentCount) {
- argumentValue = argumentValues[i];
- errorTarget = argumentNodes[i];
+ 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);
+ Expression initializer = node.initializer;
+ VariableElement element = node.element;
+ if (initializer != null &&
+ (node.isConst ||
+ treatFinalInstanceVarAsConst &&
+ element is FieldElement &&
+ node.isFinal &&
+ !element.isStatic)) {
+ if (node.element != null) {
+ variableMap[node.element as PotentiallyConstVariableElement] = node;
}
- if (errorTarget == null) {
- // No argument node that we can direct error messages to, because we
- // are handling an optional parameter that wasn't specified. So just
- // direct error messages to the constructor call.
- errorTarget = node;
+ }
+ return null;
+ }
+}
+
+/**
+ * An object used to 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 {
+ /**
+ * Source of RegExp matching declarable operator names.
+ * From sdk/lib/internal/symbol.dart.
+ */
+ static String _OPERATOR_RE =
+ "(?:[\\-+*/%&|^]|\\[\\]=?|==|~/?|<[<=]?|>[>=]?|unary-)";
+
+ /**
+ * Source of RegExp matching Dart reserved words.
+ * From sdk/lib/internal/symbol.dart.
+ */
+ static String _RESERVED_WORD_RE =
+ "(?:assert|break|c(?:a(?:se|tch)|lass|on(?:st|tinue))|d(?:efault|o)|e(?:lse|num|xtends)|f(?:alse|inal(?:ly)?|or)|i[fns]|n(?:ew|ull)|ret(?:hrow|urn)|s(?:uper|witch)|t(?:h(?:is|row)|r(?:ue|y))|v(?:ar|oid)|w(?:hile|ith))";
+
+ /**
+ * The object used to find constant variables and constant constructor
+ * invocations in the compilation units that were added.
+ */
+ ConstantFinder _constantFinder = new ConstantFinder();
+
+ /**
+ * A graph in which the nodes are the constants, and the edges are from each
+ * constant to the other constants that are referenced by it.
+ */
+ DirectedGraph<AstNode> referenceGraph = new DirectedGraph<AstNode>();
+
+ /**
+ * A table mapping constant variables to the declarations of those variables.
+ */
+ HashMap<PotentiallyConstVariableElement, VariableDeclaration> _variableDeclarationMap;
+
+ /**
+ * A table mapping constant constructors to the declarations of those
+ * constructors.
+ */
+ HashMap<ConstructorElement, ConstructorDeclaration> constructorDeclarationMap;
+
+ /**
+ * A collection of constant constructor invocations.
+ */
+ List<InstanceCreationExpression> _constructorInvocations;
+
+ /**
+ * A collection of annotations.
+ */
+ List<Annotation> _annotations;
+
+ /**
+ * The evaluation engine that does the work of evaluating instance creation
+ * expressions.
+ */
+ final ConstantEvaluationEngine evaluationEngine;
+
+ /**
+ * Initialize a newly created constant value computer. The [typeProvider] is
+ * the type provider used to access known types. The [declaredVariables] is
+ * the set of variables declared on the command line using '-D'.
+ */
+ ConstantValueComputer(
+ TypeProvider typeProvider, DeclaredVariables declaredVariables,
+ [ConstantEvaluationValidator validator])
+ : evaluationEngine = new ConstantEvaluationEngine(
+ typeProvider, declaredVariables, validator: validator);
+
+ /**
+ * Add the constants in the given compilation [unit] to the list of constants
+ * whose value needs to be computed.
+ */
+ void add(CompilationUnit unit) {
+ unit.accept(_constantFinder);
+ }
+
+ /**
+ * Compute values for all of the constants in the compilation units that were
+ * added.
+ */
+ void computeValues() {
+ _variableDeclarationMap = _constantFinder.variableMap;
+ constructorDeclarationMap = _constantFinder.constructorMap;
+ _constructorInvocations = _constantFinder.constructorInvocations;
+ _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 =
+ evaluationEngine._getConstructorBase(redirectedConstructor);
+ ConstructorDeclaration redirectedConstructorDeclaration =
+ findConstructorDeclaration(redirectedConstructorBase);
+ referenceGraph.addEdge(declaration, redirectedConstructorDeclaration);
+ return;
}
- if (argumentValue == null && baseParameter is ParameterElementImpl) {
- // The parameter is an optional positional parameter for which no value
- // was provided, so use the default value.
- validator.beforeGetParameterDefault(baseParameter);
- EvaluationResultImpl evaluationResult = baseParameter.evaluationResult;
- if (evaluationResult == null) {
- // No default was provided, so the default value is null.
- argumentValue = typeProvider.nullObject;
- } else if (evaluationResult.value != null) {
- argumentValue = evaluationResult.value;
+ 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 (argumentValue != null) {
- if (!_runtimeTypeMatch(argumentValue, parameter.type)) {
- errorReporter.reportErrorForNode(
- CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
- errorTarget, [argumentValue.type, parameter.type]);
- }
- if (baseParameter.isInitializingFormal) {
- FieldElement field = (parameter as FieldFormalParameterElement).field;
- if (field != null) {
- DartType fieldType = field.type;
- if (fieldType != parameter.type) {
- // We've already checked that the argument can be assigned to the
- // parameter; we also need to check that it can be assigned to
- // the field.
- if (!_runtimeTypeMatch(argumentValue, fieldType)) {
- errorReporter.reportErrorForNode(
- CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
- errorTarget, [argumentValue.type, fieldType]);
- }
- }
- String fieldName = field.name;
- fieldMap[fieldName] = argumentValue;
+ 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);
}
- } else {
- String name = baseParameter.name;
- parameterMap[name] = argumentValue;
}
}
- }
- ConstantVisitor initializerVisitor = new ConstantVisitor(
- typeProvider, errorReporter,
- validator: validator, lexicalEnvironment: parameterMap);
- String superName = null;
- NodeList<Expression> superArguments = null;
- for (ConstructorInitializer initializer in initializers) {
- if (initializer is ConstructorFieldInitializer) {
- ConstructorFieldInitializer constructorFieldInitializer = initializer;
- Expression initializerExpression =
- constructorFieldInitializer.expression;
- DartObjectImpl evaluationResult =
- initializerExpression.accept(initializerVisitor);
- if (evaluationResult != null) {
- String fieldName = constructorFieldInitializer.fieldName.name;
- fieldMap[fieldName] = evaluationResult;
- PropertyAccessorElement getter = definingClass.getGetter(fieldName);
- if (getter != null) {
- PropertyInducingElement field = getter.variable;
- if (!_runtimeTypeMatch(evaluationResult, field.type)) {
- errorReporter.reportErrorForNode(
- CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH,
- node, [evaluationResult.type, fieldName, field.type]);
- }
+ 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);
}
}
- } else if (initializer is SuperConstructorInvocation) {
- SuperConstructorInvocation superConstructorInvocation = initializer;
- SimpleIdentifier name = superConstructorInvocation.constructorName;
- if (name != null) {
- superName = name.name;
- }
- superArguments = superConstructorInvocation.argumentList.arguments;
- } else if (initializer is RedirectingConstructorInvocation) {
- // This is a redirecting constructor, so just evaluate the constructor
- // it redirects to.
- ConstructorElement constructor = initializer.staticElement;
- if (constructor != null && constructor.isConst) {
- return _evaluateConstructorCall(node,
- initializer.argumentList.arguments, constructor,
- initializerVisitor, errorReporter);
+ }
+ 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) {
+ continue;
+ }
+ 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);
}
- // 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);
+ 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);
}
- _evaluateSuperConstructorCall(node, fieldMap, superConstructor,
- superArguments, initializerVisitor, errorReporter);
}
}
- return new DartObjectImpl(definingClass, new GenericState(fieldMap));
- }
-
- void _evaluateSuperConstructorCall(AstNode node,
- HashMap<String, DartObjectImpl> fieldMap,
- ConstructorElement superConstructor, NodeList<Expression> superArguments,
- ConstantVisitor initializerVisitor, ErrorReporter errorReporter) {
- if (superConstructor != null && superConstructor.isConst) {
- DartObjectImpl evaluationResult = _evaluateConstructorCall(node,
- superArguments, superConstructor, initializerVisitor, errorReporter);
- if (evaluationResult != null) {
- fieldMap[GenericState.SUPERCLASS_FIELD] = evaluationResult;
- }
+ // Since no constant can depend on an annotation, we don't waste time
+ // including them in the topological sort. We just process all the
+ // annotations after all other constants are finished.
+ for (Annotation annotation in _annotations) {
+ _computeValueFor(annotation);
}
}
+ ConstructorDeclaration findConstructorDeclaration(
+ ConstructorElement constructor) => constructorDeclarationMap[
+ evaluationEngine._getConstructorBase(constructor)];
+
+ VariableDeclaration findVariableDeclaration(
+ PotentiallyConstVariableElement variable) =>
+ _variableDeclarationMap[variable];
+
/**
- * 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.
+ * Compute a value for the given [constNode].
*/
- ConstructorElement _followConstantRedirectionChain(
- ConstructorElement constructor) {
- HashSet<ConstructorElement> constructorsVisited =
- new HashSet<ConstructorElement>();
- while (true) {
- ConstructorElement redirectedConstructor =
- _getConstRedirectedConstructor(constructor);
- if (redirectedConstructor == null) {
- break;
- } else {
- ConstructorElement constructorBase = _getConstructorBase(constructor);
- constructorsVisited.add(constructorBase);
- ConstructorElement redirectedConstructorBase =
- _getConstructorBase(redirectedConstructor);
- if (constructorsVisited.contains(redirectedConstructorBase)) {
- // Cycle in redirecting factory constructors--this is not allowed
- // and is checked elsewhere--see
- // [ErrorVerifier.checkForRecursiveFactoryRedirect()]).
- break;
+ 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));
+ if (dartObject != null) {
+ if (!evaluationEngine.runtimeTypeMatch(dartObject, element.type)) {
+ errorReporter.reportErrorForElement(
+ CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, element, [
+ dartObject.type,
+ element.type
+ ]);
}
}
- constructor = redirectedConstructor;
+ (element as VariableElementImpl).evaluationResult =
+ new EvaluationResultImpl.con2(dartObject, errorListener.errors);
+ } 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.
+ // But we still need to store an evaluation result.
+ expression.constantHandle.evaluationResult =
+ new EvaluationResultImpl.con1(null);
+ return;
+ }
+ RecordingErrorListener errorListener = new RecordingErrorListener();
+ CompilationUnit sourceCompilationUnit =
+ expression.getAncestor((node) => node is CompilationUnit);
+ ErrorReporter errorReporter = new ErrorReporter(
+ errorListener, sourceCompilationUnit.element.source);
+ ConstantVisitor constantVisitor =
+ new ConstantVisitor(evaluationEngine, errorReporter);
+ DartObjectImpl result = evaluationEngine.evaluateConstructorCall(
+ constNode, expression.argumentList.arguments, constructor,
+ constantVisitor, errorReporter);
+ expression.constantHandle.evaluationResult =
+ new EvaluationResultImpl.con2(result, 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) {
+ DefaultFormalParameter parameter = constNode;
+ ParameterElement element = parameter.element;
+ Expression defaultValue = parameter.defaultValue;
+ if (defaultValue != null) {
+ RecordingErrorListener errorListener = new RecordingErrorListener();
+ ErrorReporter errorReporter =
+ new ErrorReporter(errorListener, element.source);
+ DartObjectImpl dartObject = defaultValue
+ .accept(new ConstantVisitor(evaluationEngine, errorReporter));
+ (element as ParameterElementImpl).evaluationResult =
+ 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 {
+ // 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;
}
- return constructor;
}
/**
@@ -1184,67 +1269,6 @@ class ConstantValueComputer {
void _generateCycleError(List<AstNode> cycle, AstNode constant) {
// TODO(brianwilkerson) Implement this.
}
-
- /**
- * If [constructor] redirects to another const constructor, return the
- * const constructor it redirects to. Otherwise return `null`.
- */
- ConstructorElement _getConstRedirectedConstructor(
- ConstructorElement constructor) {
- if (!constructor.isFactory) {
- return null;
- }
- 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.
- return null;
- }
- ConstructorElement redirectedConstructor =
- constructor.redirectedConstructor;
- if (redirectedConstructor == null) {
- // This can happen if constructor is an external factory constructor.
- return null;
- }
- if (!redirectedConstructor.isConst) {
- // Delegating to a non-const constructor--this is not allowed (and
- // is checked elsewhere--see
- // [ErrorVerifier.checkForRedirectToNonConstConstructor()]).
- return null;
- }
- return redirectedConstructor;
- }
-
- ConstructorElementImpl _getConstructorBase(ConstructorElement constructor) {
- while (constructor is ConstructorMember) {
- constructor = (constructor as ConstructorMember).baseElement;
- }
- return constructor;
- }
-
- /**
- * Check if the object [obj] matches the type [type] according to runtime type
- * checking rules.
- */
- bool _runtimeTypeMatch(DartObjectImpl obj, DartType type) {
- if (obj.isNull) {
- return true;
- }
- if (type.isUndefined) {
- return false;
- }
- return obj.type.isSubtypeOf(type);
- }
-
- /**
- * Determine whether the given string is a valid name for a public symbol
- * (i.e. whether it is allowed for a call to the Symbol constructor).
- */
- static bool isValidPublicSymbol(String name) => name.isEmpty ||
- name == "void" ||
- new JavaPatternMatcher(_PUBLIC_SYMBOL_PATTERN, name).matches();
}
/**
@@ -1306,17 +1330,11 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
/**
* The type provider used to access the known types.
*/
- final TypeProvider _typeProvider;
+ final ConstantEvaluationEngine evaluationEngine;
final HashMap<String, DartObjectImpl> _lexicalEnvironment;
/**
- * Validator used to verify correct dependency analysis when running unit
- * tests.
- */
- final ConstantEvaluationValidator validator;
-
- /**
* Error reporter that we use to report errors accumulated while computing the
* constant.
*/
@@ -1328,24 +1346,26 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
DartObjectComputer _dartObjectComputer;
/**
- * Initialize a newly created constant visitor. The [_typeProvider] is the
- * type provider used to access known types. The [lexicalEnvironment] is a
- * map containing values which should override identifiers, or `null` if no
- * overriding is necessary. The [_errorReporter] is used to report errors
+ * Initialize a newly created constant visitor. The [evaluationEngine] is
+ * used to evaluate instance creation expressions. The [lexicalEnvironment]
+ * is a map containing values which should override identifiers, or `null` if
+ * no overriding is necessary. The [_errorReporter] is used to report errors
* found during evaluation. The [validator] is used by unit tests to verify
* correct dependency analysis.
*/
- ConstantVisitor(this._typeProvider, this._errorReporter,
- {ConstantEvaluationValidator validator,
- HashMap<String, DartObjectImpl> lexicalEnvironment})
- : validator = validator != null
- ? validator
- : new ConstantEvaluationValidator_ForProduction(),
- _lexicalEnvironment = lexicalEnvironment {
+ ConstantVisitor(this.evaluationEngine, this._errorReporter,
+ {HashMap<String, DartObjectImpl> lexicalEnvironment})
+ : _lexicalEnvironment = lexicalEnvironment {
this._dartObjectComputer =
- new DartObjectComputer(_errorReporter, _typeProvider);
+ new DartObjectComputer(_errorReporter, evaluationEngine.typeProvider);
}
+ /**
+ * Convenience getter to gain access to the [evalationEngine]'s type
+ * provider.
+ */
+ TypeProvider get _typeProvider => evaluationEngine.typeProvider;
+
@override
DartObjectImpl visitAdjacentStrings(AdjacentStrings node) {
DartObjectImpl result = null;
@@ -1473,7 +1493,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
_error(node, null);
return null;
}
- validator.beforeGetEvaluationResult(node);
+ evaluationEngine.validator.beforeGetEvaluationResult(node);
EvaluationResultImpl result = node.evaluationResult;
if (result != null) {
return result.value;
@@ -1737,7 +1757,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
}
if (element is VariableElementImpl) {
VariableElementImpl variableElementImpl = element;
- validator.beforeGetEvaluationResult(node);
+ evaluationEngine.validator.beforeGetEvaluationResult(node);
EvaluationResultImpl value = variableElementImpl.evaluationResult;
if (variableElementImpl.isConst && value != null) {
return value.value;
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/generated/incremental_resolver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698