Package com.google.dart.engine.internal.constant

Source Code of com.google.dart.engine.internal.constant.ConstantValueComputer

/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.internal.constant;

import com.google.dart.engine.AnalysisEngine;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.ConstructorDeclaration;
import com.google.dart.engine.ast.ConstructorFieldInitializer;
import com.google.dart.engine.ast.ConstructorInitializer;
import com.google.dart.engine.ast.DefaultFormalParameter;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.FormalParameter;
import com.google.dart.engine.ast.InstanceCreationExpression;
import com.google.dart.engine.ast.NamedExpression;
import com.google.dart.engine.ast.NodeList;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.SuperConstructorInvocation;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.constant.DartObject;
import com.google.dart.engine.constant.DeclaredVariables;
import com.google.dart.engine.element.ConstructorElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.FieldElement;
import com.google.dart.engine.element.FieldFormalParameterElement;
import com.google.dart.engine.element.ParameterElement;
import com.google.dart.engine.element.VariableElement;
import com.google.dart.engine.error.CompileTimeErrorCode;
import com.google.dart.engine.internal.element.ConstructorElementImpl;
import com.google.dart.engine.internal.element.ParameterElementImpl;
import com.google.dart.engine.internal.element.VariableElementImpl;
import com.google.dart.engine.internal.element.member.ConstructorMember;
import com.google.dart.engine.internal.element.member.ParameterMember;
import com.google.dart.engine.internal.object.BoolState;
import com.google.dart.engine.internal.object.DartObjectImpl;
import com.google.dart.engine.internal.object.GenericState;
import com.google.dart.engine.internal.object.NullState;
import com.google.dart.engine.internal.object.SymbolState;
import com.google.dart.engine.internal.resolver.TypeProvider;
import com.google.dart.engine.type.InterfaceType;
import com.google.dart.engine.utilities.ast.AstCloner;
import com.google.dart.engine.utilities.collection.DirectedGraph;
import com.google.dart.engine.utilities.dart.ParameterKind;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

/**
* Instances of the class {@code ConstantValueComputer} compute the values of constant variables and
* constant constructor invocations in one or more compilation units. The expected usage pattern is
* for the compilation units to be added to this computer using the method
* {@link #add(CompilationUnit)} and then for the method {@link #computeValues()} to be invoked
* exactly once. Any use of an instance after invoking the method {@link #computeValues()} will
* result in unpredictable behavior.
*/
public class ConstantValueComputer {
  /**
   * [AstCloner] that copies the necessary information from the AST to allow const constructor
   * initializers to be evaluated.
   */
  private class InitializerCloner extends AstCloner {

    @Override
    public InstanceCreationExpression visitInstanceCreationExpression(
        InstanceCreationExpression node) {
      // All we need is the evaluation result, and the keyword so that we know whether it's const.
      InstanceCreationExpression expression = new InstanceCreationExpression(
          node.getKeyword(),
          null,
          null);
      expression.setEvaluationResult(node.getEvaluationResult());
      return expression;
    }

    @Override
    public SimpleIdentifier visitSimpleIdentifier(SimpleIdentifier node) {
      SimpleIdentifier identifier = super.visitSimpleIdentifier(node);
      identifier.setStaticElement(node.getStaticElement());
      return identifier;
    }

    @Override
    public SuperConstructorInvocation visitSuperConstructorInvocation(
        SuperConstructorInvocation node) {
      SuperConstructorInvocation invocation = super.visitSuperConstructorInvocation(node);
      invocation.setStaticElement(node.getStaticElement());
      return invocation;
    }
  }

  /**
   * Parameter to "fromEnvironment" methods that denotes the default value.
   */
  static private final String DEFAULT_VALUE_PARAM = "defaultValue";

  /**
   * Source of RegExp matching declarable operator names. From sdk/lib/internal/symbol.dart.
   */
  static private final String OPERATOR_RE = "(?:[\\-+*/%&|^]|\\[\\]=?|==|~/?|<[<=]?|>[>=]?|unary-)";

  /**
   * Source of RegExp matching any public identifier. From sdk/lib/internal/symbol.dart.
   */
  static private final 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 private final 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 private final Pattern PUBLIC_SYMBOL_PATTERN = Pattern.compile("^(?:"
      + ConstantValueComputer.OPERATOR_RE + "$|" + PUBLIC_IDENTIFIER_RE + "(?:=?$|[.](?!$)))+?$");

