blob: a927df61a25e54153166a5a6ec2f78c9bf38275f [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* The implementation of the class [DartObject].
*/
library analyzer.src.dart.constant.value;
import 'dart:collection';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/generated/utilities_general.dart';
/**
* The state of an object representing a boolean value.
*/
class BoolState extends InstanceState {
/**
* An instance representing the boolean value 'false'.
*/
static BoolState FALSE_STATE = new BoolState(false);
/**
* An instance representing the boolean value 'true'.
*/
static BoolState TRUE_STATE = new BoolState(true);
/**
* A state that can be used to represent a boolean whose value is not known.
*/
static BoolState UNKNOWN_VALUE = new BoolState(null);
/**
* The value of this instance.
*/
final bool value;
/**
* Initialize a newly created state to represent the given [value].
*/
BoolState(this.value);
@override
int get hashCode => value == null ? 0 : (value ? 2 : 3);
@override
bool get isBool => true;
@override
bool get isBoolNumStringOrNull => true;
@override
bool get isUnknown => value == null;
@override
String get typeName => "bool";
@override
bool operator ==(Object object) =>
object is BoolState && identical(value, object.value);
@override
BoolState convertToBool() => this;
@override
StringState convertToString() {
if (value == null) {
return StringState.UNKNOWN_VALUE;
}
return new StringState(value ? "true" : "false");
}
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is BoolState) {
bool rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return BoolState.from(identical(value, rightValue));
} else if (rightOperand is DynamicState) {
return UNKNOWN_VALUE;
}
return FALSE_STATE;
}
@override
BoolState logicalAnd(InstanceState rightOperand) {
assertBool(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
return value ? rightOperand.convertToBool() : FALSE_STATE;
}
@override
BoolState logicalNot() {
if (value == null) {
return UNKNOWN_VALUE;
}
return value ? FALSE_STATE : TRUE_STATE;
}
@override
BoolState logicalOr(InstanceState rightOperand) {
assertBool(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
return value ? TRUE_STATE : rightOperand.convertToBool();
}
@override
String toString() => value == null ? "-unknown-" : (value ? "true" : "false");
/**
* Return the boolean state representing the given boolean [value].
*/
static BoolState from(bool value) =>
value ? BoolState.TRUE_STATE : BoolState.FALSE_STATE;
}
/**
* A representation of an instance of a Dart class.
*/
class DartObjectImpl implements DartObject {
/**
* An empty list of objects.
*/
static const List<DartObjectImpl> EMPTY_LIST = const <DartObjectImpl>[];
@override
final ParameterizedType type;
/**
* The state of the object.
*/
final InstanceState _state;
/**
* Initialize a newly created object to have the given [type] and [_state].
*/
DartObjectImpl(this.type, this._state);
/**
* Create an object to represent an unknown value.
*/
factory DartObjectImpl.validWithUnknownValue(ParameterizedType type) {
if (type.element.library.isDartCore) {
String typeName = type.name;
if (typeName == "bool") {
return new DartObjectImpl(type, BoolState.UNKNOWN_VALUE);
} else if (typeName == "double") {
return new DartObjectImpl(type, DoubleState.UNKNOWN_VALUE);
} else if (typeName == "int") {
return new DartObjectImpl(type, IntState.UNKNOWN_VALUE);
} else if (typeName == "String") {
return new DartObjectImpl(type, StringState.UNKNOWN_VALUE);
}
}
return new DartObjectImpl(type, GenericState.UNKNOWN_VALUE);
}
HashMap<String, DartObjectImpl> get fields => _state.fields;
@override
int get hashCode => JenkinsSmiHash.hash2(type.hashCode, _state.hashCode);
@override
bool get hasKnownValue => !_state.isUnknown;
/**
* Return `true` if this object represents an object whose type is 'bool'.
*/
bool get isBool => _state.isBool;
/**
* Return `true` if this object represents an object whose type is either
* 'bool', 'num', 'String', or 'Null'.
*/
bool get isBoolNumStringOrNull => _state.isBoolNumStringOrNull;
@override
bool get isNull => _state is NullState;
/**
* Return `true` if this object represents an unknown value.
*/
bool get isUnknown => _state.isUnknown;
/**
* Return `true` if this object represents an instance of a user-defined
* class.
*/
bool get isUserDefinedObject => _state is GenericState;
@override
bool operator ==(Object object) {
if (object is DartObjectImpl) {
return type == object.type && _state == object._state;
}
return false;
}
/**
* Return the result of invoking the '+' operator on this object with the
* given [rightOperand]. The [typeProvider] is the type provider used to find
* known types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl add(TypeProvider typeProvider, DartObjectImpl rightOperand) {
InstanceState result = _state.add(rightOperand._state);
if (result is IntState) {
return new DartObjectImpl(typeProvider.intType, result);
} else if (result is DoubleState) {
return new DartObjectImpl(typeProvider.doubleType, result);
} else if (result is NumState) {
return new DartObjectImpl(typeProvider.numType, result);
} else if (result is StringState) {
return new DartObjectImpl(typeProvider.stringType, result);
}
// We should never get here.
throw new StateError("add returned a ${result.runtimeType}");
}
/**
* Return the result of invoking the '&' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl bitAnd(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.intType, _state.bitAnd(rightOperand._state));
/**
* Return the result of invoking the '~' operator on this object. The
* [typeProvider] is the type provider used to find known types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl bitNot(TypeProvider typeProvider) =>
new DartObjectImpl(typeProvider.intType, _state.bitNot());
/**
* Return the result of invoking the '|' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl bitOr(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.intType, _state.bitOr(rightOperand._state));
/**
* Return the result of invoking the '^' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl bitXor(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.intType, _state.bitXor(rightOperand._state));
/**
* Return the result of invoking the ' ' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl concatenate(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.stringType, _state.concatenate(rightOperand._state));
/**
* Return the result of applying boolean conversion to this object. The
* [typeProvider] is the type provider used to find known types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl convertToBool(TypeProvider typeProvider) {
InterfaceType boolType = typeProvider.boolType;
if (identical(type, boolType)) {
return this;
}
return new DartObjectImpl(boolType, _state.convertToBool());
}
/**
* Return the result of invoking the '/' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for
* an object of this kind.
*/
DartObjectImpl divide(
TypeProvider typeProvider, DartObjectImpl rightOperand) {
InstanceState result = _state.divide(rightOperand._state);
if (result is IntState) {
return new DartObjectImpl(typeProvider.intType, result);
} else if (result is DoubleState) {
return new DartObjectImpl(typeProvider.doubleType, result);
} else if (result is NumState) {
return new DartObjectImpl(typeProvider.numType, result);
}
// We should never get here.
throw new StateError("divide returned a ${result.runtimeType}");
}
/**
* Return the result of invoking the '==' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl equalEqual(
TypeProvider typeProvider, DartObjectImpl rightOperand) {
if (type != rightOperand.type) {
String typeName = type.name;
if (!(typeName == "bool" ||
typeName == "double" ||
typeName == "int" ||
typeName == "num" ||
typeName == "String" ||
typeName == "Null" ||
type.isDynamic)) {
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
}
}
return new DartObjectImpl(
typeProvider.boolType, _state.equalEqual(rightOperand._state));
}
@override
DartObject getField(String name) {
InstanceState state = _state;
if (state is GenericState) {
return state.fields[name];
}
return null;
}
/**
* Return the result of invoking the '&gt;' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl greaterThan(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.boolType, _state.greaterThan(rightOperand._state));
/**
* Return the result of invoking the '&gt;=' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl greaterThanOrEqual(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(typeProvider.boolType,
_state.greaterThanOrEqual(rightOperand._state));
/**
* Return the result of invoking the '~/' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl integerDivide(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.intType, _state.integerDivide(rightOperand._state));
/**
* Return the result of invoking the identical function on this object with
* the [rightOperand]. The [typeProvider] is the type provider used to find
* known types.
*/
DartObjectImpl isIdentical(
TypeProvider typeProvider, DartObjectImpl rightOperand) {
return new DartObjectImpl(
typeProvider.boolType, _state.isIdentical(rightOperand._state));
}
/**
* Return the result of invoking the '&lt;' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl lessThan(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.boolType, _state.lessThan(rightOperand._state));
/**
* Return the result of invoking the '&lt;=' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl lessThanOrEqual(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.boolType, _state.lessThanOrEqual(rightOperand._state));
/**
* Return the result of invoking the '&&' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl logicalAnd(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.boolType, _state.logicalAnd(rightOperand._state));
/**
* Return the result of invoking the '!' operator on this object. The
* [typeProvider] is the type provider used to find known types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl logicalNot(TypeProvider typeProvider) =>
new DartObjectImpl(typeProvider.boolType, _state.logicalNot());
/**
* Return the result of invoking the '||' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl logicalOr(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.boolType, _state.logicalOr(rightOperand._state));
/**
* Return the result of invoking the '-' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl minus(TypeProvider typeProvider, DartObjectImpl rightOperand) {
InstanceState result = _state.minus(rightOperand._state);
if (result is IntState) {
return new DartObjectImpl(typeProvider.intType, result);
} else if (result is DoubleState) {
return new DartObjectImpl(typeProvider.doubleType, result);
} else if (result is NumState) {
return new DartObjectImpl(typeProvider.numType, result);
}
// We should never get here.
throw new StateError("minus returned a ${result.runtimeType}");
}
/**
* Return the result of invoking the '-' operator on this object. The
* [typeProvider] is the type provider used to find known types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl negated(TypeProvider typeProvider) {
InstanceState result = _state.negated();
if (result is IntState) {
return new DartObjectImpl(typeProvider.intType, result);
} else if (result is DoubleState) {
return new DartObjectImpl(typeProvider.doubleType, result);
} else if (result is NumState) {
return new DartObjectImpl(typeProvider.numType, result);
}
// We should never get here.
throw new StateError("negated returned a ${result.runtimeType}");
}
/**
* Return the result of invoking the '!=' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl notEqual(
TypeProvider typeProvider, DartObjectImpl rightOperand) {
if (type != rightOperand.type) {
String typeName = type.name;
if (typeName != "bool" &&
typeName != "double" &&
typeName != "int" &&
typeName != "num" &&
typeName != "String") {
return new DartObjectImpl(typeProvider.boolType, BoolState.TRUE_STATE);
}
}
return new DartObjectImpl(typeProvider.boolType,
_state.equalEqual(rightOperand._state).logicalNot());
}
/**
* Return the result of converting this object to a 'String'. The
* [typeProvider] is the type provider used to find known types.
*
* Throws an [EvaluationException] if the object cannot be converted to a
* 'String'.
*/
DartObjectImpl performToString(TypeProvider typeProvider) {
InterfaceType stringType = typeProvider.stringType;
if (identical(type, stringType)) {
return this;
}
return new DartObjectImpl(stringType, _state.convertToString());
}
/**
* Return the result of invoking the '%' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl remainder(
TypeProvider typeProvider, DartObjectImpl rightOperand) {
InstanceState result = _state.remainder(rightOperand._state);
if (result is IntState) {
return new DartObjectImpl(typeProvider.intType, result);
} else if (result is DoubleState) {
return new DartObjectImpl(typeProvider.doubleType, result);
} else if (result is NumState) {
return new DartObjectImpl(typeProvider.numType, result);
}
// We should never get here.
throw new StateError("remainder returned a ${result.runtimeType}");
}
/**
* Return the result of invoking the '&lt;&lt;' operator on this object with
* the [rightOperand]. The [typeProvider] is the type provider used to find
* known types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl shiftLeft(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.intType, _state.shiftLeft(rightOperand._state));
/**
* Return the result of invoking the '&gt;&gt;' operator on this object with
* the [rightOperand]. The [typeProvider] is the type provider used to find
* known types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl shiftRight(
TypeProvider typeProvider, DartObjectImpl rightOperand) =>
new DartObjectImpl(
typeProvider.intType, _state.shiftRight(rightOperand._state));
/**
* Return the result of invoking the 'length' getter on this object. The
* [typeProvider] is the type provider used to find known types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl stringLength(TypeProvider typeProvider) =>
new DartObjectImpl(typeProvider.intType, _state.stringLength());
/**
* Return the result of invoking the '*' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
* types.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
DartObjectImpl times(TypeProvider typeProvider, DartObjectImpl rightOperand) {
InstanceState result = _state.times(rightOperand._state);
if (result is IntState) {
return new DartObjectImpl(typeProvider.intType, result);
} else if (result is DoubleState) {
return new DartObjectImpl(typeProvider.doubleType, result);
} else if (result is NumState) {
return new DartObjectImpl(typeProvider.numType, result);
}
// We should never get here.
throw new StateError("times returned a ${result.runtimeType}");
}
@override
bool toBoolValue() {
InstanceState state = _state;
if (state is BoolState) {
return state.value;
}
return null;
}
@override
double toDoubleValue() {
InstanceState state = _state;
if (state is DoubleState) {
return state.value;
}
return null;
}
@override
int toIntValue() {
InstanceState state = _state;
if (state is IntState) {
return state.value;
}
return null;
}
@override
List<DartObject> toListValue() {
InstanceState state = _state;
if (state is ListState) {
return state._elements;
}
return null;
}
@override
Map<DartObject, DartObject> toMapValue() {
InstanceState state = _state;
if (state is MapState) {
return state._entries;
}
return null;
}
@override
String toString() => "${type.displayName} ($_state)";
@override
String toStringValue() {
InstanceState state = _state;
if (state is StringState) {
return state.value;
}
return null;
}
@override
String toSymbolValue() {
InstanceState state = _state;
if (state is SymbolState) {
return state.value;
}
return null;
}
@override
DartType toTypeValue() {
InstanceState state = _state;
if (state is TypeState) {
return state._type;
}
return null;
}
}
/**
* The state of an object representing a double.
*/
class DoubleState extends NumState {
/**
* A state that can be used to represent a double whose value is not known.
*/
static DoubleState UNKNOWN_VALUE = new DoubleState(null);
/**
* The value of this instance.
*/
final double value;
/**
* Initialize a newly created state to represent a double with the given
* [value].
*/
DoubleState(this.value);
@override
int get hashCode => value == null ? 0 : value.hashCode;
@override
bool get isBoolNumStringOrNull => true;
@override
bool get isUnknown => value == null;
@override
String get typeName => "double";
@override
bool operator ==(Object object) =>
object is DoubleState && (value == object.value);
@override
NumState add(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value + rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value + rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
StringState convertToString() {
if (value == null) {
return StringState.UNKNOWN_VALUE;
}
return new StringState(value.toString());
}
@override
NumState divide(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value / rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value / rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState greaterThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value > rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value > rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
BoolState greaterThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value >= rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value >= rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
IntState integerDivide(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return IntState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return IntState.UNKNOWN_VALUE;
}
double result = value / rightValue.toDouble();
return new IntState(result.toInt());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return IntState.UNKNOWN_VALUE;
}
double result = value / rightValue;
return new IntState(result.toInt());
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return IntState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value == rightValue);
} else if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value == rightValue.toDouble());
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.FALSE_STATE;
}
@override
BoolState lessThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value < rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value < rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
BoolState lessThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value <= rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value <= rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
NumState minus(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value - rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value - rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
NumState negated() {
if (value == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(-(value));
}
@override
NumState remainder(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value % rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value % rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
NumState times(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value * rightValue.toDouble());
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new DoubleState(value * rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
String toString() => value == null ? "-unknown-" : value.toString();
}
/**
* The state of an object representing a Dart object for which there is no type
* information.
*/
class DynamicState extends InstanceState {
/**
* The unique instance of this class.
*/
static DynamicState DYNAMIC_STATE = new DynamicState();
@override
bool get isBool => true;
@override
bool get isBoolNumStringOrNull => true;
@override
String get typeName => "dynamic";
@override
NumState add(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return _unknownNum(rightOperand);
}
@override
IntState bitAnd(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
return IntState.UNKNOWN_VALUE;
}
@override
IntState bitNot() => IntState.UNKNOWN_VALUE;
@override
IntState bitOr(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
return IntState.UNKNOWN_VALUE;
}
@override
IntState bitXor(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
return IntState.UNKNOWN_VALUE;
}
@override
StringState concatenate(InstanceState rightOperand) {
assertString(rightOperand);
return StringState.UNKNOWN_VALUE;
}
@override
BoolState convertToBool() => BoolState.UNKNOWN_VALUE;
@override
StringState convertToString() => StringState.UNKNOWN_VALUE;
@override
NumState divide(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return _unknownNum(rightOperand);
}
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState greaterThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState greaterThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
IntState integerDivide(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return IntState.UNKNOWN_VALUE;
}
@override
BoolState isIdentical(InstanceState rightOperand) {
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState lessThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState lessThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState logicalAnd(InstanceState rightOperand) {
assertBool(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState logicalNot() => BoolState.UNKNOWN_VALUE;
@override
BoolState logicalOr(InstanceState rightOperand) {
assertBool(rightOperand);
return rightOperand.convertToBool();
}
@override
NumState minus(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return _unknownNum(rightOperand);
}
@override
NumState negated() => NumState.UNKNOWN_VALUE;
@override
NumState remainder(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return _unknownNum(rightOperand);
}
@override
IntState shiftLeft(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
return IntState.UNKNOWN_VALUE;
}
@override
IntState shiftRight(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
return IntState.UNKNOWN_VALUE;
}
@override
NumState times(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return _unknownNum(rightOperand);
}
/**
* Return an object representing an unknown numeric value whose type is based
* on the type of the [rightOperand].
*/
NumState _unknownNum(InstanceState rightOperand) {
if (rightOperand is IntState) {
return IntState.UNKNOWN_VALUE;
} else if (rightOperand is DoubleState) {
return DoubleState.UNKNOWN_VALUE;
}
return NumState.UNKNOWN_VALUE;
}
}
/**
* Exception that would be thrown during the evaluation of Dart code.
*/
class EvaluationException {
/**
* The error code associated with the exception.
*/
final ErrorCode errorCode;
/**
* Initialize a newly created exception to have the given [errorCode].
*/
EvaluationException(this.errorCode);
}
/**
* The state of an object representing a function.
*/
class FunctionState extends InstanceState {
/**
* The element representing the function being modeled.
*/
final ExecutableElement _element;
/**
* Initialize a newly created state to represent the function with the given
* [element].
*/
FunctionState(this._element);
@override
int get hashCode => _element == null ? 0 : _element.hashCode;
@override
String get typeName => "Function";
@override
bool operator ==(Object object) =>
object is FunctionState && (_element == object._element);
@override
StringState convertToString() {
if (_element == null) {
return StringState.UNKNOWN_VALUE;
}
return new StringState(_element.name);
}
@override
BoolState equalEqual(InstanceState rightOperand) {
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (_element == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is FunctionState) {
ExecutableElement rightElement = rightOperand._element;
if (rightElement == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(_element == rightElement);
} else if (rightOperand is DynamicState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.FALSE_STATE;
}
@override
String toString() => _element == null ? "-unknown-" : _element.name;
}
/**
* The state of an object representing a Dart object for which there is no more
* specific state.
*/
class GenericState extends InstanceState {
/**
* Pseudo-field that we use to represent fields in the superclass.
*/
static String SUPERCLASS_FIELD = "(super)";
/**
* A state that can be used to represent an object whose state is not known.
*/
static GenericState UNKNOWN_VALUE =
new GenericState(new HashMap<String, DartObjectImpl>());
/**
* The values of the fields of this instance.
*/
final HashMap<String, DartObjectImpl> _fieldMap;
/**
* Initialize a newly created state to represent a newly created object. The
* [fieldMap] contains the values of the fields of the instance.
*/
GenericState(this._fieldMap);
@override
HashMap<String, DartObjectImpl> get fields => _fieldMap;
@override
int get hashCode {
int hashCode = 0;
for (DartObjectImpl value in _fieldMap.values) {
hashCode += value.hashCode;
}
return hashCode;
}
@override
bool get isUnknown => identical(this, UNKNOWN_VALUE);
@override
String get typeName => "user defined type";
@override
bool operator ==(Object object) {
if (object is GenericState) {
HashSet<String> otherFields =
new HashSet<String>.from(object._fieldMap.keys.toSet());
for (String fieldName in _fieldMap.keys.toSet()) {
if (_fieldMap[fieldName] != object._fieldMap[fieldName]) {
return false;
}
otherFields.remove(fieldName);
}
for (String fieldName in otherFields) {
if (object._fieldMap[fieldName] != _fieldMap[fieldName]) {
return false;
}
}
return true;
}
return false;
}
@override
StringState convertToString() => StringState.UNKNOWN_VALUE;
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (rightOperand is DynamicState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(this == rightOperand);
}
@override
String toString() {
StringBuffer buffer = new StringBuffer();
List<String> fieldNames = _fieldMap.keys.toList();
fieldNames.sort();
bool first = true;
for (String fieldName in fieldNames) {
if (first) {
first = false;
} else {
buffer.write('; ');
}
buffer.write(fieldName);
buffer.write(' = ');
buffer.write(_fieldMap[fieldName]);
}
return buffer.toString();
}
}
/**
* The state of an object representing a Dart object.
*/
abstract class InstanceState {
/**
* If this represents a generic dart object, return a map from its field names
* to their values. Otherwise return null.
*/
HashMap<String, DartObjectImpl> get fields => null;
/**
* Return `true` if this object represents an object whose type is 'bool'.
*/
bool get isBool => false;
/**
* Return `true` if this object represents an object whose type is either
* 'bool', 'num', 'String', or 'Null'.
*/
bool get isBoolNumStringOrNull => false;
/**
* Return `true` if this object represents an unknown value.
*/
bool get isUnknown => false;
/**
* Return the name of the type of this value.
*/
String get typeName;
/**
* Return the result of invoking the '+' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
InstanceState add(InstanceState rightOperand) {
if (this is StringState && rightOperand is StringState) {
return concatenate(rightOperand);
}
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Throw an exception if the given [state] does not represent a boolean value.
*/
void assertBool(InstanceState state) {
if (!(state is BoolState || state is DynamicState)) {
throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL);
}
}
/**
* Throw an exception if the given [state] does not represent a boolean,
* numeric, string or null value.
*/
void assertBoolNumStringOrNull(InstanceState state) {
if (!(state is BoolState ||
state is DoubleState ||
state is IntState ||
state is NumState ||
state is StringState ||
state is NullState ||
state is DynamicState)) {
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
}
}
/**
* Throw an exception if the given [state] does not represent an integer or
* null value.
*/
void assertIntOrNull(InstanceState state) {
if (!(state is IntState ||
state is NumState ||
state is NullState ||
state is DynamicState)) {
throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_INT);
}
}
/**
* Throw an exception if the given [state] does not represent a boolean,
* numeric, string or null value.
*/
void assertNumOrNull(InstanceState state) {
if (!(state is DoubleState ||
state is IntState ||
state is NumState ||
state is NullState ||
state is DynamicState)) {
throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
}
}
/**
* Throw an exception if the given [state] does not represent a String value.
*/
void assertString(InstanceState state) {
if (!(state is StringState || state is DynamicState)) {
throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL);
}
}
/**
* Return the result of invoking the '&' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
IntState bitAnd(InstanceState rightOperand) {
assertIntOrNull(this);
assertIntOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '~' operator on this object.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
IntState bitNot() {
assertIntOrNull(this);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '|' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
IntState bitOr(InstanceState rightOperand) {
assertIntOrNull(this);
assertIntOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '^' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
IntState bitXor(InstanceState rightOperand) {
assertIntOrNull(this);
assertIntOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the ' ' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
StringState concatenate(InstanceState rightOperand) {
assertString(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of applying boolean conversion to this object.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState convertToBool() => BoolState.FALSE_STATE;
/**
* Return the result of converting this object to a String.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
StringState convertToString();
/**
* Return the result of invoking the '/' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
NumState divide(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '==' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState equalEqual(InstanceState rightOperand);
/**
* Return the result of invoking the '&gt;' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState greaterThan(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '&gt;=' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState greaterThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '~/' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
IntState integerDivide(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the identical function on this object with
* the [rightOperand].
*/
BoolState isIdentical(InstanceState rightOperand);
/**
* Return the result of invoking the '&lt;' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState lessThan(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '&lt;=' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState lessThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '&&' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState logicalAnd(InstanceState rightOperand) {
assertBool(this);
assertBool(rightOperand);
return BoolState.FALSE_STATE;
}
/**
* Return the result of invoking the '!' operator on this object.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState logicalNot() {
assertBool(this);
return BoolState.TRUE_STATE;
}
/**
* Return the result of invoking the '||' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
BoolState logicalOr(InstanceState rightOperand) {
assertBool(this);
assertBool(rightOperand);
return rightOperand.convertToBool();
}
/**
* Return the result of invoking the '-' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
NumState minus(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '-' operator on this object.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
NumState negated() {
assertNumOrNull(this);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '%' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
NumState remainder(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '&lt;&lt;' operator on this object with
* the [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
IntState shiftLeft(InstanceState rightOperand) {
assertIntOrNull(this);
assertIntOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '&gt;&gt;' operator on this object with
* the [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
IntState shiftRight(InstanceState rightOperand) {
assertIntOrNull(this);
assertIntOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the 'length' getter on this object.
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
IntState stringLength() {
assertString(this);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
/**
* Return the result of invoking the '*' operator on this object with the
* [rightOperand].
*
* Throws an [EvaluationException] if the operator is not appropriate for an
* object of this kind.
*/
NumState times(InstanceState rightOperand) {
assertNumOrNull(this);
assertNumOrNull(rightOperand);
throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
}
}
/**
* The state of an object representing an int.
*/
class IntState extends NumState {
/**
* A state that can be used to represent an int whose value is not known.
*/
static IntState UNKNOWN_VALUE = new IntState(null);
/**
* The value of this instance.
*/
final int value;
/**
* Initialize a newly created state to represent an int with the given
* [value].
*/
IntState(this.value);
@override
int get hashCode => value == null ? 0 : value.hashCode;
@override
bool get isBoolNumStringOrNull => true;
@override
bool get isUnknown => value == null;
@override
String get typeName => "int";
@override
bool operator ==(Object object) =>
object is IntState && (value == object.value);
@override
NumState add(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
if (rightOperand is DoubleState) {
return DoubleState.UNKNOWN_VALUE;
}
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new IntState(value + rightValue);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return DoubleState.UNKNOWN_VALUE;
}
return new DoubleState(value.toDouble() + rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
IntState bitAnd(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new IntState(value & rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
IntState bitNot() {
if (value == null) {
return UNKNOWN_VALUE;
}
return new IntState(~value);
}
@override
IntState bitOr(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new IntState(value | rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
IntState bitXor(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new IntState(value ^ rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
StringState convertToString() {
if (value == null) {
return StringState.UNKNOWN_VALUE;
}
return new StringState(value.toString());
}
@override
NumState divide(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return DoubleState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return DoubleState.UNKNOWN_VALUE;
} else {
return new DoubleState(value.toDouble() / rightValue.toDouble());
}
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return DoubleState.UNKNOWN_VALUE;
}
return new DoubleState(value.toDouble() / rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return DoubleState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState greaterThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value.compareTo(rightValue) > 0);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value.toDouble() > rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
BoolState greaterThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value.compareTo(rightValue) >= 0);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value.toDouble() >= rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
IntState integerDivide(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
} else if (rightValue == 0) {
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE);
}
return new IntState(value ~/ rightValue);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
double result = value.toDouble() / rightValue;
return new IntState(result.toInt());
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value == rightValue);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(rightValue == value.toDouble());
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.FALSE_STATE;
}
@override
BoolState lessThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value.compareTo(rightValue) < 0);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value.toDouble() < rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
BoolState lessThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value.compareTo(rightValue) <= 0);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value.toDouble() <= rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return BoolState.UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
NumState minus(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
if (rightOperand is DoubleState) {
return DoubleState.UNKNOWN_VALUE;
}
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new IntState(value - rightValue);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return DoubleState.UNKNOWN_VALUE;
}
return new DoubleState(value.toDouble() - rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
NumState negated() {
if (value == null) {
return UNKNOWN_VALUE;
}
return new IntState(-value);
}
@override
NumState remainder(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
if (rightOperand is DoubleState) {
return DoubleState.UNKNOWN_VALUE;
}
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
} else if (rightValue == 0) {
return new DoubleState(value.toDouble() % rightValue.toDouble());
}
return new IntState(value.remainder(rightValue));
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return DoubleState.UNKNOWN_VALUE;
}
return new DoubleState(value.toDouble() % rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
IntState shiftLeft(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
} else if (rightValue.bitLength > 31) {
return UNKNOWN_VALUE;
}
return new IntState(value << rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
IntState shiftRight(InstanceState rightOperand) {
assertIntOrNull(rightOperand);
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
} else if (rightValue.bitLength > 31) {
return UNKNOWN_VALUE;
}
return new IntState(value >> rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
NumState times(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (value == null) {
if (rightOperand is DoubleState) {
return DoubleState.UNKNOWN_VALUE;
}
return UNKNOWN_VALUE;
}
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new IntState(value * rightValue);
} else if (rightOperand is DoubleState) {
double rightValue = rightOperand.value;
if (rightValue == null) {
return DoubleState.UNKNOWN_VALUE;
}
return new DoubleState(value.toDouble() * rightValue);
} else if (rightOperand is DynamicState || rightOperand is NumState) {
return UNKNOWN_VALUE;
}
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
String toString() => value == null ? "-unknown-" : value.toString();
}
/**
* The state of an object representing a list.
*/
class ListState extends InstanceState {
/**
* The elements of the list.
*/
final List<DartObjectImpl> _elements;
/**
* Initialize a newly created state to represent a list with the given
* [elements].
*/
ListState(this._elements);
@override
int get hashCode {
int value = 0;
int count = _elements.length;
for (int i = 0; i < count; i++) {
value = (value << 3) ^ _elements[i].hashCode;
}
return value;
}
@override
String get typeName => "List";
@override
bool operator ==(Object object) {
if (object is ListState) {
List<DartObjectImpl> otherElements = object._elements;
int count = _elements.length;
if (otherElements.length != count) {
return false;
} else if (count == 0) {
return true;
}
for (int i = 0; i < count; i++) {
if (_elements[i] != otherElements[i]) {
return false;
}
}
return true;
}
return false;
}
@override
StringState convertToString() => StringState.UNKNOWN_VALUE;
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (rightOperand is DynamicState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(this == rightOperand);
}
@override
String toString() {
StringBuffer buffer = new StringBuffer();
buffer.write('[');
bool first = true;
_elements.forEach((DartObjectImpl element) {
if (first) {
first = false;
} else {
buffer.write(', ');
}
buffer.write(element);
});
buffer.write(']');
return buffer.toString();
}
}
/**
* The state of an object representing a map.
*/
class MapState extends InstanceState {
/**
* The entries in the map.
*/
final HashMap<DartObjectImpl, DartObjectImpl> _entries;
/**
* Initialize a newly created state to represent a map with the given
* [entries].
*/
MapState(this._entries);
@override
int get hashCode {
int value = 0;
for (DartObjectImpl key in _entries.keys.toSet()) {
value = (value << 3) ^ key.hashCode;
}
return value;
}
@override
String get typeName => "Map";
@override
bool operator ==(Object object) {
if (object is MapState) {
HashMap<DartObjectImpl, DartObjectImpl> otherElements = object._entries;
int count = _entries.length;
if (otherElements.length != count) {
return false;
} else if (count == 0) {
return true;
}
for (DartObjectImpl key in _entries.keys) {
DartObjectImpl value = _entries[key];
DartObjectImpl otherValue = otherElements[key];
if (value != otherValue) {
return false;
}
}
return true;
}
return false;
}
@override
StringState convertToString() => StringState.UNKNOWN_VALUE;
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (rightOperand is DynamicState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(this == rightOperand);
}
@override
String toString() {
StringBuffer buffer = new StringBuffer();
buffer.write('{');
bool first = true;
_entries.forEach((DartObjectImpl key, DartObjectImpl value) {
if (first) {
first = false;
} else {
buffer.write(', ');
}
buffer.write(key);
buffer.write(' = ');
buffer.write(value);
});
buffer.write('}');
return buffer.toString();
}
}
/**
* The state of an object representing the value 'null'.
*/
class NullState extends InstanceState {
/**
* An instance representing the boolean value 'null'.
*/
static NullState NULL_STATE = new NullState();
@override
int get hashCode => 0;
@override
bool get isBoolNumStringOrNull => true;
@override
String get typeName => "Null";
@override
bool operator ==(Object object) => object is NullState;
@override
BoolState convertToBool() {
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
StringState convertToString() => new StringState("null");
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (rightOperand is DynamicState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(rightOperand is NullState);
}
@override
BoolState logicalNot() {
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
}
@override
String toString() => "null";
}
/**
* The state of an object representing a number of an unknown type (a 'num').
*/
class NumState extends InstanceState {
/**
* A state that can be used to represent a number whose value is not known.
*/
static NumState UNKNOWN_VALUE = new NumState();
@override
int get hashCode => 7;
@override
bool get isBoolNumStringOrNull => true;
@override
bool get isUnknown => identical(this, UNKNOWN_VALUE);
@override
String get typeName => "num";
@override
bool operator ==(Object object) => object is NumState;
@override
NumState add(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return UNKNOWN_VALUE;
}
@override
StringState convertToString() => StringState.UNKNOWN_VALUE;
@override
NumState divide(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return DoubleState.UNKNOWN_VALUE;
}
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState greaterThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState greaterThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
IntState integerDivide(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
if (rightOperand is IntState) {
int rightValue = rightOperand.value;
if (rightValue == null) {
return IntState.UNKNOWN_VALUE;
} else if (rightValue == 0) {
throw new EvaluationException(
CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE);
}
} else if (rightOperand is DynamicState) {
return IntState.UNKNOWN_VALUE;
}
return IntState.UNKNOWN_VALUE;
}
@override
BoolState isIdentical(InstanceState rightOperand) {
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState lessThan(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
BoolState lessThanOrEqual(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return BoolState.UNKNOWN_VALUE;
}
@override
NumState minus(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return UNKNOWN_VALUE;
}
@override
NumState negated() => UNKNOWN_VALUE;
@override
NumState remainder(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return UNKNOWN_VALUE;
}
@override
NumState times(InstanceState rightOperand) {
assertNumOrNull(rightOperand);
return UNKNOWN_VALUE;
}
@override
String toString() => "-unknown-";
}
/**
* The state of an object representing a string.
*/
class StringState extends InstanceState {
/**
* A state that can be used to represent a double whose value is not known.
*/
static StringState UNKNOWN_VALUE = new StringState(null);
/**
* The value of this instance.
*/
final String value;
/**
* Initialize a newly created state to represent the given [value].
*/
StringState(this.value);
@override
int get hashCode => value == null ? 0 : value.hashCode;
@override
bool get isBoolNumStringOrNull => true;
@override
bool get isUnknown => value == null;
@override
String get typeName => "String";
@override
bool operator ==(Object object) =>
object is StringState && (value == object.value);
@override
StringState concatenate(InstanceState rightOperand) {
if (value == null) {
return UNKNOWN_VALUE;
}
if (rightOperand is StringState) {
String rightValue = rightOperand.value;
if (rightValue == null) {
return UNKNOWN_VALUE;
}
return new StringState("$value$rightValue");
} else if (rightOperand is DynamicState) {
return UNKNOWN_VALUE;
}
return super.concatenate(rightOperand);
}
@override
StringState convertToString() => this;
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is StringState) {
String rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value == rightValue);
} else if (rightOperand is DynamicState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.FALSE_STATE;
}
@override
IntState stringLength() {
if (value == null) {
return IntState.UNKNOWN_VALUE;
}
return new IntState(value.length);
}
@override
String toString() => value == null ? "-unknown-" : "'$value'";
}
/**
* The state of an object representing a symbol.
*/
class SymbolState extends InstanceState {
/**
* The value of this instance.
*/
final String value;
/**
* Initialize a newly created state to represent the given [value].
*/
SymbolState(this.value);
@override
int get hashCode => value == null ? 0 : value.hashCode;
@override
String get typeName => "Symbol";
@override
bool operator ==(Object object) =>
object is SymbolState && (value == object.value);
@override
StringState convertToString() {
if (value == null) {
return StringState.UNKNOWN_VALUE;
}
return new StringState(value);
}
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (value == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is SymbolState) {
String rightValue = rightOperand.value;
if (rightValue == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(value == rightValue);
} else if (rightOperand is DynamicState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.FALSE_STATE;
}
@override
String toString() => value == null ? "-unknown-" : "#$value";
}
/**
* The state of an object representing a type.
*/
class TypeState extends InstanceState {
/**
* The element representing the type being modeled.
*/
final DartType _type;
/**
* Initialize a newly created state to represent the given [value].
*/
TypeState(this._type);
@override
int get hashCode => _type?.hashCode ?? 0;
@override
String get typeName => "Type";
@override
bool operator ==(Object object) =>
object is TypeState && (_type == object._type);
@override
StringState convertToString() {
if (_type == null) {
return StringState.UNKNOWN_VALUE;
}
return new StringState(_type.displayName);
}
@override
BoolState equalEqual(InstanceState rightOperand) {
assertBoolNumStringOrNull(rightOperand);
return isIdentical(rightOperand);
}
@override
BoolState isIdentical(InstanceState rightOperand) {
if (_type == null) {
return BoolState.UNKNOWN_VALUE;
}
if (rightOperand is TypeState) {
DartType rightType = rightOperand._type;
if (rightType == null) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(_type == rightType);
} else if (rightOperand is DynamicState) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.FALSE_STATE;
}
@override
String toString() => _type?.toString() ?? "-unknown-";
}