blob: 240323147b3715537923b7dbf809992b8d9a4655 [file] [log] [blame]
// This code was auto-generated, is not intended to be edited, and is subject to
// significant change. Please see the README file for more information.
library engine.constant;
import 'java_core.dart';
import 'source.dart' show Source;
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 `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 `null`.
* * `null`.
* * A reference to a static constant variable.
* * An identifier expression that denotes a constant variable, a class or a type parameter.
* * 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 `(e)` where `e` is a constant expression.
* * An expression of one of the forms `identical(e1, e2)`, `e1 == e2`,
* `e1 != e2` where `e1` and `e2` are constant expressions that evaluate to a
* numeric, string or boolean value or to `null`.
* * An expression of one of the forms `!e`, `e1 && e2` or `e1 || e2`, where
* `e`, `e1` and `e2` are constant expressions that evaluate to a boolean value or
* to `null`.
* * An expression of one of the forms `~e`, `e1 ^ e2`, `e1 & e2`,
* `e1 | e2`, `e1 >> e2` or `e1 << e2`, where `e`, `e1` and `e2`
* are constant expressions that evaluate to an integer value or to `null`.
* * An expression of one of the forms `-e`, `e1 + e2`, `e1 - e2`,
* `e1 * e2`, `e1 / e2`, `e1 ~/ e2`, `e1 > e2`, `e1 < e2`,
* `e1 >= e2`, `e1 <= e2` or `e1 % e2`, where `e`, `e1` and `e2`
* are constant expressions that evaluate to a numeric value or to `null`.
*
* </blockquote> The values returned by instances of this class are therefore `null` and
* instances of the classes `Boolean`, `BigInteger`, `Double`, `String`, and
* `DartObject`.
*
* In addition, this class defines several values that can be returned to indicate various
* conditions encountered during evaluation. These are documented with the static field that define
* those values.
*/
class ConstantEvaluator {
/**
* The source containing the expression(s) that will be evaluated.
*/
Source _source;
/**
* Initialize a newly created evaluator to evaluate expressions in the given source.
*
* @param source the source containing the expression(s) that will be evaluated
*/
ConstantEvaluator(Source source) {
this._source = source;
}
EvaluationResult evaluate(Expression expression) {
EvaluationResultImpl result = expression.accept(new ConstantVisitor());
if (result is ValidResult) {
return EvaluationResult.forValue(((result as ValidResult)).value);
}
List<AnalysisError> errors = new List<AnalysisError>();
for (ErrorResult_ErrorData data in ((result as ErrorResult)).errorData) {
ASTNode node = data.node;
errors.add(new AnalysisError.con2(_source, node.offset, node.length, data.errorCode, []));
}
return EvaluationResult.forErrors(new List.from(errors));
}
}
/**
* Instances of the class `EvaluationResult` represent the result of attempting to evaluate an
* expression.
*/
class EvaluationResult {
/**
* Return an evaluation result representing the result of evaluating an expression that is not a
* compile-time constant because of the given errors.
*
* @param errors the errors that should be reported for the expression(s) that were evaluated
* @return the result of evaluating an expression that is not a compile-time constant
*/
static EvaluationResult forErrors(List<AnalysisError> errors) => new EvaluationResult(null, errors);
/**
* Return an evaluation result representing the result of evaluating an expression that is a
* compile-time constant that evaluates to the given value.
*
* @param value the value of the expression
* @return the result of evaluating an expression that is a compile-time constant
*/
static EvaluationResult forValue(Object value) => new EvaluationResult(value, null);
/**
* The value of the expression.
*/
Object value;
/**
* The errors that should be reported for the expression(s) that were evaluated.
*/
List<AnalysisError> _errors;
/**
* Initialize a newly created result object with the given state. Clients should use one of the
* factory methods: [forErrors] and [forValue].
*
* @param value the value of the expression
* @param errors the errors that should be reported for the expression(s) that were evaluated
*/
EvaluationResult(Object value, List<AnalysisError> errors) {
this.value = value;
this._errors = errors;
}
/**
* Return an array containing the errors that should be reported for the expression(s) that were
* evaluated. If there are no such errors, the array will be empty. The array can be empty even if
* the expression is not a valid compile time constant if the errors would have been reported by
* other parts of the analysis engine.
*/
List<AnalysisError> get errors => _errors == null ? AnalysisError.NO_ERRORS : _errors;
/**
* Return `true` if the expression is a compile-time constant expression that would not
* throw an exception when evaluated.
*
* @return `true` if the expression is a valid compile-time constant expression
*/
bool get isValid => _errors == null;
}
/**
* Instances of the class `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.
*/
final Map<VariableElement, VariableDeclaration> variableMap = new Map<VariableElement, VariableDeclaration>();
Object visitVariableDeclaration(VariableDeclaration node) {
super.visitVariableDeclaration(node);
Expression initializer = node.initializer;
if (initializer != null && node.isConst) {
VariableElement element = node.element;
if (element != null) {
variableMap[element] = node;
}
}
return null;
}
}
/**
* Instances of the class `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 [add] and then for the method
* [computeValues] to invoked exactly once. Any use of an instance after invoking the
* method [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;
/**
* 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 node = data.node;
Source source = variable.getAncestor(CompilationUnitElement).source;
errors.add(new AnalysisError.con2(source, node.offset, node.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 `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:
*
* * 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 `null`.
* * `null`.
* * A reference to a static constant variable.
* * An identifier expression that denotes a constant variable, a class or a type parameter.
* * 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 `(e)` where `e` is a constant expression.
* * An expression of one of the forms `identical(e1, e2)`, `e1 == e2`,
* `e1 != e2` where `e1` and `e2` are constant expressions that evaluate to a
* numeric, string or boolean value or to `null`.
* * An expression of one of the forms `!e`, `e1 && e2` or `e1 || e2`, where
* `e`, `e1` and `e2` are constant expressions that evaluate to a boolean value or
* to `null`.
* * An expression of one of the forms `~e`, `e1 ^ e2`, `e1 & e2`,
* `e1 | e2`, `e1 >> e2` or `e1 << e2`, where `e`, `e1` and `e2`
* are constant expressions that evaluate to an integer value or to `null`.
* * An expression of one of the forms `-e`, `e1 + e2`, `e1 - e2`,
* `e1 * e2`, `e1 / e2`, `e1 ~/ e2`, `e1 > e2`, `e1 < e2`,
* `e1 >= e2`, `e1 <= e2` or `e1 % e2`, where `e`, `e1` and `e2`
* are constant expressions that evaluate to a numeric value or to `null`.
*
* </blockquote>
*/
class ConstantVisitor extends GeneralizingASTVisitor<EvaluationResultImpl> {
EvaluationResultImpl visitAdjacentStrings(AdjacentStrings node) {
EvaluationResultImpl result = null;
for (StringLiteral string in node.strings) {
if (result == null) {
result = string.accept(this);
} else {
result = result.concatenate(node, string.accept(this));
}
}
return result;
}
EvaluationResultImpl visitBinaryExpression(BinaryExpression node) {
EvaluationResultImpl leftResult = node.leftOperand.accept(this);
EvaluationResultImpl rightResult = node.rightOperand.accept(this);
TokenType operatorType = node.operator.type;
if (operatorType != TokenType.BANG_EQ && operatorType != TokenType.EQ_EQ) {
if (leftResult is ValidResult && ((leftResult as ValidResult)).isNull || rightResult is ValidResult && ((rightResult as ValidResult)).isNull) {
return error(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
}
while (true) {
if (operatorType == TokenType.AMPERSAND) {
return leftResult.bitAnd(node, rightResult);
} else if (operatorType == TokenType.AMPERSAND_AMPERSAND) {
return leftResult.logicalAnd(node, rightResult);
} else if (operatorType == TokenType.BANG_EQ) {
return leftResult.notEqual(node, rightResult);
} else if (operatorType == TokenType.BAR) {
return leftResult.bitOr(node, rightResult);
} else if (operatorType == TokenType.BAR_BAR) {
return leftResult.logicalOr(node, rightResult);
} else if (operatorType == TokenType.CARET) {
return leftResult.bitXor(node, rightResult);
} else if (operatorType == TokenType.EQ_EQ) {
return leftResult.equalEqual(node, rightResult);
} else if (operatorType == TokenType.GT) {
return leftResult.greaterThan(node, rightResult);
} else if (operatorType == TokenType.GT_EQ) {
return leftResult.greaterThanOrEqual(node, rightResult);
} else if (operatorType == TokenType.GT_GT) {
return leftResult.shiftRight(node, rightResult);
} else if (operatorType == TokenType.LT) {
return leftResult.lessThan(node, rightResult);
} else if (operatorType == TokenType.LT_EQ) {
return leftResult.lessThanOrEqual(node, rightResult);
} else if (operatorType == TokenType.LT_LT) {
return leftResult.shiftLeft(node, rightResult);
} else if (operatorType == TokenType.MINUS) {
return leftResult.minus(node, rightResult);
} else if (operatorType == TokenType.PERCENT) {
return leftResult.remainder(node, rightResult);
} else if (operatorType == TokenType.PLUS) {
return leftResult.add(node, rightResult);
} else if (operatorType == TokenType.STAR) {
return leftResult.times(node, rightResult);
} else if (operatorType == TokenType.SLASH) {
return leftResult.divide(node, rightResult);
} else if (operatorType == TokenType.TILDE_SLASH) {
return leftResult.integerDivide(node, rightResult);
}
break;
}
return error(node, null);
}
EvaluationResultImpl visitBooleanLiteral(BooleanLiteral node) => node.value ? ValidResult.RESULT_TRUE : ValidResult.RESULT_FALSE;
EvaluationResultImpl visitConditionalExpression(ConditionalExpression node) {
Expression condition = node.condition;
EvaluationResultImpl conditionResult = condition.accept(this);
conditionResult = conditionResult.applyBooleanConversion(condition);
if (conditionResult is ErrorResult) {
return conditionResult;
}
EvaluationResultImpl thenResult = node.thenExpression.accept(this);
if (thenResult is ErrorResult) {
return thenResult;
}
EvaluationResultImpl elseResult = node.elseExpression.accept(this);
if (elseResult is ErrorResult) {
return elseResult;
}
return (identical(conditionResult, ValidResult.RESULT_TRUE)) ? thenResult : elseResult;
}
EvaluationResultImpl visitDoubleLiteral(DoubleLiteral node) => new ValidResult(node.value);
EvaluationResultImpl visitInstanceCreationExpression(InstanceCreationExpression node) {
if (!node.isConst) {
return error(node, null);
}
ConstructorElement constructor = node.staticElement;
if (constructor != null && constructor.isConst) {
node.argumentList.accept(this);
return ValidResult.RESULT_OBJECT;
}
return error(node, null);
}
EvaluationResultImpl visitIntegerLiteral(IntegerLiteral node) => new ValidResult(node.value);
EvaluationResultImpl visitInterpolationExpression(InterpolationExpression node) {
EvaluationResultImpl result = node.expression.accept(this);
return result.performToString(node);
}
EvaluationResultImpl visitInterpolationString(InterpolationString node) => new ValidResult(node.value);
EvaluationResultImpl visitListLiteral(ListLiteral node) {
if (node.constKeyword == null) {
return new ErrorResult.con1(node, CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL);
}
ErrorResult result = null;
for (Expression element in node.elements) {
result = union(result, element.accept(this));
}
if (result != null) {
return result;
}
return ValidResult.RESULT_OBJECT;
}
EvaluationResultImpl visitMapLiteral(MapLiteral node) {
if (node.constKeyword == null) {
return new ErrorResult.con1(node, CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL);
}
ErrorResult result = null;
for (MapLiteralEntry entry in node.entries) {
result = union(result, entry.key.accept(this));
result = union(result, entry.value.accept(this));
}
if (result != null) {
return result;
}
return ValidResult.RESULT_OBJECT;
}
EvaluationResultImpl visitMethodInvocation(MethodInvocation node) {
Element element = node.methodName.staticElement;
if (element is FunctionElement) {
FunctionElement function = element as FunctionElement;
if (function.name == "identical") {
NodeList<Expression> arguments = node.argumentList.arguments;
if (arguments.length == 2) {
Element enclosingElement = function.enclosingElement;
if (enclosingElement is CompilationUnitElement) {
LibraryElement library = ((enclosingElement as CompilationUnitElement)).library;
if (library.isDartCore) {
EvaluationResultImpl leftArgument = arguments[0].accept(this);
EvaluationResultImpl rightArgument = arguments[1].accept(this);
return leftArgument.equalEqual(node, rightArgument);
}
}
}
}
}
return error(node, null);
}
EvaluationResultImpl visitNamedExpression(NamedExpression node) => node.expression.accept(this);
EvaluationResultImpl visitNode(ASTNode node) => error(node, null);
EvaluationResultImpl visitNullLiteral(NullLiteral node) => ValidResult.RESULT_NULL;
EvaluationResultImpl visitParenthesizedExpression(ParenthesizedExpression node) => node.expression.accept(this);
EvaluationResultImpl visitPrefixedIdentifier(PrefixedIdentifier node) {
SimpleIdentifier prefixNode = node.prefix;
Element prefixElement = prefixNode.staticElement;
if (prefixElement is! PrefixElement) {
EvaluationResultImpl prefixResult = prefixNode.accept(this);
if (prefixResult is! ValidResult) {
return error(node, null);
}
}
return getConstantValue(node, node.staticElement);
}
EvaluationResultImpl visitPrefixExpression(PrefixExpression node) {
EvaluationResultImpl operand = node.operand.accept(this);
if (operand is ValidResult && ((operand as ValidResult)).isNull) {
return error(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
while (true) {
if (node.operator.type == TokenType.BANG) {
return operand.logicalNot(node);
} else if (node.operator.type == TokenType.TILDE) {
return operand.bitNot(node);
} else if (node.operator.type == TokenType.MINUS) {
return operand.negated(node);
}
break;
}
return error(node, null);
}
EvaluationResultImpl visitPropertyAccess(PropertyAccess node) => getConstantValue(node, node.propertyName.staticElement);
EvaluationResultImpl visitSimpleIdentifier(SimpleIdentifier node) => getConstantValue(node, node.staticElement);
EvaluationResultImpl visitSimpleStringLiteral(SimpleStringLiteral node) => new ValidResult(node.value);
EvaluationResultImpl visitStringInterpolation(StringInterpolation node) {
EvaluationResultImpl result = null;
for (InterpolationElement element in node.elements) {
if (result == null) {
result = element.accept(this);
} else {
result = result.concatenate(node, element.accept(this));
}
}
return result;
}
/**
* Return a result object representing an error associated with the given node.
*
* @param node the AST node associated with the error
* @param code the error code indicating the nature of the error
* @return a result object representing an error associated with the given node
*/
ErrorResult error(ASTNode node, ErrorCode code) => new ErrorResult.con1(node, code == null ? CompileTimeErrorCode.INVALID_CONSTANT : code);
/**
* Return the constant value of the static constant represented by the given element.
*
* @param node the node to be used if an error needs to be reported
* @param element the element whose value is to be returned
* @return the constant value of the static constant
*/
EvaluationResultImpl getConstantValue(ASTNode node, Element element) {
if (element is PropertyAccessorElement) {
element = ((element as PropertyAccessorElement)).variable;
}
if (element is VariableElementImpl) {
VariableElementImpl variableElementImpl = element as VariableElementImpl;
EvaluationResultImpl value = variableElementImpl.evaluationResult;
if (variableElementImpl.isConst && value != null) {
return value;
}
} else if (element is ExecutableElement) {
if (((element as ExecutableElement)).isStatic) {
return new ValidResult(element);
}
} else if (element is ClassElement) {
return ValidResult.RESULT_OBJECT;
}
return error(node, null);
}
/**
* Return the union of the errors encoded in the given results.
*
* @param leftResult the first set of errors, or `null` if there was no previous collection
* of errors
* @param rightResult the errors to be added to the collection, or a valid result if there are no
* errors to be added
* @return the union of the errors encoded in the given results
*/
ErrorResult union(ErrorResult leftResult, EvaluationResultImpl rightResult) {
if (rightResult is ErrorResult) {
if (leftResult != null) {
return new ErrorResult.con2(leftResult, rightResult as ErrorResult);
} else {
return rightResult as ErrorResult;
}
}
return leftResult;
}
}
/**
* Instances of the class `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>>();
/**
* 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 `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 `true` if this graph is empty.
*
* @return `true` if this graph is empty
*/
bool get 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 `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 `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 `null` if there are no such nodes.
*
* @return a sink node
*/
N findSink() {
for (N key in _edges.keys) {
if (_edges[key].isEmpty) return key;
}
return null;
}
}
/**
* Instances of the class `ErrorResult` represent the result of evaluating an expression that
* is not a valid compile time constant.
*/
class ErrorResult extends EvaluationResultImpl {
/**
* The errors that prevent the expression from being a valid compile time constant.
*/
final List<ErrorResult_ErrorData> errorData = new List<ErrorResult_ErrorData>();
/**
* Initialize a newly created result representing the error with the given code reported against
* the given node.
*
* @param node the node against which the error should be reported
* @param errorCode the error code for the error to be generated
*/
ErrorResult.con1(ASTNode node, ErrorCode errorCode) {
errorData.add(new ErrorResult_ErrorData(node, errorCode));
}
/**
* Initialize a newly created result to represent the union of the errors in the given result
* objects.
*
* @param firstResult the first set of results being merged
* @param secondResult the second set of results being merged
*/
ErrorResult.con2(ErrorResult firstResult, ErrorResult secondResult) {
errorData.addAll(firstResult.errorData);
errorData.addAll(secondResult.errorData);
}
EvaluationResultImpl add(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.addToError(node, this);
EvaluationResultImpl applyBooleanConversion(ASTNode node) => this;
EvaluationResultImpl bitAnd(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.bitAndError(node, this);
EvaluationResultImpl bitNot(Expression node) => this;
EvaluationResultImpl bitOr(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.bitOrError(node, this);
EvaluationResultImpl bitXor(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.bitXorError(node, this);
EvaluationResultImpl concatenate(Expression node, EvaluationResultImpl rightOperand) => rightOperand.concatenateError(node, this);
EvaluationResultImpl divide(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.divideError(node, this);
EvaluationResultImpl equalEqual(Expression node, EvaluationResultImpl rightOperand) => rightOperand.equalEqualError(node, this);
bool equalValues(EvaluationResultImpl result) => false;
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);
EvaluationResultImpl integerDivideValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl lessThan(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.lessThanError(node, this);
EvaluationResultImpl lessThanOrEqual(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.lessThanOrEqualError(node, this);
EvaluationResultImpl logicalAnd(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.logicalAndError(node, this);
EvaluationResultImpl logicalNot(Expression node) => this;
EvaluationResultImpl logicalOr(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.logicalOrError(node, this);
EvaluationResultImpl minus(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.minusError(node, this);
EvaluationResultImpl negated(Expression node) => this;
EvaluationResultImpl notEqual(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.notEqualError(node, this);
EvaluationResultImpl performToString(ASTNode node) => this;
EvaluationResultImpl remainder(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.remainderError(node, this);
EvaluationResultImpl shiftLeft(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.shiftLeftError(node, this);
EvaluationResultImpl shiftRight(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.shiftRightError(node, this);
EvaluationResultImpl times(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.timesError(node, this);
EvaluationResultImpl addToError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl addToValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl bitAndError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl bitAndValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl bitOrError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl bitOrValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl bitXorError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl bitXorValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl concatenateError(Expression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl concatenateValid(Expression node, ValidResult leftOperand) => this;
EvaluationResultImpl divideError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl divideValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl equalEqualError(Expression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl equalEqualValid(Expression node, ValidResult leftOperand) => this;
EvaluationResultImpl greaterThanError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl greaterThanOrEqualError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl greaterThanOrEqualValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl greaterThanValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl integerDivideError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl lessThanError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl lessThanOrEqualError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl lessThanOrEqualValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl lessThanValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl logicalAndError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl logicalAndValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl logicalOrError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl logicalOrValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl minusError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl minusValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl notEqualError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl notEqualValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl remainderError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl remainderValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl shiftLeftError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl shiftLeftValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl shiftRightError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl shiftRightValid(BinaryExpression node, ValidResult leftOperand) => this;
EvaluationResultImpl timesError(BinaryExpression node, ErrorResult leftOperand) => new ErrorResult.con2(this, leftOperand);
EvaluationResultImpl timesValid(BinaryExpression node, ValidResult leftOperand) => this;
}
class ErrorResult_ErrorData {
/**
* The node against which the error should be reported.
*/
ASTNode node;
/**
* The error code for the error to be generated.
*/
ErrorCode errorCode;
/**
* Initialize a newly created data holder to represent the error with the given code reported
* against the given node.
*
* @param node the node against which the error should be reported
* @param errorCode the error code for the error to be generated
*/
ErrorResult_ErrorData(ASTNode node, ErrorCode errorCode) {
this.node = node;
this.errorCode = errorCode;
}
}
/**
* Instances of the class `InternalResult` represent the result of attempting to evaluate a
* expression.
*/
abstract class EvaluationResultImpl {
EvaluationResultImpl add(BinaryExpression node, EvaluationResultImpl rightOperand);
/**
* Return the result of applying boolean conversion to this result.
*
* @param node the node against which errors should be reported
* @return the result of applying boolean conversion to the given value
*/
EvaluationResultImpl applyBooleanConversion(ASTNode node);
EvaluationResultImpl bitAnd(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl bitNot(Expression node);
EvaluationResultImpl bitOr(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl bitXor(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl concatenate(Expression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl divide(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl equalEqual(Expression node, EvaluationResultImpl rightOperand);
bool equalValues(EvaluationResultImpl result);
EvaluationResultImpl greaterThan(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl greaterThanOrEqual(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl integerDivide(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl lessThan(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl lessThanOrEqual(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl logicalAnd(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl logicalNot(Expression node);
EvaluationResultImpl logicalOr(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl minus(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl negated(Expression node);
EvaluationResultImpl notEqual(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl performToString(ASTNode node);
EvaluationResultImpl remainder(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl shiftLeft(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl shiftRight(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl times(BinaryExpression node, EvaluationResultImpl rightOperand);
EvaluationResultImpl addToError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl addToValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl bitAndError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl bitAndValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl bitOrError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl bitOrValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl bitXorError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl bitXorValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl concatenateError(Expression node, ErrorResult leftOperand);
EvaluationResultImpl concatenateValid(Expression node, ValidResult leftOperand);
EvaluationResultImpl divideError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl divideValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl equalEqualError(Expression node, ErrorResult leftOperand);
EvaluationResultImpl equalEqualValid(Expression node, ValidResult leftOperand);
EvaluationResultImpl greaterThanError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl greaterThanOrEqualError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl greaterThanOrEqualValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl greaterThanValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl integerDivideError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl integerDivideValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl lessThanError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl lessThanOrEqualError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl lessThanOrEqualValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl lessThanValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl logicalAndError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl logicalAndValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl logicalOrError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl logicalOrValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl minusError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl minusValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl notEqualError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl notEqualValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl remainderError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl remainderValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl shiftLeftError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl shiftLeftValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl shiftRightError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl shiftRightValid(BinaryExpression node, ValidResult leftOperand);
EvaluationResultImpl timesError(BinaryExpression node, ErrorResult leftOperand);
EvaluationResultImpl timesValid(BinaryExpression node, ValidResult leftOperand);
}
/**
* Instances of the class `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 element = node.staticElement;
if (element is PropertyAccessorElement) {
element = ((element as PropertyAccessorElement)).variable;
}
if (element is VariableElement) {
VariableElement variable = element as VariableElement;
if (variable.isConst) {
_referenceGraph.addEdge(_source, variable);
}
}
return null;
}
}
/**
* Instances of the class `ValidResult` represent the result of attempting to evaluate a valid
* compile time constant expression.
*/
class ValidResult extends EvaluationResultImpl {
/**
* A result object representing the value 'false'.
*/
static ValidResult RESULT_FALSE = new ValidResult(false);
/**
* A result object representing the an object without specific type on which no further operations
* can be performed.
*/
static ValidResult RESULT_DYNAMIC = new ValidResult(null);
/**
* A result object representing the an arbitrary integer on which no further operations can be
* performed.
*/
static ValidResult RESULT_INT = new ValidResult(null);
/**
* A result object representing the `null` value.
*/
static ValidResult RESULT_NULL = new ValidResult(null);
/**
* A result object representing the an arbitrary numeric on which no further operations can be
* performed.
*/
static ValidResult RESULT_NUM = new ValidResult(null);
/**
* A result object representing the an arbitrary boolean on which no further operations can be
* performed.
*/
static ValidResult RESULT_BOOL = new ValidResult(null);
/**
* A result object representing the an arbitrary object on which no further operations can be
* performed.
*/
static ValidResult RESULT_OBJECT = new ValidResult(new Object());
/**
* A result object representing the an arbitrary string on which no further operations can be
* performed.
*/
static ValidResult RESULT_STRING = new ValidResult("<string>");
/**
* A result object representing the value 'true'.
*/
static ValidResult RESULT_TRUE = new ValidResult(true);
/**
* The value of the expression.
*/
Object value;
/**
* Initialize a newly created result to represent the given value.
*
* @param value the value of the expression
*/
ValidResult(Object value) {
this.value = value;
}
EvaluationResultImpl add(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.addToValid(node, this);
/**
* Return the result of applying boolean conversion to this result.
*
* @param node the node against which errors should be reported
* @return the result of applying boolean conversion to the given value
*/
EvaluationResultImpl applyBooleanConversion(ASTNode node) => booleanConversion(node, value);
EvaluationResultImpl bitAnd(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.bitAndValid(node, this);
EvaluationResultImpl bitNot(Expression node) {
if (isSomeInt) {
return RESULT_INT;
}
if (value == null) {
return error(node);
} else if (value is int) {
return valueOf(~((value as int)));
}
return error(node);
}
EvaluationResultImpl bitOr(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.bitOrValid(node, this);
EvaluationResultImpl bitXor(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.bitXorValid(node, this);
EvaluationResultImpl concatenate(Expression node, EvaluationResultImpl rightOperand) => rightOperand.concatenateValid(node, this);
EvaluationResultImpl divide(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.divideValid(node, this);
EvaluationResultImpl equalEqual(Expression node, EvaluationResultImpl rightOperand) => rightOperand.equalEqualValid(node, this);
bool equalValues(EvaluationResultImpl result) => identical(equalEqual(null, result), RESULT_TRUE);
EvaluationResultImpl greaterThan(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.greaterThanValid(node, this);
EvaluationResultImpl greaterThanOrEqual(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.greaterThanOrEqualValid(node, this);
EvaluationResultImpl integerDivide(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.integerDivideValid(node, this);
EvaluationResultImpl lessThan(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.lessThanValid(node, this);
EvaluationResultImpl lessThanOrEqual(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.lessThanOrEqualValid(node, this);
EvaluationResultImpl logicalAnd(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.logicalAndValid(node, this);
EvaluationResultImpl logicalNot(Expression node) {
if (isSomeBool) {
return RESULT_BOOL;
}
if (value == null) {
return RESULT_TRUE;
} else if (value is bool) {
return ((value as bool)) ? RESULT_FALSE : RESULT_TRUE;
}
return error(node);
}
EvaluationResultImpl logicalOr(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.logicalOrValid(node, this);
EvaluationResultImpl minus(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.minusValid(node, this);
EvaluationResultImpl negated(Expression node) {
if (isSomeNum) {
return RESULT_INT;
}
if (value == null) {
return error(node);
} else if (value is int) {
return valueOf(-((value as int)));
} else if (value is double) {
return valueOf3(-((value as double)));
}
return error(node);
}
EvaluationResultImpl notEqual(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.notEqualValid(node, this);
EvaluationResultImpl performToString(ASTNode node) {
if (value == null) {
return valueOf4("null");
} else if (value is bool) {
return valueOf4(((value as bool)).toString());
} else if (value is int) {
return valueOf4(((value as int)).toString());
} else if (value is double) {
return valueOf4(((value as double)).toString());
} else if (value is String) {
return this;
}
return error(node);
}
EvaluationResultImpl remainder(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.remainderValid(node, this);
EvaluationResultImpl shiftLeft(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.shiftLeftValid(node, this);
EvaluationResultImpl shiftRight(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.shiftRightValid(node, this);
EvaluationResultImpl times(BinaryExpression node, EvaluationResultImpl rightOperand) => rightOperand.timesValid(node, this);
String toString() {
if (value == null) {
return "null";
}
return value.toString();
}
EvaluationResultImpl addToError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl addToValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
} else if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_NUM;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf(((leftValue as int)) + (value as int));
} else if (value is double) {
return valueOf3(((leftValue as int)).toDouble() + ((value as double)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf3(((leftValue as double)) + ((value as int)).toDouble());
} else if (value is double) {
return valueOf3(((leftValue as double)) + ((value as double)));
}
} else if (leftValue is String) {
if (value is String) {
return valueOf4("${((leftValue as String))}${((value as String))}");
}
}
return error(node);
}
EvaluationResultImpl bitAndError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl bitAndValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyInt || !leftOperand.isAnyInt) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_INT);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf(((leftValue as int)) & (value as int));
}
return error(node.leftOperand);
}
if (value is int) {
return error(node.rightOperand);
}
return union(error(node.leftOperand), error(node.rightOperand));
}
EvaluationResultImpl bitOrError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl bitOrValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyInt || !leftOperand.isAnyInt) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_INT);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf(((leftValue as int)) | (value as int));
}
return error(node.leftOperand);
}
if (value is int) {
return error(node.rightOperand);
}
return union(error(node.leftOperand), error(node.rightOperand));
}
EvaluationResultImpl bitXorError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl bitXorValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyInt || !leftOperand.isAnyInt) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_INT);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf(((leftValue as int)) ^ (value as int));
}
return error(node.leftOperand);
}
if (value is int) {
return error(node.rightOperand);
}
return union(error(node.leftOperand), error(node.rightOperand));
}
EvaluationResultImpl concatenateError(Expression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl concatenateValid(Expression node, ValidResult leftOperand) {
Object leftValue = leftOperand.value;
if (leftValue is String && value is String) {
return valueOf4("${((leftValue as String))}${((value as String))}");
}
return error(node);
}
EvaluationResultImpl divideError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl divideValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_NUM;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
if (((value as int)) == 0) {
return valueOf3(((leftValue as int)).toDouble() / ((value as int)).toDouble());
}
return valueOf(((leftValue as int)) ~/ (value as int));
} else if (value is double) {
return valueOf3(((leftValue as int)).toDouble() / ((value as double)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf3(((leftValue as double)) / ((value as int)).toDouble());
} else if (value is double) {
return valueOf3(((leftValue as double)) / ((value as double)));
}
}
return error(node);
}
EvaluationResultImpl equalEqualError(Expression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl equalEqualValid(Expression node, ValidResult leftOperand) {
if (node is BinaryExpression) {
if (!isAnyNullBoolNumString || !leftOperand.isAnyNullBoolNumString) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
}
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return valueOf2(value == null);
} else if (leftValue is bool) {
if (value is bool) {
return valueOf2(identical(leftValue as bool, value as bool));
}
return RESULT_FALSE;
} else if (leftValue is int) {
if (value is int) {
return valueOf2(((leftValue as int)) == value);
} else if (value is double) {
return valueOf2(toDouble(leftValue as int) == value);
}
return RESULT_FALSE;
} else if (leftValue is double) {
if (value is int) {
return valueOf2(((leftValue as double)) == toDouble(value as int));
} else if (value is double) {
return valueOf2(((leftValue as double)) == value);
}
return RESULT_FALSE;
} else if (leftValue is String) {
if (value is String) {
return valueOf2(((leftValue as String)) == value);
}
return RESULT_FALSE;
}
return RESULT_FALSE;
}
EvaluationResultImpl greaterThanError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl greaterThanOrEqualError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl greaterThanOrEqualValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_BOOL;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf2(((leftValue as int)).compareTo(value as int) >= 0);
} else if (value is double) {
return valueOf2(((leftValue as int)).toDouble() >= ((value as double)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf2(((leftValue as double)) >= ((value as int)).toDouble());
} else if (value is double) {
return valueOf2(((leftValue as double)) >= ((value as double)));
}
}
return error(node);
}
EvaluationResultImpl greaterThanValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_BOOL;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf2(((leftValue as int)).compareTo(value as int) > 0);
} else if (value is double) {
return valueOf2(((leftValue as int)).toDouble() > ((value as double)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf2(((leftValue as double)) > ((value as int)).toDouble());
} else if (value is double) {
return valueOf2(((leftValue as double)) > ((value as double)));
}
}
return error(node);
}
EvaluationResultImpl integerDivideError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl integerDivideValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_INT;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
if (((value as int)) == 0) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE);
}
return valueOf(((leftValue as int)) ~/ (value as int));
} else if (value is double) {
double result = ((leftValue as int)).toDouble() / ((value as double));
return valueOf(result.toInt());
}
} else if (leftValue is double) {
if (value is int) {
double result = ((leftValue as double)) / ((value as int)).toDouble();
return valueOf(result.toInt());
} else if (value is double) {
double result = ((leftValue as double)) / ((value as double));
return valueOf(result.toInt());
}
}
return error(node);
}
EvaluationResultImpl lessThanError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl lessThanOrEqualError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl lessThanOrEqualValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_BOOL;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf2(((leftValue as int)).compareTo(value as int) <= 0);
} else if (value is double) {
return valueOf2(((leftValue as int)).toDouble() <= ((value as double)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf2(((leftValue as double)) <= ((value as int)).toDouble());
} else if (value is double) {
return valueOf2(((leftValue as double)) <= ((value as double)));
}
}
return error(node);
}
EvaluationResultImpl lessThanValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_BOOL;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf2(((leftValue as int)).compareTo(value as int) < 0);
} else if (value is double) {
return valueOf2(((leftValue as int)).toDouble() < ((value as double)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf2(((leftValue as double)) < ((value as int)).toDouble());
} else if (value is double) {
return valueOf2(((leftValue as double)) < ((value as double)));
}
}
return error(node);
}
EvaluationResultImpl logicalAndError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl logicalAndValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyBool || !leftOperand.isAnyBool) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL);
}
if (isSomeBool || leftOperand.isSomeBool) {
return RESULT_BOOL;
}
Object leftValue = leftOperand.value;
if (leftValue is bool) {
if (((leftValue as bool))) {
return booleanConversion(node.rightOperand, value);
}
return RESULT_FALSE;
}
return error(node);
}
EvaluationResultImpl logicalOrError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl logicalOrValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyBool || !leftOperand.isAnyBool) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL);
}
if (isSomeBool || leftOperand.isSomeBool) {
return RESULT_BOOL;
}
Object leftValue = leftOperand.value;
if (leftValue is bool && ((leftValue as bool))) {
return RESULT_TRUE;
}
return booleanConversion(node.rightOperand, value);
}
EvaluationResultImpl minusError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl minusValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
} else if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_NUM;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf(((leftValue as int)) - (value as int));
} else if (value is double) {
return valueOf3(((leftValue as int)).toDouble() - ((value as double)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf3(((leftValue as double)) - ((value as int)).toDouble());
} else if (value is double) {
return valueOf3(((leftValue as double)) - ((value as double)));
}
}
return error(node);
}
EvaluationResultImpl notEqualError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl notEqualValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNullBoolNumString || !leftOperand.isAnyNullBoolNumString) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return valueOf2(value != null);
} else if (leftValue is bool) {
if (value is bool) {
return valueOf2(((leftValue as bool)) != ((value as bool)));
}
return RESULT_TRUE;
} else if (leftValue is int) {
if (value is int) {
return valueOf2(((leftValue as int)) != value);
} else if (value is double) {
return valueOf2(toDouble(leftValue as int) != value);
}
return RESULT_TRUE;
} else if (leftValue is double) {
if (value is int) {
return valueOf2(((leftValue as double)) != toDouble(value as int));
} else if (value is double) {
return valueOf2(((leftValue as double)) != value);
}
return RESULT_TRUE;
} else if (leftValue is String) {
if (value is String) {
return valueOf2(((leftValue as String)) != value);
}
return RESULT_TRUE;
}
return RESULT_TRUE;
}
EvaluationResultImpl remainderError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl remainderValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
} else if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_NUM;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
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)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf3(((leftValue as double)) % ((value as int)).toDouble());
} else if (value is double) {
return valueOf3(((leftValue as double)) % ((value as double)));
}
}
return error(node);
}
EvaluationResultImpl shiftLeftError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl shiftLeftValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyInt || !leftOperand.isAnyInt) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_INT);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf(((leftValue as int)) << ((value as int)));
}
return error(node.rightOperand);
}
if (value is int) {
return error(node.leftOperand);
}
return union(error(node.leftOperand), error(node.rightOperand));
}
EvaluationResultImpl shiftRightError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl shiftRightValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyInt || !leftOperand.isAnyInt) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_INT);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf(((leftValue as int)) >> ((value as int)));
}
return error(node.rightOperand);
}
if (value is int) {
return error(node.leftOperand);
}
return union(error(node.leftOperand), error(node.rightOperand));
}
EvaluationResultImpl timesError(BinaryExpression node, ErrorResult leftOperand) => leftOperand;
EvaluationResultImpl timesValid(BinaryExpression node, ValidResult leftOperand) {
if (!isAnyNum || !leftOperand.isAnyNum) {
return error2(node, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
if (isSomeInt || leftOperand.isSomeInt) {
return RESULT_INT;
} else if (isSomeNum || leftOperand.isSomeNum) {
return RESULT_NUM;
}
Object leftValue = leftOperand.value;
if (leftValue == null) {
return error(node.leftOperand);
} else if (value == null) {
return error(node.rightOperand);
} else if (leftValue is int) {
if (value is int) {
return valueOf(((leftValue as int)) * (value as int));
} else if (value is double) {
return valueOf3(((leftValue as int)).toDouble() * ((value as double)));
}
} else if (leftValue is double) {
if (value is int) {
return valueOf3(((leftValue as double)) * ((value as int)).toDouble());
} else if (value is double) {
return valueOf3(((leftValue as double)) * ((value as double)));
}
}
return error(node);
}
bool get isNull => identical(this, RESULT_NULL);
/**
* Return the result of applying boolean conversion to the given value.
*
* @param node the node against which errors should be reported
* @param value the value to be converted to a boolean
* @return the result of applying boolean conversion to the given value
*/
EvaluationResultImpl booleanConversion(ASTNode node, Object value) {
if (value is bool) {
if (((value as bool))) {
return RESULT_TRUE;
} else {
return RESULT_FALSE;
}
}
return error(node);
}
ErrorResult error(ASTNode node) => error2(node, CompileTimeErrorCode.INVALID_CONSTANT);
/**
* Return a result object representing an error associated with the given node.
*
* @param node the AST node associated with the error
* @param code the error code indicating the nature of the error
* @return a result object representing an error associated with the given node
*/
ErrorResult error2(ASTNode node, ErrorCode code) => new ErrorResult.con1(node, code);
/**
* Checks if this result has type "bool", with known or unknown value.
*/
bool get isAnyBool => isSomeBool || identical(this, RESULT_TRUE) || identical(this, RESULT_FALSE);
/**
* Checks if this result has type "int", with known or unknown value.
*/
bool get isAnyInt => identical(this, RESULT_INT) || value is int;
/**
* Checks if this result has one of the types - "bool", "num" or "string"; or may be `null`.
*/
bool get isAnyNullBoolNumString => isNull || isAnyBool || isAnyNum || value is String;
/**
* Checks if this result has type "num", with known or unknown value.
*/
bool get isAnyNum => isSomeNum || value is num;
/**
* Checks if this result has type "bool", exact value of which we don't know.
*/
bool get isSomeBool => identical(this, RESULT_BOOL);
/**
* Checks if this result has type "int", exact value of which we don't know.
*/
bool get isSomeInt => identical(this, RESULT_INT);
/**
* Checks if this result has type "num" (or "int"), exact value of which we don't know.
*/
bool get isSomeNum => identical(this, RESULT_DYNAMIC) || identical(this, RESULT_INT) || identical(this, RESULT_NUM);
double toDouble(int value) => value.toDouble();
/**
* Return an error result that is the union of the two given error results.
*
* @param firstError the first error to be combined
* @param secondError the second error to be combined
* @return an error result that is the union of the two given error results
*/
ErrorResult union(ErrorResult firstError, ErrorResult secondError) => new ErrorResult.con2(firstError, secondError);
/**
* Return a result object representing the given value.
*
* @param value the value to be represented as a result object
* @return a result object representing the given value
*/
ValidResult valueOf(int value) => new ValidResult(value);
/**
* Return a result object representing the given value.
*
* @param value the value to be represented as a result object
* @return a result object representing the given value
*/
ValidResult valueOf2(bool value) => value ? RESULT_TRUE : RESULT_FALSE;
/**
* Return a result object representing the given value.
*
* @param value the value to be represented as a result object
* @return a result object representing the given value
*/
ValidResult valueOf3(double value) => new ValidResult(value);
/**
* Return a result object representing the given value.
*
* @param value the value to be represented as a result object
* @return a result object representing the given value
*/
ValidResult valueOf4(String value) => new ValidResult(value);
}