  /**
   * 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 public boolean isValidPublicSymbol(String name) {
    return name.isEmpty() || name.equals("void") || PUBLIC_SYMBOL_PATTERN.matcher(name).matches();
  }

  /**
   * The type provider used to access the known types.
   */
  protected TypeProvider typeProvider;

  /**
   * The object used to find constant variables and constant constructor invocations in the
   * compilation units that were added.
   */
  private 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.
   */
  protected DirectedGraph<AstNode> referenceGraph = new DirectedGraph<AstNode>();

  /**
   * A table mapping constant variables to the declarations of those variables.
   */
  private HashMap<VariableElement, VariableDeclaration> variableDeclarationMap;

  /**
   * A table mapping constant constructors to the declarations of those constructors.
   */
  protected HashMap<ConstructorElement, ConstructorDeclaration> constructorDeclarationMap;

  /**
   * A collection of constant constructor invocations.
   */
  private ArrayList<InstanceCreationExpression> constructorInvocations;

  /**
   * The set of variables declared on the command line using '-D'.
   */
  private final DeclaredVariables declaredVariables;

  /**
   * Initialize a newly created constant value computer.
   *
   * @param typeProvider the type provider used to access known types
   * @param declaredVariables the set of variables declared on the command line using '-D'
   */
  public ConstantValueComputer(TypeProvider typeProvider, DeclaredVariables declaredVariables) {
    this.typeProvider = typeProvider;
    this.declaredVariables = declaredVariables;
  }

  /**
   * Add the constants in the given compilation unit to the list of constants whose value needs to
   * be computed.
   *
   * @param unit the compilation unit defining the constants to be added
   */
  public void add(CompilationUnit unit) {
    unit.accept(constantFinder);
  }

  /**
   * Compute values for all of the constants in the compilation units that were added.
   */
  public void computeValues() {
    variableDeclarationMap = constantFinder.getVariableMap();
    constructorDeclarationMap = constantFinder.getConstructorMap();
    constructorInvocations = constantFinder.getConstructorInvocations();
    for (Map.Entry<VariableElement, VariableDeclaration> entry : variableDeclarationMap.entrySet()) {
      VariableDeclaration declaration = entry.getValue();
      ReferenceFinder referenceFinder = new ReferenceFinder(
          declaration,
          referenceGraph,
          variableDeclarationMap,
          constructorDeclarationMap);
      referenceGraph.addNode(declaration);
      declaration.getInitializer().accept(referenceFinder);
    }
    for (Entry<ConstructorElement, ConstructorDeclaration> entry : constructorDeclarationMap.entrySet()) {
      ConstructorDeclaration declaration = entry.getValue();
      ReferenceFinder referenceFinder = new ReferenceFinder(
          declaration,
          referenceGraph,
          variableDeclarationMap,
          constructorDeclarationMap);
      referenceGraph.addNode(declaration);
      boolean superInvocationFound = false;
      NodeList<ConstructorInitializer> initializers = declaration.getInitializers();
      for (ConstructorInitializer initializer : initializers) {
        if (initializer instanceof SuperConstructorInvocation) {
          superInvocationFound = true;
        }
        initializer.accept(referenceFinder);
      }
      if (!superInvocationFound) {
        // No explicit superconstructor invocation found, so we need to manually insert
        // a reference to the implicit superconstructor.
        InterfaceType superclass = ((InterfaceType) entry.getKey().getReturnType()).getSuperclass();
        if (superclass != null && !superclass.isObject()) {
          ConstructorElement unnamedConstructor = superclass.getElement().getUnnamedConstructor();
          ConstructorDeclaration superConstructorDeclaration = findConstructorDeclaration(unnamedConstructor);
          if (superConstructorDeclaration != null) {
            referenceGraph.addEdge(declaration, superConstructorDeclaration);
          }
        }
      }
      for (FormalParameter parameter : declaration.getParameters().getParameters()) {
        referenceGraph.addNode(parameter);
        referenceGraph.addEdge(declaration, parameter);
        if (parameter instanceof DefaultFormalParameter) {
          Expression defaultValue = ((DefaultFormalParameter) parameter).getDefaultValue();
          if (defaultValue != null) {
            ReferenceFinder parameterReferenceFinder = new ReferenceFinder(
                parameter,
                referenceGraph,
                variableDeclarationMap,
                constructorDeclarationMap);
            defaultValue.accept(parameterReferenceFinder);
          }
        }
      }
    }
    for (InstanceCreationExpression expression : constructorInvocations) {
      referenceGraph.addNode(expression);
      ConstructorElement constructor = expression.getStaticElement();
      if (constructor == null) {
        break;
      }
      constructor = followConstantRedirectionChain(constructor);
      ConstructorDeclaration declaration = findConstructorDeclaration(constructor);
      // An instance creation expression depends both on the constructor and the arguments passed
      // to it.
      ReferenceFinder referenceFinder = new ReferenceFinder(
          expression,
          referenceGraph,
          variableDeclarationMap,
          constructorDeclarationMap);
      if (declaration != null) {
        referenceGraph.addEdge(expression, declaration);
      }
      expression.getArgumentList().accept(referenceFinder);
    }
    ArrayList<ArrayList<AstNode>> topologicalSort = referenceGraph.computeTopologicalSort();
    for (ArrayList<AstNode> constantsInCycle : topologicalSort) {
      if (constantsInCycle.size() == 1) {
        computeValueFor(constantsInCycle.get(0));
      } else {
        for (AstNode constant : constantsInCycle) {
          generateCycleError(constantsInCycle, constant);
        }
      }
    }
  }

