| Index: pkg/analyzer_experimental/lib/src/generated/constant.dart
|
| diff --git a/pkg/analyzer_experimental/lib/src/generated/constant.dart b/pkg/analyzer_experimental/lib/src/generated/constant.dart
|
| index 6dfbb47179c983c80778a73b24012cd3283705f5..4f7f0abd35f3af86bfe1bfe768d357320f404dd5 100644
|
| --- a/pkg/analyzer_experimental/lib/src/generated/constant.dart
|
| +++ b/pkg/analyzer_experimental/lib/src/generated/constant.dart
|
| @@ -9,6 +9,7 @@ import 'error.dart' show AnalysisError, ErrorCode, CompileTimeErrorCode;
|
| import 'scanner.dart' show TokenType;
|
| import 'ast.dart';
|
| import 'element.dart';
|
| +import 'engine.dart' show AnalysisEngine;
|
|
|
| /**
|
| * Instances of the class {@code ConstantEvaluator} evaluate constant expressions to produce their
|
| @@ -124,6 +125,131 @@ class EvaluationResult {
|
| bool isValid() => _errors == null;
|
| }
|
| /**
|
| + * Instances of the class {@code ConstantFinder} are used to traverse the AST structures of all of
|
| + * the compilation units being resolved and build a table mapping constant variable elements to the
|
| + * declarations of those variables.
|
| + */
|
| +class ConstantFinder extends RecursiveASTVisitor<Object> {
|
| + /**
|
| + * A table mapping constant variable elements to the declarations of those variables.
|
| + */
|
| + Map<VariableElement, VariableDeclaration> _variableMap = new Map<VariableElement, VariableDeclaration>();
|
| + /**
|
| + * Initialize a newly created constant finder.
|
| + */
|
| + ConstantFinder() : super() {
|
| + }
|
| + /**
|
| + * Return a table mapping constant variable elements to the declarations of those variables.
|
| + * @return a table mapping constant variable elements to the declarations of those variables
|
| + */
|
| + Map<VariableElement, VariableDeclaration> get variableMap => _variableMap;
|
| + Object visitVariableDeclaration(VariableDeclaration node) {
|
| + super.visitVariableDeclaration(node);
|
| + Expression initializer4 = node.initializer;
|
| + if (initializer4 != null && node.isConst()) {
|
| + VariableElement element23 = node.element;
|
| + if (element23 != null) {
|
| + _variableMap[element23] = node;
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +/**
|
| + * Instances of the class {@code ConstantValueComputer} compute the values of constant variables in
|
| + * one or more compilation units. The expected usage pattern is for the compilation units to be
|
| + * added to this computer using the method {@link #add(CompilationUnit)} and then for the method{@link #computeValues()} to invoked exactly once. Any use of an instance after invoking the
|
| + * method {@link #computeValues()} will result in unpredictable behavior.
|
| + */
|
| +class ConstantValueComputer {
|
| + /**
|
| + * The object used to find constant variables in the compilation units that were added.
|
| + */
|
| + ConstantFinder _constantFinder = new ConstantFinder();
|
| + /**
|
| + * A graph in which the nodes are the constant variables and the edges are from each variable to
|
| + * the other constant variables that are referenced in the head's initializer.
|
| + */
|
| + DirectedGraph<VariableElement> _referenceGraph = new DirectedGraph<VariableElement>();
|
| + /**
|
| + * A table mapping constant variables to the declarations of those variables.
|
| + */
|
| + Map<VariableElement, VariableDeclaration> _declarationMap;
|
| + /**
|
| + * Initialize a newly created constant value computer.
|
| + */
|
| + ConstantValueComputer() : super() {
|
| + }
|
| + /**
|
| + * Add the constant variables in the given compilation unit to the list of constant variables
|
| + * whose value needs to be computed.
|
| + * @param unit the compilation unit defining the constant variables to be added
|
| + */
|
| + void add(CompilationUnit unit) {
|
| + unit.accept(_constantFinder);
|
| + }
|
| + /**
|
| + * Compute values for all of the constant variables in the compilation units that were added.
|
| + */
|
| + void computeValues() {
|
| + _declarationMap = _constantFinder.variableMap;
|
| + for (MapEntry<VariableElement, VariableDeclaration> entry in getMapEntrySet(_declarationMap)) {
|
| + VariableElement element = entry.getKey();
|
| + ReferenceFinder referenceFinder = new ReferenceFinder(element, _referenceGraph);
|
| + _referenceGraph.addNode(element);
|
| + entry.getValue().initializer.accept(referenceFinder);
|
| + }
|
| + while (!_referenceGraph.isEmpty()) {
|
| + VariableElement element = _referenceGraph.removeSink();
|
| + while (element != null) {
|
| + computeValueFor(element);
|
| + element = _referenceGraph.removeSink();
|
| + }
|
| + if (!_referenceGraph.isEmpty()) {
|
| + List<VariableElement> variablesInCycle = _referenceGraph.findCycle();
|
| + if (variablesInCycle == null) {
|
| + AnalysisEngine.instance.logger.logError("Exiting constant value computer with ${_referenceGraph.nodeCount} variables that are neither sinks no in a cycle");
|
| + return;
|
| + }
|
| + for (VariableElement variable in variablesInCycle) {
|
| + generateCycleError(variablesInCycle, variable);
|
| + }
|
| + _referenceGraph.removeAllNodes(variablesInCycle);
|
| + }
|
| + }
|
| + }
|
| + /**
|
| + * Compute a value for the given variable.
|
| + * @param variable the variable for which a value is to be computed
|
| + */
|
| + void computeValueFor(VariableElement variable) {
|
| + VariableDeclaration declaration = _declarationMap[variable];
|
| + if (declaration == null) {
|
| + return;
|
| + }
|
| + EvaluationResultImpl result = declaration.initializer.accept(new ConstantVisitor());
|
| + ((variable as VariableElementImpl)).evaluationResult = result;
|
| + if (result is ErrorResult) {
|
| + List<AnalysisError> errors = new List<AnalysisError>();
|
| + for (ErrorResult_ErrorData data in ((result as ErrorResult)).errorData) {
|
| + ASTNode node3 = data.node;
|
| + Source source10 = variable.getAncestor(CompilationUnitElement).source;
|
| + errors.add(new AnalysisError.con2(source10, node3.offset, node3.length, data.errorCode, []));
|
| + }
|
| + }
|
| + }
|
| + /**
|
| + * Generate an error indicating that the given variable is not a valid compile-time constant
|
| + * because it references at least one of the variables in the given cycle, each of which directly
|
| + * or indirectly references the variable.
|
| + * @param variablesInCycle the variables in the cycle that includes the given variable
|
| + * @param variable the variable that is not a valid compile-time constant
|
| + */
|
| + void generateCycleError(List<VariableElement> variablesInCycle, VariableElement variable) {
|
| + }
|
| +}
|
| +/**
|
| * Instances of the class {@code ConstantVisitor} evaluate constant expressions to produce their
|
| * compile-time value. According to the Dart Language Specification: <blockquote> A constant
|
| * expression is one of the following:
|
| @@ -251,16 +377,16 @@ class ConstantVisitor extends GeneralizingASTVisitor<EvaluationResultImpl> {
|
| return ValidResult.RESULT_OBJECT;
|
| }
|
| EvaluationResultImpl visitMethodInvocation(MethodInvocation node) {
|
| - Element element23 = node.methodName.element;
|
| - if (element23 is FunctionElement) {
|
| - FunctionElement function = element23 as FunctionElement;
|
| + Element element24 = node.methodName.element;
|
| + if (element24 is FunctionElement) {
|
| + FunctionElement function = element24 as FunctionElement;
|
| if (function.name == "identical") {
|
| NodeList<Expression> arguments3 = node.argumentList.arguments;
|
| if (arguments3.length == 2) {
|
| Element enclosingElement2 = function.enclosingElement;
|
| if (enclosingElement2 is CompilationUnitElement) {
|
| - LibraryElement library20 = ((enclosingElement2 as CompilationUnitElement)).library;
|
| - if (library20.isDartCore()) {
|
| + LibraryElement library30 = ((enclosingElement2 as CompilationUnitElement)).library;
|
| + if (library30.isDartCore()) {
|
| EvaluationResultImpl leftArgument = arguments3[0].accept(this);
|
| EvaluationResultImpl rightArgument = arguments3[1].accept(this);
|
| return leftArgument.equalEqual(node, rightArgument);
|
| @@ -317,14 +443,16 @@ class ConstantVisitor extends GeneralizingASTVisitor<EvaluationResultImpl> {
|
| * @return the constant value of the static constant
|
| */
|
| EvaluationResultImpl getConstantValue(ASTNode node, Element element) {
|
| - if (element is PropertyAccessorElementImpl) {
|
| - element = ((element as PropertyAccessorElementImpl)).variable;
|
| + if (element is PropertyAccessorElement) {
|
| + element = ((element as PropertyAccessorElement)).variable;
|
| }
|
| if (element is VariableElementImpl) {
|
| EvaluationResultImpl value = ((element as VariableElementImpl)).evaluationResult;
|
| if (value != null) {
|
| return value;
|
| }
|
| + } else if (element is ExecutableElement) {
|
| + return new ValidResult(element);
|
| }
|
| return error(node, null);
|
| }
|
| @@ -348,6 +476,147 @@ class ConstantVisitor extends GeneralizingASTVisitor<EvaluationResultImpl> {
|
| }
|
| }
|
| /**
|
| + * Instances of the class {@code DirectedGraph} implement a directed graph in which the nodes are
|
| + * arbitrary (client provided) objects and edges are represented implicitly. The graph will allow an
|
| + * edge from any node to any other node, including itself, but will not represent multiple edges
|
| + * between the same pair of nodes.
|
| + * @param N the type of the nodes in the graph
|
| + */
|
| +class DirectedGraph<N> {
|
| + /**
|
| + * The table encoding the edges in the graph. An edge is represented by an entry mapping the head
|
| + * to a set of tails. Nodes that are not the head of any edge are represented by an entry mapping
|
| + * the node to an empty set of tails.
|
| + */
|
| + Map<N, Set<N>> _edges = new Map<N, Set<N>>();
|
| + /**
|
| + * Initialize a newly create directed graph to be empty.
|
| + */
|
| + DirectedGraph() : super() {
|
| + }
|
| + /**
|
| + * Add an edge from the given head node to the given tail node. Both nodes will be a part of the
|
| + * graph after this method is invoked, whether or not they were before.
|
| + * @param head the node at the head of the edge
|
| + * @param tail the node at the tail of the edge
|
| + */
|
| + void addEdge(N head, N tail) {
|
| + Set<N> tails = _edges[tail];
|
| + if (tails == null) {
|
| + _edges[tail] = new Set<N>();
|
| + }
|
| + tails = _edges[head];
|
| + if (tails == null) {
|
| + tails = new Set<N>();
|
| + _edges[head] = tails;
|
| + }
|
| + javaSetAdd(tails, tail);
|
| + }
|
| + /**
|
| + * Add the given node to the set of nodes in the graph.
|
| + * @param node the node to be added
|
| + */
|
| + void addNode(N node) {
|
| + Set<N> tails = _edges[node];
|
| + if (tails == null) {
|
| + _edges[node] = new Set<N>();
|
| + }
|
| + }
|
| + /**
|
| + * Return a list of nodes that form a cycle, or {@code null} if there are no cycles in this graph.
|
| + * @return a list of nodes that form a cycle
|
| + */
|
| + List<N> findCycle() => null;
|
| + /**
|
| + * Return the number of nodes in this graph.
|
| + * @return the number of nodes in this graph
|
| + */
|
| + int get nodeCount => _edges.length;
|
| + /**
|
| + * Return a set containing the tails of edges that have the given node as their head. The set will
|
| + * be empty if there are no such edges or if the node is not part of the graph. Clients must not
|
| + * modify the returned set.
|
| + * @param head the node at the head of all of the edges whose tails are to be returned
|
| + * @return a set containing the tails of edges that have the given node as their head
|
| + */
|
| + Set<N> getTails(N head) {
|
| + Set<N> tails = _edges[head];
|
| + if (tails == null) {
|
| + return new Set<N>();
|
| + }
|
| + return tails;
|
| + }
|
| + /**
|
| + * Return {@code true} if this graph is empty.
|
| + * @return {@code true} if this graph is empty
|
| + */
|
| + bool isEmpty() => _edges.isEmpty;
|
| + /**
|
| + * Remove all of the given nodes from this graph. As a consequence, any edges for which those
|
| + * nodes were either a head or a tail will also be removed.
|
| + * @param nodes the nodes to be removed
|
| + */
|
| + void removeAllNodes(List<N> nodes) {
|
| + for (N node in nodes) {
|
| + removeNode(node);
|
| + }
|
| + }
|
| + /**
|
| + * Remove the edge from the given head node to the given tail node. If there was no such edge then
|
| + * the graph will be unmodified: the number of edges will be the same and the set of nodes will be
|
| + * the same (neither node will either be added or removed).
|
| + * @param head the node at the head of the edge
|
| + * @param tail the node at the tail of the edge
|
| + * @return {@code true} if the graph was modified as a result of this operation
|
| + */
|
| + void removeEdge(N head, N tail) {
|
| + Set<N> tails = _edges[head];
|
| + if (tails != null) {
|
| + tails.remove(tail);
|
| + }
|
| + }
|
| + /**
|
| + * Remove the given node from this graph. As a consequence, any edges for which that node was
|
| + * either a head or a tail will also be removed.
|
| + * @param node the node to be removed
|
| + */
|
| + void removeNode(N node) {
|
| + _edges.remove(node);
|
| + for (Set<N> tails in _edges.values) {
|
| + tails.remove(node);
|
| + }
|
| + }
|
| + /**
|
| + * Find one node (referred to as a sink node) that has no outgoing edges (that is, for which there
|
| + * are no edges that have that node as the head of the edge) and remove it from this graph. Return
|
| + * the node that was removed, or {@code null} if there are no such nodes either because the graph
|
| + * is empty or because every node in the graph has at least one outgoing edge. As a consequence of
|
| + * removing the node from the graph any edges for which that node was a tail will also be removed.
|
| + * @return the sink node that was removed
|
| + */
|
| + N removeSink() {
|
| + N sink = findSink();
|
| + if (sink == null) {
|
| + return null;
|
| + }
|
| + removeNode(sink);
|
| + return sink;
|
| + }
|
| + /**
|
| + * Return one node that has no outgoing edges (that is, for which there are no edges that have
|
| + * that node as the head of the edge), or {@code null} if there are no such nodes.
|
| + * @return a sink node
|
| + */
|
| + N findSink() {
|
| + for (MapEntry<N, Set<N>> entry in getMapEntrySet(_edges)) {
|
| + if (entry.getValue().isEmpty) {
|
| + return entry.getKey();
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +/**
|
| * Instances of the class {@code ErrorResult} represent the result of evaluating an expression that
|
| * is not a valid compile time constant.
|
| */
|
| @@ -363,9 +632,9 @@ class ErrorResult extends EvaluationResultImpl {
|
| * @param errorCode the error code for the error to be generated
|
| */
|
| ErrorResult.con1(ASTNode node, ErrorCode errorCode) {
|
| - _jtd_constructor_157_impl(node, errorCode);
|
| + _jtd_constructor_162_impl(node, errorCode);
|
| }
|
| - _jtd_constructor_157_impl(ASTNode node, ErrorCode errorCode) {
|
| + _jtd_constructor_162_impl(ASTNode node, ErrorCode errorCode) {
|
| _errors.add(new ErrorResult_ErrorData(node, errorCode));
|
| }
|
| /**
|
| @@ -375,9 +644,9 @@ class ErrorResult extends EvaluationResultImpl {
|
| * @param secondResult the second set of results being merged
|
| */
|
| ErrorResult.con2(ErrorResult firstResult, ErrorResult secondResult) {
|
| - _jtd_constructor_158_impl(firstResult, secondResult);
|
| + _jtd_constructor_163_impl(firstResult, secondResult);
|
| }
|
| - _jtd_constructor_158_impl(ErrorResult firstResult, ErrorResult secondResult) {
|
| + _jtd_constructor_163_impl(ErrorResult firstResult, ErrorResult secondResult) {
|
| _errors.addAll(firstResult._errors);
|
| _errors.addAll(secondResult._errors);
|
| }
|
| @@ -390,7 +659,6 @@ class ErrorResult extends EvaluationResultImpl {
|
| EvaluationResultImpl divide(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.divideError(node, this);
|
| EvaluationResultImpl equalEqual(Expression node, EvaluationResultImpl rightOperand) => rightOperand.equalEqualError(node, this);
|
| List<ErrorResult_ErrorData> get errorData => _errors;
|
| - List<AnalysisError> get errors => new List.from(_errors);
|
| EvaluationResultImpl greaterThan(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.greaterThanError(node, this);
|
| EvaluationResultImpl greaterThanOrEqual(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.greaterThanOrEqualError(node, this);
|
| EvaluationResultImpl integerDivide(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.integerDivideError(node, this);
|
| @@ -549,6 +817,45 @@ abstract class EvaluationResultImpl {
|
| EvaluationResultImpl timesValid(BinaryExpression node, ValidResult leftOperand);
|
| }
|
| /**
|
| + * Instances of the class {@code ReferenceFinder} add reference information for a given variable to
|
| + * the bi-directional mapping used to order the evaluation of constants.
|
| + */
|
| +class ReferenceFinder extends RecursiveASTVisitor<Object> {
|
| + /**
|
| + * The element representing the variable whose initializer will be visited.
|
| + */
|
| + VariableElement _source;
|
| + /**
|
| + * A graph in which the nodes are the constant variables and the edges are from each variable to
|
| + * the other constant variables that are referenced in the head's initializer.
|
| + */
|
| + DirectedGraph<VariableElement> _referenceGraph;
|
| + /**
|
| + * Initialize a newly created reference finder to find references from the given variable to other
|
| + * variables and to add those references to the given graph.
|
| + * @param source the element representing the variable whose initializer will be visited
|
| + * @param referenceGraph a graph recording which variables (heads) reference which other variables
|
| + * (tails) in their initializers
|
| + */
|
| + ReferenceFinder(VariableElement source, DirectedGraph<VariableElement> referenceGraph) {
|
| + this._source = source;
|
| + this._referenceGraph = referenceGraph;
|
| + }
|
| + Object visitSimpleIdentifier(SimpleIdentifier node) {
|
| + Element element25 = node.element;
|
| + if (element25 is PropertyAccessorElement) {
|
| + element25 = ((element25 as PropertyAccessorElement)).variable;
|
| + }
|
| + if (element25 is VariableElement) {
|
| + VariableElement variable = element25 as VariableElement;
|
| + if (variable.isConst()) {
|
| + _referenceGraph.addEdge(_source, variable);
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +/**
|
| * Instances of the class {@code ValidResult} represent the result of attempting to evaluate a valid
|
| * compile time constant expression.
|
| */
|
| @@ -742,7 +1049,7 @@ class ValidResult extends EvaluationResultImpl {
|
| } else if (leftValue is int) {
|
| if (_value is int) {
|
| if (((_value as int)) == 0) {
|
| - return error2(node.rightOperand, CompileTimeErrorCode.COMPILE_TIME_CONSTANT_RAISES_EXCEPTION_DIVIDE_BY_ZERO);
|
| + return valueOf3(((leftValue as int)).toDouble() / ((_value as int)).toDouble());
|
| }
|
| return valueOf(((leftValue as int)) ~/ (_value as int));
|
| } else if (_value is double) {
|
| @@ -843,7 +1150,7 @@ class ValidResult extends EvaluationResultImpl {
|
| } else if (leftValue is int) {
|
| if (_value is int) {
|
| if (((_value as int)) == 0) {
|
| - return error2(node.rightOperand, CompileTimeErrorCode.COMPILE_TIME_CONSTANT_RAISES_EXCEPTION_DIVIDE_BY_ZERO);
|
| + return valueOf3(((leftValue as int)).toDouble() / ((_value as int)).toDouble());
|
| }
|
| return valueOf(((leftValue as int)) ~/ (_value as int));
|
| } else if (_value is double) {
|
| @@ -983,10 +1290,10 @@ class ValidResult extends EvaluationResultImpl {
|
| } else if (_value == null) {
|
| return error(node.rightOperand);
|
| } else if (leftValue is int) {
|
| - if (((_value as int)) == 0) {
|
| - return error2(node.rightOperand, CompileTimeErrorCode.COMPILE_TIME_CONSTANT_RAISES_EXCEPTION_DIVIDE_BY_ZERO);
|
| - }
|
| if (_value is int) {
|
| + if (((_value as int)) == 0) {
|
| + return valueOf3(((leftValue as int)).toDouble() % ((_value as int)).toDouble());
|
| + }
|
| return valueOf(((leftValue as int)).remainder((_value as int)));
|
| } else if (_value is double) {
|
| return valueOf3(((leftValue as int)).toDouble() % ((_value as double)));
|
|
|