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; |
+} |