  /**
   * This method is called just before computing the constant value associated with an AST node.
   * Unit tests will override this method to introduce additional error checking.
   */
  protected void beforeComputeValue(AstNode constNode) {
  }

  /**
   * This method is called just before getting the constant initializers associated with a
   * constructor AST node. Unit tests will override this method to introduce additional error
   * checking.
   */
  protected void beforeGetConstantInitializers(ConstructorElement constructor) {
  }

  /**
   * This method is called just before getting a parameter's default value. Unit tests will override
   * this method to introduce additional error checking.
   */
  protected void beforeGetParameterDefault(ParameterElement parameter) {
  }

  /**
   * Create the ConstantVisitor used to evaluate constants. Unit tests will override this method to
   * introduce additional error checking.
   */
  protected ConstantVisitor createConstantVisitor() {
    return new ConstantVisitor(typeProvider);
  }

  protected ConstructorDeclaration findConstructorDeclaration(ConstructorElement constructor) {
    return constructorDeclarationMap.get(getConstructorBase(constructor));
  }

  /**
   * Check that the arguments to a call to fromEnvironment() are correct.
   *
   * @param arguments the AST nodes of the arguments.
   * @param argumentValues the values of the unnamed arguments.
   * @param namedArgumentValues the values of the named arguments.
   * @param expectedDefaultValueType 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.
   */
  private boolean checkFromEnvironmentArguments(NodeList<Expression> arguments,
      DartObjectImpl[] argumentValues, HashMap<String, DartObjectImpl> namedArgumentValues,
      InterfaceType expectedDefaultValueType) {
    int argumentCount = arguments.size();
    if (argumentCount < 1 || argumentCount > 2) {
      return false;
    }
    if (arguments.get(0) instanceof NamedExpression) {
      return false;
    }
    if (argumentValues[0].getType() != typeProvider.getStringType()) {
      return false;
    }
    if (argumentCount == 2) {
      if (!(arguments.get(1) instanceof NamedExpression)) {
        return false;
      }
      if (!(((NamedExpression) arguments.get(1)).getName().getLabel().getName().equals(DEFAULT_VALUE_PARAM))) {
        return false;
      }
      InterfaceType defaultValueType = namedArgumentValues.get(DEFAULT_VALUE_PARAM).getType();
      if (!(defaultValueType == expectedDefaultValueType || defaultValueType == typeProvider.getNullType())) {
        return false;
      }
    }
    return true;
  }

  /**
   * Check that the arguments to a call to Symbol() are correct.
   *
   * @param arguments the AST nodes of the arguments.
   * @param argumentValues the values of the unnamed arguments.
   * @param namedArgumentValues the values of the named arguments.
   * @return true if the arguments are correct, false if there is an error.
   */
  private boolean checkSymbolArguments(NodeList<Expression> arguments,
      DartObjectImpl[] argumentValues, HashMap<String, DartObjectImpl> namedArgumentValues) {
    if (arguments.size() != 1) {
      return false;
    }
    if (arguments.get(0) instanceof NamedExpression) {
      return false;
    }
    if (argumentValues[0].getType() != typeProvider.getStringType()) {
      return false;
    }
    String name = argumentValues[0].getStringValue();
    return isValidPublicSymbol(name);
  }

