| Index: pkg/compiler/lib/src/constants/evaluation.dart
|
| diff --git a/pkg/compiler/lib/src/constants/evaluation.dart b/pkg/compiler/lib/src/constants/evaluation.dart
|
| index 176d736ed2599de93cbac9028f9bb9b7b7d25a56..1636d61d663500d057dfd9568c3eda10639fb309 100644
|
| --- a/pkg/compiler/lib/src/constants/evaluation.dart
|
| +++ b/pkg/compiler/lib/src/constants/evaluation.dart
|
| @@ -6,17 +6,45 @@ library dart2js.constants.evaluation;
|
|
|
| import '../compiler.dart' show
|
| Compiler;
|
| +import '../core_types.dart';
|
| +import '../dart_types.dart';
|
| +import '../diagnostics/messages.dart' show
|
| + MessageKind;
|
| +import '../elements/elements.dart' show
|
| + ConstructorElement,
|
| + VariableElement;
|
| +import '../resolution/operators.dart';
|
| import '../universe/call_structure.dart' show
|
| CallStructure;
|
| import 'expressions.dart';
|
| +import 'values.dart';
|
|
|
| /// Environment used for evaluating constant expressions.
|
| abstract class Environment {
|
| // TODO(johnniwinther): Replace this with [CoreTypes] and maybe [Backend].
|
| Compiler get compiler;
|
|
|
| + /// The core types in the evaluation environment.
|
| + CoreTypes get coreTypes;
|
| +
|
| /// Read environments string passed in using the '-Dname=value' option.
|
| String readFromEnvironment(String name);
|
| +
|
| + void reportWarning(ConstantExpression expression,
|
| + MessageKind kind,
|
| + Map arguments);
|
| +
|
| + void reportError(ConstantExpression expression,
|
| + MessageKind kind,
|
| + Map arguments);
|
| +
|
| + ConstantValue evaluateConstructor(
|
| + ConstructorElement constructor,
|
| + ConstantValue evaluate());
|
| +
|
| + ConstantValue evaluateVariable(
|
| + VariableElement variable,
|
| + ConstantValue evaluate());
|
| }
|
|
|
| /// The normalized arguments passed to a const constructor computed from the
|
| @@ -47,3 +75,496 @@ class NormalizedArguments {
|
| return arguments[index];
|
| }
|
| }
|
| +
|
| +abstract class ConstantTypeChecker<T> {
|
| + CoreTypes get coreTypes;
|
| + bool get isRequired;
|
| + bool get allowUnknown;
|
| +
|
| + void reportError(T position, MessageKind messageKind, Map arguments);
|
| +
|
| + bool isBool(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.boolType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool isBoolOrNull(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.boolType ||
|
| + type == coreTypes.nullType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool isInt(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.intType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool isIntOrNull(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.intType ||
|
| + type == coreTypes.nullType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool isNum(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.intType ||
|
| + type == coreTypes.doubleType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool isString(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.stringType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool isStringOrNull(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.stringType ||
|
| + type == coreTypes.nullType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool isPrimitive(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.intType ||
|
| + type == coreTypes.doubleType ||
|
| + type == coreTypes.stringType ||
|
| + type == coreTypes.boolType ||
|
| + type == coreTypes.nullType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool isNonNullPrimitive(DartType type, {bool allowUnknown}) {
|
| + if (allowUnknown == null) allowUnknown = this.allowUnknown;
|
| + return
|
| + type == coreTypes.boolType ||
|
| + type == coreTypes.intType ||
|
| + type == coreTypes.doubleType ||
|
| + type == coreTypes.stringType ||
|
| + (type == null && allowUnknown);
|
| + }
|
| +
|
| + bool checkBinaryExpression(
|
| + T position,
|
| + ConstantTypeInfo<T> left,
|
| + BinaryOperator operator,
|
| + ConstantTypeInfo<T> right) {
|
| + bool isValid = true;
|
| + switch (operator.kind) {
|
| + case BinaryOperatorKind.EQ:
|
| + case BinaryOperatorKind.NOT_EQ:
|
| + if (!isPrimitive(left.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + left.position,
|
| + MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE,
|
| + {'constant': left.constant,
|
| + 'type': left.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + if (!isPrimitive(right.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + right.position,
|
| + MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE,
|
| + {'constant': right.constant,
|
| + 'type': right.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + case BinaryOperatorKind.ADD:
|
| + if (isString(left.type, allowUnknown: false)) {
|
| + if (!isString(right.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + right.position,
|
| + MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE,
|
| + {'constant': right.constant,
|
| + 'type': right.type});
|
| + }
|
| + isValid = false;
|
| + }
|
| + } else if (isNum(left.type, allowUnknown: false)) {
|
| + if (!isNum(right.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + right.position,
|
| + MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE,
|
| + {'constant': right.constant,
|
| + 'type': right.type});
|
| + }
|
| + isValid = false;
|
| + }
|
| + } else if (isString(right.type, allowUnknown: false)) {
|
| + if (!isString(left.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + left.position,
|
| + MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE,
|
| + {'constant': left.constant,
|
| + 'type': left.type});
|
| + }
|
| + isValid = false;
|
| + }
|
| + } else if (isNum(right.type, allowUnknown: false)) {
|
| + if (!isNum(left.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + left.position,
|
| + MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE,
|
| + {'constant': left.constant,
|
| + 'type': left.type});
|
| + }
|
| + isValid = false;
|
| + }
|
| + } else if (allowUnknown && (left.type == null || right.type == null)) {
|
| + // Assume valid on unknown types.
|
| + } else {
|
| + if (isRequired) {
|
| + assert(left.type != null);
|
| + assert(right.type != null);
|
| + reportError(
|
| + position,
|
| + MessageKind.INVALID_CONSTANT_ADD_TYPES,
|
| + {'leftConstant': left.constant,
|
| + 'leftType': left.type,
|
| + 'rightConstant': right.constant,
|
| + 'rightType': right.type});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + case BinaryOperatorKind.SUB:
|
| + case BinaryOperatorKind.MUL:
|
| + case BinaryOperatorKind.DIV:
|
| + case BinaryOperatorKind.IDIV:
|
| + case BinaryOperatorKind.MOD:
|
| + case BinaryOperatorKind.GTEQ:
|
| + case BinaryOperatorKind.GT:
|
| + case BinaryOperatorKind.LTEQ:
|
| + case BinaryOperatorKind.LT:
|
| + if (!isNum(left.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + left.position,
|
| + MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE,
|
| + {'constant': left.constant,
|
| + 'type': left.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + if (!isNum(right.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + right.position,
|
| + MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE,
|
| + {'constant': right.constant,
|
| + 'type': right.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + case BinaryOperatorKind.SHL:
|
| + case BinaryOperatorKind.SHR:
|
| + case BinaryOperatorKind.AND:
|
| + case BinaryOperatorKind.OR:
|
| + case BinaryOperatorKind.XOR:
|
| + if (!isInt(left.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + left.position,
|
| + MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE,
|
| + {'constant': left.constant,
|
| + 'type': left.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + if (!isInt(right.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + right.position,
|
| + MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE,
|
| + {'constant': right.constant,
|
| + 'type': right.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + case BinaryOperatorKind.LOGICAL_AND:
|
| + if (!isBool(left.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + left.position,
|
| + MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE,
|
| + {'constant': left.constant,
|
| + 'type': left.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + if (!isBool(right.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + right.position,
|
| + MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE,
|
| + {'constant': right.constant,
|
| + 'type': right.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + case BinaryOperatorKind.LOGICAL_OR:
|
| + if (!isBool(left.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + left.position,
|
| + MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE,
|
| + {'constant': left.constant,
|
| + 'type': left.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + if (!isBool(right.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + right.position,
|
| + MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE,
|
| + {'constant': right.constant,
|
| + 'type': right.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + case BinaryOperatorKind.INDEX:
|
| + if (isRequired) {
|
| + reportError(position, MessageKind.INVALID_CONSTANT_INDEX, {});
|
| + }
|
| + isValid = false;
|
| + break;
|
| + case BinaryOperatorKind.IF_NULL:
|
| + if (isRequired) {
|
| + reportError(position, MessageKind.INVALID_CONSTANT_IF_NULL, {});
|
| + }
|
| + isValid = false;
|
| + break;
|
| + }
|
| + return isValid;
|
| + }
|
| +
|
| + bool checkFromEnvironment(ConstantTypeInfo<T> name) {
|
| + bool isValidAsConstant = true;
|
| + if (!isString(name.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + name.position,
|
| + MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE,
|
| + {'constant': name.constant,
|
| + 'type': name.type});
|
| + }
|
| + isValidAsConstant = false;
|
| + }
|
| + return isValidAsConstant;
|
| + }
|
| +
|
| + bool checkBoolFromEnvironment(
|
| + ConstantTypeInfo<T> name,
|
| + ConstantTypeInfo<T> defaultValue) {
|
| + bool isValidAsConstant = checkFromEnvironment(name);
|
| + if (defaultValue != null) {
|
| + if (!isBoolOrNull(defaultValue.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + defaultValue.position,
|
| + MessageKind.INVALID_BOOL_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE,
|
| + {'constant': defaultValue.constant,
|
| + 'type': defaultValue.type});
|
| + }
|
| + isValidAsConstant = false;
|
| + }
|
| + }
|
| + return isValidAsConstant;
|
| + }
|
| +
|
| + bool checkIntFromEnvironment(
|
| + ConstantTypeInfo<T> name,
|
| + ConstantTypeInfo<T> defaultValue) {
|
| + bool isValidAsConstant = checkFromEnvironment(name);
|
| + if (defaultValue != null) {
|
| + if (!isIntOrNull(defaultValue.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + defaultValue.position,
|
| + MessageKind.INVALID_INT_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE,
|
| + {'constant': defaultValue.constant,
|
| + 'type': defaultValue.type});
|
| + }
|
| + isValidAsConstant = false;
|
| + }
|
| + }
|
| + return isValidAsConstant;
|
| + }
|
| +
|
| + bool checkStringFromEnvironment(
|
| + ConstantTypeInfo<T> name,
|
| + ConstantTypeInfo<T> defaultValue) {
|
| + bool isValidAsConstant = checkFromEnvironment(name);
|
| + if (defaultValue != null) {
|
| + if (!isStringOrNull(defaultValue.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + defaultValue.position,
|
| + MessageKind.INVALID_STRING_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE,
|
| + {'constant': defaultValue.constant,
|
| + 'type': defaultValue.type});
|
| + }
|
| + isValidAsConstant = false;
|
| + }
|
| + }
|
| + return isValidAsConstant;
|
| + }
|
| +
|
| + bool checkConcatenate(ConstantTypeInfo<T> part) {
|
| + bool isValidAsConstant = true;
|
| + if (!isNonNullPrimitive(part.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + part.position,
|
| + MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE,
|
| + {'constant': part.constant,
|
| + 'type': part.type});
|
| + }
|
| + isValidAsConstant = false;
|
| + }
|
| + return isValidAsConstant;
|
| + }
|
| +
|
| + bool checkConditional(ConstantTypeInfo<T> condition) {
|
| + bool isValidAsConstant = true;
|
| + if (!isBool(condition.type)) {
|
| + reportError(
|
| + condition.position,
|
| + MessageKind.INVALID_CONSTANT_CONDITIONAL_TYPE,
|
| + {'constant': condition.constant,
|
| + 'type': condition.type});
|
| + isValidAsConstant = false;
|
| + }
|
| + return isValidAsConstant;
|
| + }
|
| +
|
| +
|
| + bool checkUnary(
|
| + UnaryOperator operator,
|
| + ConstantTypeInfo<T> expression) {
|
| + bool isValid = true;
|
| + switch (operator.kind) {
|
| + case UnaryOperatorKind.NOT:
|
| + if (!isBool(expression.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + expression.position,
|
| + MessageKind.INVALID_CONSTANT_NOT_TYPE,
|
| + {'constant': expression.constant,
|
| + 'type': expression.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + case UnaryOperatorKind.NEGATE:
|
| + if (!isNum(expression.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + expression.position,
|
| + MessageKind.INVALID_CONSTANT_NEGATE_TYPE,
|
| + {'constant': expression.constant,
|
| + 'type': expression.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + case UnaryOperatorKind.COMPLEMENT:
|
| + if (!isInt(expression.type)) {
|
| + if (isRequired) {
|
| + reportError(
|
| + expression.position,
|
| + MessageKind.INVALID_CONSTANT_COMPLEMENT_TYPE,
|
| + {'constant': expression.constant,
|
| + 'type': expression.type,
|
| + 'operator': operator});
|
| + }
|
| + isValid = false;
|
| + }
|
| + break;
|
| + }
|
| + return isValid;
|
| + }
|
| +}
|
| +
|
| +abstract class ConstantTypeInfo<T> {
|
| + T get position;
|
| + ConstantExpression get constant;
|
| + DartType get type;
|
| +}
|
| +
|
| +class ConstantExpressionChecker
|
| + extends ConstantTypeChecker<ConstantExpression> {
|
| + final Environment environment;
|
| +
|
| + ConstantExpressionChecker(this.environment);
|
| +
|
| + @override
|
| + bool get allowUnknown => false;
|
| +
|
| + @override
|
| + CoreTypes get coreTypes => environment.coreTypes;
|
| +
|
| + @override
|
| + bool get isRequired => true;
|
| +
|
| + @override
|
| + void reportError(ConstantExpression position,
|
| + MessageKind messageKind,
|
| + Map arguments) {
|
| + environment.reportError(position, messageKind, arguments);
|
| + }
|
| +
|
| + ConstantValueTypeInfo createInfo(ConstantExpression expression,
|
| + ConstantValue value) {
|
| + if (expression == null) return null;
|
| + return new ConstantValueTypeInfo(expression, value.getType(coreTypes));
|
| + }
|
| +}
|
| +
|
| +class ConstantValueTypeInfo implements ConstantTypeInfo<ConstantExpression> {
|
| + final ConstantExpression constant;
|
| + final DartType type;
|
| +
|
| + ConstantValueTypeInfo(this.constant, this.type);
|
| +
|
| + ConstantExpression get position => constant;
|
| +}
|
|
|