| 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> &&
|
| - * 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> & 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> 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 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> > 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> 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> &&
|
| + * 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> & 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> 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 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> > 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> 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;
|
|
|