  /**
   * Compute a value for the given constant.
   *
   * @param constNode the constant for which a value is to be computed
   */
  private void computeValueFor(AstNode constNode) {
    beforeComputeValue(constNode);
    if (constNode instanceof VariableDeclaration) {
      VariableDeclaration declaration = (VariableDeclaration) constNode;
      Element element = declaration.getElement();
      EvaluationResultImpl result = declaration.getInitializer().accept(createConstantVisitor());
      ((VariableElementImpl) element).setEvaluationResult(result);
    } else if (constNode instanceof InstanceCreationExpression) {
      InstanceCreationExpression expression = (InstanceCreationExpression) constNode;
      ConstructorElement constructor = expression.getStaticElement();
      if (constructor == null) {
        // Couldn't resolve the constructor so we can't compute a value.  No problem--the error
        // has already been reported.
        return;
      }
      ConstantVisitor constantVisitor = createConstantVisitor();
      EvaluationResultImpl result = evaluateConstructorCall(
          constNode,
          expression.getArgumentList().getArguments(),
          constructor,
          constantVisitor);
      expression.setEvaluationResult(result);
    } else if (constNode instanceof ConstructorDeclaration) {
      ConstructorDeclaration declaration = (ConstructorDeclaration) constNode;
      NodeList<ConstructorInitializer> initializers = declaration.getInitializers();
      ConstructorElementImpl constructor = (ConstructorElementImpl) declaration.getElement();
      constructor.setConstantInitializers(new InitializerCloner().cloneNodeList(initializers));
    } else if (constNode instanceof FormalParameter) {
      if (constNode instanceof DefaultFormalParameter) {
        DefaultFormalParameter parameter = ((DefaultFormalParameter) constNode);
        ParameterElement element = parameter.getElement();
        Expression defaultValue = parameter.getDefaultValue();
        if (defaultValue != null) {
          EvaluationResultImpl result = defaultValue.accept(createConstantVisitor());
          ((ParameterElementImpl) element).setEvaluationResult(result);
        }
      }
    } else {
      // Should not happen.
      AnalysisEngine.getInstance().getLogger().logError(
          "Constant value computer trying to compute the value of a node which is not a "
              + "VariableDeclaration, InstanceCreationExpression, FormalParameter, or "
              + "ConstructorDeclaration");
      return;
    }
  }

  /**
   * Evaluate a call to fromEnvironment() on the bool, int, or String class.
   *
   * @param environmentValue Value fetched from the environment
   * @param builtInDefaultValue Value that should be used as the default if no "defaultValue"
   *          argument appears in {@link namedArgumentValues}.
   * @param namedArgumentValues Named parameters passed to fromEnvironment()
   * @return A {@link ValidResult} object corresponding to the evaluated result
   */
  private ValidResult computeValueFromEnvironment(DartObject environmentValue,
      DartObjectImpl builtInDefaultValue, HashMap<String, DartObjectImpl> namedArgumentValues) {
    DartObjectImpl value = (DartObjectImpl) environmentValue;
    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.get(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 new ValidResult(value);
  }

  private EvaluationResultImpl evaluateConstructorCall(AstNode node,
      NodeList<Expression> arguments, ConstructorElement constructor,
      ConstantVisitor constantVisitor) {
    int argumentCount = arguments.size();
    DartObjectImpl[] argumentValues = new DartObjectImpl[argumentCount];
    HashMap<String, DartObjectImpl> namedArgumentValues = new HashMap<String, DartObjectImpl>();
    for (int i = 0; i < argumentCount; i++) {
      Expression argument = arguments.get(i);
      if (argument instanceof NamedExpression) {
        NamedExpression namedExpression = (NamedExpression) argument;
        String name = namedExpression.getName().getLabel().getName();
        namedArgumentValues.put(name, constantVisitor.valueOf(namedExpression.getExpression()));
        argumentValues[i] = constantVisitor.getNull();
      } else {
        argumentValues[i] = constantVisitor.valueOf(argument);
      }
    }
    constructor = followConstantRedirectionChain(constructor);
    InterfaceType definingClass = (InterfaceType) constructor.getReturnType();
    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.getName().equals("fromEnvironment")) {
        if (!checkFromEnvironmentArguments(
            arguments,
            argumentValues,
            namedArgumentValues,
            definingClass)) {
          return new ErrorResult(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
        }
        String variableName = argumentCount < 1 ? null : argumentValues[0].getStringValue();
        if (definingClass == typeProvider.getBoolType()) {
          DartObject valueFromEnvironment;
          valueFromEnvironment = declaredVariables.getBool(typeProvider, variableName);
          return computeValueFromEnvironment(
              valueFromEnvironment,
              new DartObjectImpl(typeProvider.getBoolType(), BoolState.FALSE_STATE),
              namedArgumentValues);
        } else if (definingClass == typeProvider.getIntType()) {
          DartObject valueFromEnvironment;
          valueFromEnvironment = declaredVariables.getInt(typeProvider, variableName);
          return computeValueFromEnvironment(
              valueFromEnvironment,
              new DartObjectImpl(typeProvider.getNullType(), NullState.NULL_STATE),
              namedArgumentValues);
        } else if (definingClass == typeProvider.getStringType()) {
          DartObject valueFromEnvironment;
          valueFromEnvironment = declaredVariables.getString(typeProvider, variableName);
          return computeValueFromEnvironment(
              valueFromEnvironment,
              new DartObjectImpl(typeProvider.getNullType(), NullState.NULL_STATE),
              namedArgumentValues);
        }
      } else if (constructor.getName().equals("") && definingClass == typeProvider.getSymbolType()
          && argumentCount == 1) {
        if (!checkSymbolArguments(arguments, argumentValues, namedArgumentValues)) {
          return new ErrorResult(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
        }
        String argumentValue = argumentValues[0].getStringValue();
        return constantVisitor.valid(definingClass, new SymbolState(argumentValue));
      }

      // Either it's an external const factory constructor that we can't emulate, or an error
      // occurred (a cycle, or a const constructor trying to delegate to a non-const constructor).
      // In the former case, the best we can do is consider it an unknown value.  In the latter
      // case, the error has already been reported, so considering it an unknown value will
      // suppress further errors.
      return constantVisitor.validWithUnknownValue(definingClass);
    }
    beforeGetConstantInitializers(constructor);
    ConstructorElementImpl constructorBase = (ConstructorElementImpl) getConstructorBase(constructor);
    List<ConstructorInitializer> initializers = constructorBase.getConstantInitializers();
    if (initializers == null) {
      // This can happen in some cases where there are compile errors in the code being analyzed
      // (for example if the code is trying to create a const instance using a non-const
      // constructor, or the node we're visiting is involved in a cycle).  The error has already
      // been reported, so consider it an unknown value to suppress further errors.
      return constantVisitor.validWithUnknownValue(definingClass);
    }
    HashMap<String, DartObjectImpl> fieldMap = new HashMap<String, DartObjectImpl>();
    HashMap<String, DartObjectImpl> parameterMap = new HashMap<String, DartObjectImpl>();
    ParameterElement[] parameters = constructorBase.getParameters();
    int parameterCount = parameters.length;
    for (int i = 0; i < parameterCount; i++) {
      ParameterElement parameter = parameters[i];
      while (parameter instanceof ParameterMember) {
        parameter = ((ParameterMember) parameter).getBaseElement();
      }
      DartObjectImpl argumentValue = null;
      if (parameter.getParameterKind() == ParameterKind.NAMED) {
        argumentValue = namedArgumentValues.get(parameter.getName());
      } else if (i < argumentCount) {
        argumentValue = argumentValues[i];
      }
      if (argumentValue == null && parameter instanceof ParameterElementImpl) {
        // The parameter is an optional positional parameter for which no value was provided, so
        // use the default value.
        beforeGetParameterDefault(parameter);
        EvaluationResultImpl evaluationResult = ((ParameterElementImpl) parameter).getEvaluationResult();
        if (evaluationResult instanceof ValidResult) {
          argumentValue = ((ValidResult) evaluationResult).getValue();
        } else if (evaluationResult == null) {
          // No default was provided, so the default value is null.
          argumentValue = constantVisitor.getNull();
        }
      }
      if (argumentValue != null) {
        if (parameter.isInitializingFormal()) {
          FieldElement field = ((FieldFormalParameterElement) parameter).getField();
          if (field != null) {
            String fieldName = field.getName();
            fieldMap.put(fieldName, argumentValue);
          }
        } else {
          String name = parameter.getName();
          parameterMap.put(name, argumentValue);
        }
      }
    }
    ConstantVisitor initializerVisitor = new ConstantVisitor(typeProvider, parameterMap);
    String superName = null;
    NodeList<Expression> superArguments = null;
    for (ConstructorInitializer initializer : initializers) {
      if (initializer instanceof ConstructorFieldInitializer) {
        ConstructorFieldInitializer constructorFieldInitializer = (ConstructorFieldInitializer) initializer;
        Expression initializerExpression = constructorFieldInitializer.getExpression();
        EvaluationResultImpl evaluationResult = initializerExpression.accept(initializerVisitor);
        if (evaluationResult instanceof ValidResult) {
          DartObjectImpl value = ((ValidResult) evaluationResult).getValue();
          String fieldName = constructorFieldInitializer.getFieldName().getName();
          fieldMap.put(fieldName, value);
        }
      } else if (initializer instanceof SuperConstructorInvocation) {
        SuperConstructorInvocation superConstructorInvocation = (SuperConstructorInvocation) initializer;
        SimpleIdentifier name = superConstructorInvocation.getConstructorName();
        if (name != null) {
          superName = name.getName();
        }
        superArguments = superConstructorInvocation.getArgumentList().getArguments();
      }
    }
    // Evaluate explicit or implicit call to super().
    InterfaceType superclass = definingClass.getSuperclass();
    if (superclass != null && !superclass.isObject()) {
      ConstructorElement superConstructor = superclass.lookUpConstructor(
          superName,
          constructor.getLibrary());
      if (superConstructor != null) {
        if (superArguments == null) {
          superArguments = new NodeList<Expression>(null);
        }
        evaluateSuperConstructorCall(
            node,
            fieldMap,
            superConstructor,
            superArguments,
            initializerVisitor);
      }
    }
    return constantVisitor.valid(definingClass, new GenericState(fieldMap));
  }

  private void evaluateSuperConstructorCall(AstNode node, HashMap<String, DartObjectImpl> fieldMap,
      ConstructorElement superConstructor, NodeList<Expression> superArguments,
      ConstantVisitor initializerVisitor) {
    if (superConstructor != null && superConstructor.isConst()) {
      EvaluationResultImpl evaluationResult = evaluateConstructorCall(
          node,
          superArguments,
          superConstructor,
          initializerVisitor);
      if (evaluationResult instanceof ValidResult) {
        ValidResult validResult = (ValidResult) evaluationResult;
        fieldMap.put(GenericState.SUPERCLASS_FIELD, validResult.getValue());
      }
    }
  }

  /**
   * 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.
   */
  private ConstructorElement followConstantRedirectionChain(ConstructorElement constructor) {
    HashSet<ConstructorElement> constructorsVisited = new HashSet<ConstructorElement>();
    while (constructor.isFactory()) {
      if (constructor.getEnclosingElement().getType() == typeProvider.getSymbolType()) {
        // The dart:core.Symbol has a const factory constructor that redirects to
        // dart:_internal.Symbol.  That in turn redirects to an external const constructor, which
        // we won't be able to evaluate.  So stop following the chain of redirections at
        // dart:core.Symbol, and let [evaluateInstanceCreationExpression] handle it specially.
        break;
      }

      constructorsVisited.add(constructor);
      ConstructorElement redirectedConstructor = constructor.getRedirectedConstructor();
      if (redirectedConstructor == null) {
        // This can happen if constructor is an external factory constructor.
        break;
      }
      if (!redirectedConstructor.isConst()) {
        // Delegating to a non-const constructor--this is not allowed (and
        // is checked elsewhere--see [ErrorVerifier.checkForRedirectToNonConstConstructor()]).
        break;
      }
      if (constructorsVisited.contains(redirectedConstructor)) {
        // Cycle in redirecting factory constructors--this is not allowed
        // and is checked elsewhere--see [ErrorVerifier.checkForRecursiveFactoryRedirect()]).
        break;
      }
      constructor = redirectedConstructor;
    }
    return constructor;
  }

  /**
   * Generate an error indicating that the given constant is not a valid compile-time constant
   * because it references at least one of the constants in the given cycle, each of which directly
   * or indirectly references the constant.
   *
   * @param constantsInCycle the constants in the cycle that includes the given constant
   * @param constant the constant that is not a valid compile-time constant
   */
  private void generateCycleError(List<AstNode> constantsInCycle, AstNode constant) {
    // TODO(brianwilkerson) Implement this.
  }

  private ConstructorElement getConstructorBase(ConstructorElement constructor) {
    while (constructor instanceof ConstructorMember) {
      constructor = ((ConstructorMember) constructor).getBaseElement();
    }
    return constructor;
  }
}
TOP

Related Classes of com.google.dart.engine.internal.constant.ConstantValueComputer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.